diff options
281 files changed, 3975 insertions, 725 deletions
diff --git a/cmake/FindBundletool.cmake b/cmake/FindBundletool.cmake new file mode 100644 index 00000000000..f691b27da89 --- /dev/null +++ b/cmake/FindBundletool.cmake @@ -0,0 +1,48 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +#.rst: +# FindBundletool +# --------- +# +# Try to locate the android bundletool. +# If found, this will define the following variables: +# +# ``Bundletool_FOUND`` +# True if the Bundletool is found +# +# ``Bundletool_EXECUTABLE `` +# Path to the Bundletool executable +# +# If ``Bundletool_FOUND`` is TRUE, it will also define the following +# imported target: +# +# ``Bundletool::Bundletool`` +# The Bundletool executable + +if(DEFINED ENV{Bundletool_EXECUTABLE}) + if((NOT Bundletool_EXECUTABLE OR NOT EXISTS "${Bundletool_EXECUTABLE}") + AND EXISTS "$ENV{Bundletool_EXECUTABLE}") + set(_Bundletool_use_force FORCE) + else() + set(_Bundletool_use_force "") + endif() + set(Bundletool_EXECUTABLE "$ENV{Bundletool_EXECUTABLE}" CACHE FILEPATH + "Path to the 'bundletool' executable." ${_Bundletool_use_force}) + unset(_Bundletool_use_force) +endif() + +find_file(Bundletool_EXECUTABLE bundletool bundletool.jar) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Bundletool DEFAULT_MSG Bundletool_EXECUTABLE) + +if(Bundletool_FOUND) + if(NOT TARGET Bundletool::Bundletool) + add_executable(Bundletool::Bundletool IMPORTED) + set_target_properties(Bundletool::Bundletool PROPERTIES + IMPORTED_LOCATION "${Bundletool_EXECUTABLE}") + endif() +endif() + +mark_as_advanced(Bundletool_EXECUTABLE) diff --git a/cmake/QtAndroidHelpers.cmake b/cmake/QtAndroidHelpers.cmake index 64696fff6d4..cb7d4716f12 100644 --- a/cmake/QtAndroidHelpers.cmake +++ b/cmake/QtAndroidHelpers.cmake @@ -473,3 +473,33 @@ function(qt_internal_create_source_jar) add_dependencies(install_android_source_jar_${module} ${jar_target}) add_dependencies(install_android_source_jars install_android_source_jar_${module}) endfunction() + +# The function stores Android permissions that are required by the module target. +# The stored INTERFACE_QT_ANDROID_PERMISSIONS is the transitive property. +function(qt_internal_android_add_interface_permissions target) + get_target_property(permissions ${target} QT_ANDROID_PERMISSIONS) + if(NOT permissions) + return() + endif() + + set(postprocessed_permissions "") + foreach(permission IN LISTS permissions) + # TODO: skip processing extras for now, add them back once internal API + # will cover adding extras using internal function. + list(APPEND postprocessed_permissions "name\;${permission}") + endforeach() + qt_internal_set_module_transitive_properties(${target} TYPE LINK PROPERTIES + INTERFACE_QT_ANDROID_PERMISSIONS "${postprocessed_permissions}") +endfunction() + +# The function stores Android features that are required by the module target. +# The stored INTERFACE_QT_ANDROID_FEATURES is the transitive property. +function(qt_internal_android_add_interface_features target) + get_target_property(features ${target} QT_ANDROID_FEATURES) + if(NOT features) + return() + endif() + + qt_internal_set_module_transitive_properties(${target} TYPE LINK PROPERTIES + INTERFACE_QT_ANDROID_FEATURES "${features}") +endfunction() diff --git a/cmake/QtBaseHelpers.cmake b/cmake/QtBaseHelpers.cmake index b3a4e8c7619..911b6001564 100644 --- a/cmake/QtBaseHelpers.cmake +++ b/cmake/QtBaseHelpers.cmake @@ -237,6 +237,9 @@ macro(qt_internal_qtbase_build_repo) # Needed when building qtbase for android. if(ANDROID) include(src/corelib/Qt6AndroidMacros.cmake) + include(src/corelib/Qt6AndroidDynamicFeatureHelpers.cmake) + include(src/corelib/Qt6AndroidGradleHelpers.cmake) + include(src/corelib/Qt6AndroidPermissionHelpers.cmake) endif() # Needed when building for WebAssembly. diff --git a/cmake/QtInternalTargets.cmake b/cmake/QtInternalTargets.cmake index 1b0adccd5b5..c041f9d3ac5 100644 --- a/cmake/QtInternalTargets.cmake +++ b/cmake/QtInternalTargets.cmake @@ -368,6 +368,13 @@ if (MSVC AND NOT CLANG) ) endif() +if (WIN32 AND (CLANG OR MINGW) AND (TEST_architecture_arch STREQUAL x86_64)) + # windows 10 requires cmpxchg16b + target_compile_options(PlatformCommonInternal INTERFACE + -mcx16 + ) +endif() + set(_qt_internal_clang_msvc_frontend False) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") diff --git a/cmake/QtPlatformAndroid.cmake b/cmake/QtPlatformAndroid.cmake index b480b1c14a6..d56a889d114 100644 --- a/cmake/QtPlatformAndroid.cmake +++ b/cmake/QtPlatformAndroid.cmake @@ -30,6 +30,7 @@ include(UseJava) # Find JDK 8.0 find_package(Java 1.8 COMPONENTS Development REQUIRED) +find_package(Bundletool) # Ensure we are using the shared version of libc++ if(NOT ANDROID_STL STREQUAL c++_shared) @@ -117,13 +118,32 @@ function(qt_internal_android_test_runner_arguments target out_test_runner out_te set(deployment_tool "${host_bin_dir}/androiddeployqt") _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) - set(${out_test_arguments} + _qt_internal_android_get_platform_tools_path(platform_tools) + set(test_arguments "--path" "${android_build_dir}" - "--adb" "${ANDROID_SDK_ROOT}/platform-tools/adb" + "--adb" "${platform_tools}/adb" "--skip-install-root" - "--make" "\"${CMAKE_COMMAND}\" --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk" - "--apk" "${android_build_dir}/${target}.apk" "--ndk-stack" "${ANDROID_NDK_ROOT}/ndk-stack" - PARENT_SCOPE ) + + if(QT_USE_ANDROID_MODERN_BUNDLE) + _qt_internal_android_get_target_deployment_dir(target_deployment_dir ${target}) + list(APPEND test_arguments + "--manifest" "${target_deployment_dir}/AndroidManifest.xml") + endif() + + if(EXISTS "${Bundletool_EXECUTABLE}" AND QT_USE_ANDROID_MODERN_BUNDLE) + list(APPEND test_arguments + "--make" "\"${CMAKE_COMMAND}\" --build ${CMAKE_BINARY_DIR} --target ${target}_make_aab" + "--aab" "${android_build_dir}/${target}.aab" + "--bundletool" "${Bundletool_EXECUTABLE}" + ) + else() + list(APPEND test_arguments + "--make" "\"${CMAKE_COMMAND}\" --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk" + "--apk" "${android_build_dir}/${target}.apk" + ) + endif() + + set(${out_test_arguments} "${test_arguments}" PARENT_SCOPE) endfunction() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 9f220f9d78b..07fcdee268b 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -790,6 +790,8 @@ function(qt_modules_process_android_dependencies) qt_internal_get_qt_repo_known_modules(repo_known_modules) foreach (target ${repo_known_modules}) qt_internal_android_dependencies(${target}) + qt_internal_android_add_interface_permissions(${target}) + qt_internal_android_add_interface_features(${target}) endforeach() endfunction() diff --git a/cmake/QtTestHelpers.cmake b/cmake/QtTestHelpers.cmake index ad8a9d65c14..8a0666b0ceb 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -1176,6 +1176,13 @@ function(qt_internal_collect_command_environment out_path out_plugin_path) set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}${install_prefix}") endforeach() set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}$ENV{PATH}") + if(ANDROID) + # Add android platform tools to path. Required for the correct androidtestrunner work. + _qt_internal_android_get_platform_tools_path(platform_tools) + string(PREPEND test_env_path + "${platform_tools}" "${QT_PATH_SEPARATOR}") + endif() + string(REPLACE ";" "\;" test_env_path "${test_env_path}") set(${out_path} "${test_env_path}" PARENT_SCOPE) diff --git a/coin/instructions/prepare_building_env.yaml b/coin/instructions/prepare_building_env.yaml index 20e7ef7f01c..85661f8f3eb 100644 --- a/coin/instructions/prepare_building_env.yaml +++ b/coin/instructions/prepare_building_env.yaml @@ -646,6 +646,26 @@ instructions: property: features contains_value: Sccache + # VCPKG + - type: Group + instructions: + - type: AppendToEnvironmentVariable + variableName: COMMON_NON_QTBASE_CMAKE_ARGS + variableValue: " -DQT_USE_VCPKG=ON -DVCPKG_INSTALLED_DIR={{.Env.VCPKG_INSTALLED_DIR}} -DVCPKG_HOST_TRIPLET={{.Env.VCPKG_HOST_TRIPLET}} -DVCPKG_TARGET_TRIPLET={{.Env.VCPKG_TARGET_TRIPLET}}" + - type: AppendToEnvironmentVariable + variableName: COMMON_TEST_CMAKE_ARGS + variableValue: " -DQT_USE_VCPKG=ON -DVCPKG_INSTALLED_DIR={{.Env.VCPKG_INSTALLED_DIR}} -DVCPKG_HOST_TRIPLET={{.Env.VCPKG_HOST_TRIPLET}} -DVCPKG_TARGET_TRIPLET={{.Env.VCPKG_TARGET_TRIPLET}}" + - type: AppendToEnvironmentVariable + variableName: COMMON_EXAMPLES_CMAKE_ARGS + variableValue: " -DQT_USE_VCPKG=ON -DVCPKG_INSTALLED_DIR={{.Env.VCPKG_INSTALLED_DIR}} -DVCPKG_HOST_TRIPLET={{.Env.VCPKG_HOST_TRIPLET}} -DVCPKG_TARGET_TRIPLET={{.Env.VCPKG_TARGET_TRIPLET}}" + - type: AppendToEnvironmentVariable + variableName: COMMON_NON_QTBASE_TARGET_CMAKE_ARGS + variableValue: " -DQT_USE_VCPKG=ON -DVCPKG_INSTALLED_DIR={{.Env.VCPKG_INSTALLED_DIR}} -DVCPKG_HOST_TRIPLET={{.Env.VCPKG_HOST_TRIPLET}} -DVCPKG_TARGET_TRIPLET={{.Env.VCPKG_TARGET_TRIPLET}}" + enable_if: + condition: runtime + env_var: USE_VCPKG + equals_value: "ON" + # Specify a custom examples installation directory, so that the built example binaries are not # packaged into the artifact archive together with the Qt libraries. # Also specify that during examples deployment, only a subset of examples should be deployed, to diff --git a/configure.cmake b/configure.cmake index 92fe4dba448..8d96ba7e1f0 100644 --- a/configure.cmake +++ b/configure.cmake @@ -1116,13 +1116,12 @@ qt_feature("network" PRIVATE ) qt_feature("printsupport" PRIVATE LABEL "Qt PrintSupport" - CONDITION QT_FEATURE_widgets AND NOT WASM + CONDITION QT_FEATURE_widgets SECTION "Module" PURPOSE "Provides the Qt PrintSupport module." ) qt_feature("sql" PRIVATE LABEL "Qt Sql" - CONDITION NOT WASM SECTION "Module" PURPOSE "Provides the Sql module." ) diff --git a/doc/global/includes/cli-build-cmake.qdocinc b/doc/global/includes/cli-build-cmake.qdocinc index b4c6ffccb4b..5f30e8632b7 100644 --- a/doc/global/includes/cli-build-cmake.qdocinc +++ b/doc/global/includes/cli-build-cmake.qdocinc @@ -28,7 +28,7 @@ \badcode \QtVersion md notepad-build cd notepad-build - C:\Qt\\1\msvc2019_64\bin\qt-cmake -GNinja C:\Examples\notepad + C:\Qt\\1\msvc2022_64\bin\qt-cmake -GNinja C:\Examples\notepad ninja \endcode diff --git a/licenseRule.json b/licenseRule.json index e35e8ba7fd1..b40e225e347 100644 --- a/licenseRule.json +++ b/licenseRule.json @@ -119,7 +119,7 @@ "file type": "test", "spdx": ["LicenseRef-Qt-Commercial OR GPL-3.0-only"] }, - "ests/auto/testlib/selftests/": { + "tests/auto/testlib/selftests/(?!README)": { "comment": "Exception. Those are test files", "file type": "test", "spdx": ["LicenseRef-Qt-Commercial OR GPL-3.0-only"] diff --git a/src/3rdparty/gradle/gradle.properties b/src/3rdparty/gradle/gradle.properties index 4fe1674abd3..3472b396d96 100644 --- a/src/3rdparty/gradle/gradle.properties +++ b/src/3rdparty/gradle/gradle.properties @@ -16,3 +16,6 @@ org.gradle.parallel=true # Allow AndroidX usage android.useAndroidX=true + +# User-defined properties +@EXTRA_PROPERTIES@ diff --git a/src/android/CMakeLists.txt b/src/android/CMakeLists.txt index 7c99ce7264e..12fbca624b3 100644 --- a/src/android/CMakeLists.txt +++ b/src/android/CMakeLists.txt @@ -9,6 +9,7 @@ if (ANDROID) add_subdirectory(java) add_subdirectory(templates) add_subdirectory(templates_aar) + add_subdirectory(templates_cmake) endif() qt_internal_add_java_documentation(Global) diff --git a/src/android/REUSE.toml b/src/android/REUSE.toml index 7d5a22fd2f7..6d821d52904 100644 --- a/src/android/REUSE.toml +++ b/src/android/REUSE.toml @@ -1,7 +1,7 @@ version = 1 [[annotations]] -path = ["jar/build.gradle", "jar/settings.gradle", "templates/build.gradle"] +path = ["jar/build.gradle", "jar/settings.gradle", "templates/build.gradle", "templates_cmake/**"] precedence = "closest" comment = "double check" SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd." diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 6595cb5358f..b6c4797fde7 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -70,8 +70,8 @@ android { abortOnError = false } - // Do not compress Qt binary resources file aaptOptions { + // Do not compress Qt binary resources file noCompress 'rcc' } diff --git a/src/android/templates_cmake/CMakeLists.txt b/src/android/templates_cmake/CMakeLists.txt new file mode 100644 index 00000000000..546a4fc50f3 --- /dev/null +++ b/src/android/templates_cmake/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +set(templates_files + "${CMAKE_CURRENT_SOURCE_DIR}/settings.gradle.in" + "${CMAKE_CURRENT_SOURCE_DIR}/gradle.properties.in" +) + +set(app_template_files + "${CMAKE_CURRENT_SOURCE_DIR}/app/gradle.properties.in" + "${CMAKE_CURRENT_SOURCE_DIR}/app/build.gradle.in" + "${CMAKE_CURRENT_SOURCE_DIR}/app/AndroidManifest.xml.in" +) + +set(dynamic_feature_files + "${CMAKE_CURRENT_SOURCE_DIR}/dynamic_feature/AndroidManifest.xml.in" + "${CMAKE_CURRENT_SOURCE_DIR}/dynamic_feature/build.gradle.in" +) + +add_custom_target(Qt6AndroidCMakeTemplates + SOURCES + ${templates_files} +) + +qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/templates_cmake") + +qt_copy_or_install(FILES ${templates_files} DESTINATION "${destination}") +qt_copy_or_install(FILES ${app_template_files} DESTINATION "${destination}/app") +qt_copy_or_install(FILES ${dynamic_feature_files} DESTINATION "${destination}/dynamic_feature") + +if(NOT QT_WILL_INSTALL) + qt_internal_copy_at_build_time(TARGET Qt6AndroidCMakeTemplates + FILES ${templates_files} + DESTINATION ${destination} + ) + + qt_internal_copy_at_build_time(TARGET Qt6AndroidCMakeTemplates + FILES ${app_templates_files} + DESTINATION ${destination}/app + ) + + qt_internal_copy_at_build_time(TARGET Qt6AndroidCMakeTemplates + FILES ${dynamic_feature_files} + DESTINATION ${destination}/dynamic_feature + ) +endif() diff --git a/src/android/templates_cmake/app/AndroidManifest.xml.in b/src/android/templates_cmake/app/AndroidManifest.xml.in new file mode 100644 index 00000000000..61c33cc7657 --- /dev/null +++ b/src/android/templates_cmake/app/AndroidManifest.xml.in @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:dist="http://schemas.android.com/apk/distribution" + package="@APP_PACKAGE_NAME@" + android:installLocation="auto" + android:versionCode="@APP_VERSION_CODE@" + android:versionName="@APP_VERSION_NAME@"> + @APP_PERMISSIONS@ + @APP_FEATURES@ + <dist:module dist:instant="true"/> + <supports-screens + android:anyDensity="true" + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" /> + <application + android:name="org.qtproject.qt.android.bindings.QtApplication" + android:hardwareAccelerated="true" + android:label="@APP_NAME@" + @APP_ICON@ + android:requestLegacyExternalStorage="true" + android:allowBackup="true" + android:fullBackupOnly="false"> + <activity + android:name="org.qtproject.qt.android.bindings.QtActivity" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" + android:launchMode="singleTop" + android:screenOrientation="unspecified" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <meta-data + android:name="android.app.lib_name" + android:value="@APP_LIB_NAME@" /> + + <meta-data + android:name="android.app.arguments" + android:value="@APP_ARGUMENTS@" /> + </activity> + + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.qtprovider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/qtprovider_paths"/> + </provider> + </application> +</manifest> diff --git a/src/android/templates_cmake/app/build.gradle.in b/src/android/templates_cmake/app/build.gradle.in new file mode 100644 index 00000000000..917f371e46f --- /dev/null +++ b/src/android/templates_cmake/app/build.gradle.in @@ -0,0 +1,53 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.10.1' + } +} + +apply plugin: '@GRADLE_PLUGIN_TYPE@' + +dependencies { + @GRADLE_DEPENDENCIES@ +} + +android { + namespace '@PACKAGE_NAME@' + compileSdkVersion '@ANDROID_COMPILE_SDK_VERSION@' + buildToolsVersion '@ANDROID_BUILD_TOOLS_VERSION@' + ndkVersion '@ANDROID_NDK_REVISION@' + + defaultConfig { + @DEFAULT_CONFIG_VALUES@ + } + + sourceSets { + main { +@SOURCE_SETS@ + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + aaptOptions { + // Do not compress Qt binary resources file + noCompress 'rcc' + } + + @ANDROID_DEPLOYMENT_EXTRAS@ +} diff --git a/src/android/templates_cmake/app/gradle.properties.in b/src/android/templates_cmake/app/gradle.properties.in new file mode 100644 index 00000000000..1136551f3fa --- /dev/null +++ b/src/android/templates_cmake/app/gradle.properties.in @@ -0,0 +1 @@ +# Extend this template with the extra gradle properties applicable for Android app. diff --git a/src/android/templates_cmake/dynamic_feature/AndroidManifest.xml.in b/src/android/templates_cmake/dynamic_feature/AndroidManifest.xml.in new file mode 100644 index 00000000000..9945ec76f27 --- /dev/null +++ b/src/android/templates_cmake/dynamic_feature/AndroidManifest.xml.in @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:dist="http://schemas.android.com/apk/distribution" + split="@APP_TARGET@" + android:isFeatureSplit="true"> + <dist:module + dist:instant="true" + dist:title="@TITLE_VAR@"> + <dist:delivery> + <dist:on-demand /> + </dist:delivery> + <dist:fusing dist:include="false" /> + </dist:module> + <application android:hasCode="false"/> +</manifest> diff --git a/src/android/templates_cmake/dynamic_feature/build.gradle.in b/src/android/templates_cmake/dynamic_feature/build.gradle.in new file mode 100644 index 00000000000..917f371e46f --- /dev/null +++ b/src/android/templates_cmake/dynamic_feature/build.gradle.in @@ -0,0 +1,53 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.10.1' + } +} + +apply plugin: '@GRADLE_PLUGIN_TYPE@' + +dependencies { + @GRADLE_DEPENDENCIES@ +} + +android { + namespace '@PACKAGE_NAME@' + compileSdkVersion '@ANDROID_COMPILE_SDK_VERSION@' + buildToolsVersion '@ANDROID_BUILD_TOOLS_VERSION@' + ndkVersion '@ANDROID_NDK_REVISION@' + + defaultConfig { + @DEFAULT_CONFIG_VALUES@ + } + + sourceSets { + main { +@SOURCE_SETS@ + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + aaptOptions { + // Do not compress Qt binary resources file + noCompress 'rcc' + } + + @ANDROID_DEPLOYMENT_EXTRAS@ +} diff --git a/src/android/templates_cmake/gradle.properties.in b/src/android/templates_cmake/gradle.properties.in new file mode 100644 index 00000000000..b751946190a --- /dev/null +++ b/src/android/templates_cmake/gradle.properties.in @@ -0,0 +1,12 @@ +# Project-wide Gradle settings. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# Enable building projects in parallel +org.gradle.parallel=true + +# Allow AndroidX usage +android.useAndroidX=true diff --git a/src/android/templates_cmake/settings.gradle.in b/src/android/templates_cmake/settings.gradle.in new file mode 100644 index 00000000000..03954ae5f9f --- /dev/null +++ b/src/android/templates_cmake/settings.gradle.in @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "@ROOT_PROJECT_NAME@" +include(":app") +@SUBPROJECTS@ diff --git a/src/assets/CMakeLists.txt b/src/assets/CMakeLists.txt index da9c0c14eea..ab07d27696e 100644 --- a/src/assets/CMakeLists.txt +++ b/src/assets/CMakeLists.txt @@ -1,6 +1,5 @@ # Copyright (C) 2024 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -add_subdirectory(icons) if (NOT INTEGRITY AND TARGET Qt6::Network AND TARGET Qt6::Concurrent) add_subdirectory(downloader) diff --git a/src/assets/icons/128x128/document-new.png b/src/assets/icons/128x128/document-new.png Binary files differdeleted file mode 100644 index 8d86a4827a7..00000000000 --- a/src/assets/icons/128x128/document-new.png +++ /dev/null diff --git a/src/assets/icons/128x128/document-open.png b/src/assets/icons/128x128/document-open.png Binary files differdeleted file mode 100644 index 2183dbbea64..00000000000 --- a/src/assets/icons/128x128/document-open.png +++ /dev/null diff --git a/src/assets/icons/128x128/document-print.png b/src/assets/icons/128x128/document-print.png Binary files differdeleted file mode 100644 index 9e7378aab2a..00000000000 --- a/src/assets/icons/128x128/document-print.png +++ /dev/null diff --git a/src/assets/icons/128x128/document-save.png b/src/assets/icons/128x128/document-save.png Binary files differdeleted file mode 100644 index e8b28406433..00000000000 --- a/src/assets/icons/128x128/document-save.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-copy.png b/src/assets/icons/128x128/edit-copy.png Binary files differdeleted file mode 100644 index 7585f4baa0c..00000000000 --- a/src/assets/icons/128x128/edit-copy.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-cut.png b/src/assets/icons/128x128/edit-cut.png Binary files differdeleted file mode 100644 index 51ede2fe37e..00000000000 --- a/src/assets/icons/128x128/edit-cut.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-delete.png b/src/assets/icons/128x128/edit-delete.png Binary files differdeleted file mode 100644 index bdf785c8285..00000000000 --- a/src/assets/icons/128x128/edit-delete.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-paste.png b/src/assets/icons/128x128/edit-paste.png Binary files differdeleted file mode 100644 index 690ffa172d9..00000000000 --- a/src/assets/icons/128x128/edit-paste.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-redo.png b/src/assets/icons/128x128/edit-redo.png Binary files differdeleted file mode 100644 index f1c97f71c25..00000000000 --- a/src/assets/icons/128x128/edit-redo.png +++ /dev/null diff --git a/src/assets/icons/128x128/edit-undo.png b/src/assets/icons/128x128/edit-undo.png Binary files differdeleted file mode 100644 index e728cbf6e03..00000000000 --- a/src/assets/icons/128x128/edit-undo.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-justify-center.png b/src/assets/icons/128x128/format-justify-center.png Binary files differdeleted file mode 100644 index 44ceb2af4d5..00000000000 --- a/src/assets/icons/128x128/format-justify-center.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-justify-fill.png b/src/assets/icons/128x128/format-justify-fill.png Binary files differdeleted file mode 100644 index b99a8507046..00000000000 --- a/src/assets/icons/128x128/format-justify-fill.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-justify-left.png b/src/assets/icons/128x128/format-justify-left.png Binary files differdeleted file mode 100644 index 2b63887b495..00000000000 --- a/src/assets/icons/128x128/format-justify-left.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-justify-right.png b/src/assets/icons/128x128/format-justify-right.png Binary files differdeleted file mode 100644 index 6c61889d595..00000000000 --- a/src/assets/icons/128x128/format-justify-right.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-text-bold.png b/src/assets/icons/128x128/format-text-bold.png Binary files differdeleted file mode 100644 index 96a5ca88a22..00000000000 --- a/src/assets/icons/128x128/format-text-bold.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-text-italic.png b/src/assets/icons/128x128/format-text-italic.png Binary files differdeleted file mode 100644 index 2bb71b4a4d3..00000000000 --- a/src/assets/icons/128x128/format-text-italic.png +++ /dev/null diff --git a/src/assets/icons/128x128/format-text-underline.png b/src/assets/icons/128x128/format-text-underline.png Binary files differdeleted file mode 100644 index ecf6830c929..00000000000 --- a/src/assets/icons/128x128/format-text-underline.png +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 32776b51a96..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 06e188b93b9..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 644e3c149af..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 16fa70493a2..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index b18bead117f..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index d9454cebf1c..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 4081cdb2ca7..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 3358426818d..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index e28b28542c6..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index fe10f57a398..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index d4ad74b0d07..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index bf0dd84bbb0..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index dde68c8514a..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 8a5e7518bdc..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 665d3ce37b5..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 4b6846a6b98..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/128x128@2/[email protected] b/src/assets/icons/128x128@2/[email protected] Binary files differdeleted file mode 100644 index 601f73216a3..00000000000 --- a/src/assets/icons/128x128@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16/document-new.png b/src/assets/icons/16x16/document-new.png Binary files differdeleted file mode 100644 index 893e7e1aec4..00000000000 --- a/src/assets/icons/16x16/document-new.png +++ /dev/null diff --git a/src/assets/icons/16x16/document-open.png b/src/assets/icons/16x16/document-open.png Binary files differdeleted file mode 100644 index b07906f40bd..00000000000 --- a/src/assets/icons/16x16/document-open.png +++ /dev/null diff --git a/src/assets/icons/16x16/document-print.png b/src/assets/icons/16x16/document-print.png Binary files differdeleted file mode 100644 index 9341060076f..00000000000 --- a/src/assets/icons/16x16/document-print.png +++ /dev/null diff --git a/src/assets/icons/16x16/document-save.png b/src/assets/icons/16x16/document-save.png Binary files differdeleted file mode 100644 index 6238718191b..00000000000 --- a/src/assets/icons/16x16/document-save.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-copy.png b/src/assets/icons/16x16/edit-copy.png Binary files differdeleted file mode 100644 index 585f5bfc8d8..00000000000 --- a/src/assets/icons/16x16/edit-copy.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-cut.png b/src/assets/icons/16x16/edit-cut.png Binary files differdeleted file mode 100644 index 661ef1ad030..00000000000 --- a/src/assets/icons/16x16/edit-cut.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-delete.png b/src/assets/icons/16x16/edit-delete.png Binary files differdeleted file mode 100644 index 7b5998df8a4..00000000000 --- a/src/assets/icons/16x16/edit-delete.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-paste.png b/src/assets/icons/16x16/edit-paste.png Binary files differdeleted file mode 100644 index 6318a22caf3..00000000000 --- a/src/assets/icons/16x16/edit-paste.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-redo.png b/src/assets/icons/16x16/edit-redo.png Binary files differdeleted file mode 100644 index 7eb10fe899c..00000000000 --- a/src/assets/icons/16x16/edit-redo.png +++ /dev/null diff --git a/src/assets/icons/16x16/edit-undo.png b/src/assets/icons/16x16/edit-undo.png Binary files differdeleted file mode 100644 index 108712547cb..00000000000 --- a/src/assets/icons/16x16/edit-undo.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-justify-center.png b/src/assets/icons/16x16/format-justify-center.png Binary files differdeleted file mode 100644 index 6b0951fa5dd..00000000000 --- a/src/assets/icons/16x16/format-justify-center.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-justify-fill.png b/src/assets/icons/16x16/format-justify-fill.png Binary files differdeleted file mode 100644 index 6e1c10d7c45..00000000000 --- a/src/assets/icons/16x16/format-justify-fill.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-justify-left.png b/src/assets/icons/16x16/format-justify-left.png Binary files differdeleted file mode 100644 index 9dfdc89b68a..00000000000 --- a/src/assets/icons/16x16/format-justify-left.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-justify-right.png b/src/assets/icons/16x16/format-justify-right.png Binary files differdeleted file mode 100644 index 36a52081f18..00000000000 --- a/src/assets/icons/16x16/format-justify-right.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-text-bold.png b/src/assets/icons/16x16/format-text-bold.png Binary files differdeleted file mode 100644 index a079317a947..00000000000 --- a/src/assets/icons/16x16/format-text-bold.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-text-italic.png b/src/assets/icons/16x16/format-text-italic.png Binary files differdeleted file mode 100644 index 04202b2842f..00000000000 --- a/src/assets/icons/16x16/format-text-italic.png +++ /dev/null diff --git a/src/assets/icons/16x16/format-text-underline.png b/src/assets/icons/16x16/format-text-underline.png Binary files differdeleted file mode 100644 index a80368212d8..00000000000 --- a/src/assets/icons/16x16/format-text-underline.png +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 482ae52024e..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 9858b146f44..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 1672ec58971..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index f04de746731..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index bbb34cc4c22..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index d89ef6c0167..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 4c97ee24952..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 299fa776867..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 4f8849c7110..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index b3d366c53f6..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 80c3afd9a6e..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 33589ea25d0..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index ba028211355..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 8e15d0cb44f..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 754efdd9755..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 6db31a4f697..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/16x16@2/[email protected] b/src/assets/icons/16x16@2/[email protected] Binary files differdeleted file mode 100644 index 977cde9d974..00000000000 --- a/src/assets/icons/16x16@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256/document-new.png b/src/assets/icons/256x256/document-new.png Binary files differdeleted file mode 100644 index 32776b51a96..00000000000 --- a/src/assets/icons/256x256/document-new.png +++ /dev/null diff --git a/src/assets/icons/256x256/document-open.png b/src/assets/icons/256x256/document-open.png Binary files differdeleted file mode 100644 index 06e188b93b9..00000000000 --- a/src/assets/icons/256x256/document-open.png +++ /dev/null diff --git a/src/assets/icons/256x256/document-print.png b/src/assets/icons/256x256/document-print.png Binary files differdeleted file mode 100644 index 644e3c149af..00000000000 --- a/src/assets/icons/256x256/document-print.png +++ /dev/null diff --git a/src/assets/icons/256x256/document-save.png b/src/assets/icons/256x256/document-save.png Binary files differdeleted file mode 100644 index 16fa70493a2..00000000000 --- a/src/assets/icons/256x256/document-save.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-copy.png b/src/assets/icons/256x256/edit-copy.png Binary files differdeleted file mode 100644 index b18bead117f..00000000000 --- a/src/assets/icons/256x256/edit-copy.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-cut.png b/src/assets/icons/256x256/edit-cut.png Binary files differdeleted file mode 100644 index d9454cebf1c..00000000000 --- a/src/assets/icons/256x256/edit-cut.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-delete.png b/src/assets/icons/256x256/edit-delete.png Binary files differdeleted file mode 100644 index 4081cdb2ca7..00000000000 --- a/src/assets/icons/256x256/edit-delete.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-paste.png b/src/assets/icons/256x256/edit-paste.png Binary files differdeleted file mode 100644 index 3358426818d..00000000000 --- a/src/assets/icons/256x256/edit-paste.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-redo.png b/src/assets/icons/256x256/edit-redo.png Binary files differdeleted file mode 100644 index e28b28542c6..00000000000 --- a/src/assets/icons/256x256/edit-redo.png +++ /dev/null diff --git a/src/assets/icons/256x256/edit-undo.png b/src/assets/icons/256x256/edit-undo.png Binary files differdeleted file mode 100644 index fe10f57a398..00000000000 --- a/src/assets/icons/256x256/edit-undo.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-justify-center.png b/src/assets/icons/256x256/format-justify-center.png Binary files differdeleted file mode 100644 index d4ad74b0d07..00000000000 --- a/src/assets/icons/256x256/format-justify-center.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-justify-fill.png b/src/assets/icons/256x256/format-justify-fill.png Binary files differdeleted file mode 100644 index bf0dd84bbb0..00000000000 --- a/src/assets/icons/256x256/format-justify-fill.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-justify-left.png b/src/assets/icons/256x256/format-justify-left.png Binary files differdeleted file mode 100644 index dde68c8514a..00000000000 --- a/src/assets/icons/256x256/format-justify-left.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-justify-right.png b/src/assets/icons/256x256/format-justify-right.png Binary files differdeleted file mode 100644 index 8a5e7518bdc..00000000000 --- a/src/assets/icons/256x256/format-justify-right.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-text-bold.png b/src/assets/icons/256x256/format-text-bold.png Binary files differdeleted file mode 100644 index 665d3ce37b5..00000000000 --- a/src/assets/icons/256x256/format-text-bold.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-text-italic.png b/src/assets/icons/256x256/format-text-italic.png Binary files differdeleted file mode 100644 index 4b6846a6b98..00000000000 --- a/src/assets/icons/256x256/format-text-italic.png +++ /dev/null diff --git a/src/assets/icons/256x256/format-text-underline.png b/src/assets/icons/256x256/format-text-underline.png Binary files differdeleted file mode 100644 index 601f73216a3..00000000000 --- a/src/assets/icons/256x256/format-text-underline.png +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index bfec6d0e6d1..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 630a05f622c..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index c8611c31c49..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 6f460959818..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 2f350041a05..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index e11cf6d2347..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index efe6b90bf59..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 32f54b39598..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 1f6e366535b..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 980ed370621..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index af7044ddee1..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index da14563bd68..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index c1025bf0102..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 3a07e06e0f3..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index b0f4cb09958..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 85f0cfc1d65..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/256x256@2/[email protected] b/src/assets/icons/256x256@2/[email protected] Binary files differdeleted file mode 100644 index 51ee0aa7783..00000000000 --- a/src/assets/icons/256x256@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32/document-new.png b/src/assets/icons/32x32/document-new.png Binary files differdeleted file mode 100644 index 482ae52024e..00000000000 --- a/src/assets/icons/32x32/document-new.png +++ /dev/null diff --git a/src/assets/icons/32x32/document-open.png b/src/assets/icons/32x32/document-open.png Binary files differdeleted file mode 100644 index 9858b146f44..00000000000 --- a/src/assets/icons/32x32/document-open.png +++ /dev/null diff --git a/src/assets/icons/32x32/document-print.png b/src/assets/icons/32x32/document-print.png Binary files differdeleted file mode 100644 index 1672ec58971..00000000000 --- a/src/assets/icons/32x32/document-print.png +++ /dev/null diff --git a/src/assets/icons/32x32/document-save.png b/src/assets/icons/32x32/document-save.png Binary files differdeleted file mode 100644 index f04de746731..00000000000 --- a/src/assets/icons/32x32/document-save.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-copy.png b/src/assets/icons/32x32/edit-copy.png Binary files differdeleted file mode 100644 index bbb34cc4c22..00000000000 --- a/src/assets/icons/32x32/edit-copy.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-cut.png b/src/assets/icons/32x32/edit-cut.png Binary files differdeleted file mode 100644 index d89ef6c0167..00000000000 --- a/src/assets/icons/32x32/edit-cut.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-delete.png b/src/assets/icons/32x32/edit-delete.png Binary files differdeleted file mode 100644 index 4c97ee24952..00000000000 --- a/src/assets/icons/32x32/edit-delete.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-paste.png b/src/assets/icons/32x32/edit-paste.png Binary files differdeleted file mode 100644 index 299fa776867..00000000000 --- a/src/assets/icons/32x32/edit-paste.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-redo.png b/src/assets/icons/32x32/edit-redo.png Binary files differdeleted file mode 100644 index 4f8849c7110..00000000000 --- a/src/assets/icons/32x32/edit-redo.png +++ /dev/null diff --git a/src/assets/icons/32x32/edit-undo.png b/src/assets/icons/32x32/edit-undo.png Binary files differdeleted file mode 100644 index b3d366c53f6..00000000000 --- a/src/assets/icons/32x32/edit-undo.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-justify-center.png b/src/assets/icons/32x32/format-justify-center.png Binary files differdeleted file mode 100644 index 80c3afd9a6e..00000000000 --- a/src/assets/icons/32x32/format-justify-center.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-justify-fill.png b/src/assets/icons/32x32/format-justify-fill.png Binary files differdeleted file mode 100644 index 33589ea25d0..00000000000 --- a/src/assets/icons/32x32/format-justify-fill.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-justify-left.png b/src/assets/icons/32x32/format-justify-left.png Binary files differdeleted file mode 100644 index ba028211355..00000000000 --- a/src/assets/icons/32x32/format-justify-left.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-justify-right.png b/src/assets/icons/32x32/format-justify-right.png Binary files differdeleted file mode 100644 index 8e15d0cb44f..00000000000 --- a/src/assets/icons/32x32/format-justify-right.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-text-bold.png b/src/assets/icons/32x32/format-text-bold.png Binary files differdeleted file mode 100644 index 754efdd9755..00000000000 --- a/src/assets/icons/32x32/format-text-bold.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-text-italic.png b/src/assets/icons/32x32/format-text-italic.png Binary files differdeleted file mode 100644 index 6db31a4f697..00000000000 --- a/src/assets/icons/32x32/format-text-italic.png +++ /dev/null diff --git a/src/assets/icons/32x32/format-text-underline.png b/src/assets/icons/32x32/format-text-underline.png Binary files differdeleted file mode 100644 index 977cde9d974..00000000000 --- a/src/assets/icons/32x32/format-text-underline.png +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index c9245760612..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 68e75b549a0..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index b7843367393..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index f4cca4b3238..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 9690d6bb041..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 408b0ae19b0..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 58abfc1fa53..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index b8c288f6c70..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 89fcd33c300..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 6f7ad2cb402..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 9b2cc1ed160..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 1212e9f7613..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 8c0eca3037e..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index fb0ed702525..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 0e67ead0b8c..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index f746f8956f8..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/32x32@2/[email protected] b/src/assets/icons/32x32@2/[email protected] Binary files differdeleted file mode 100644 index 47d6fced026..00000000000 --- a/src/assets/icons/32x32@2/[email protected] +++ /dev/null diff --git a/src/assets/icons/CMakeLists.txt b/src/assets/icons/CMakeLists.txt deleted file mode 100644 index f5adb229d85..00000000000 --- a/src/assets/icons/CMakeLists.txt +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (C) 2023 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause - -qt_internal_add_module(ExampleIconsPrivate - CONFIG_MODULE_NAME example_icons - STATIC - INTERNAL_MODULE - NO_GENERATE_CPP_EXPORTS -) - -set(icons_resource_files - index.theme - 16x16/document-new.png - 16x16/document-open.png - 16x16/document-print.png - 16x16/document-save.png - 16x16/edit-copy.png - 16x16/edit-cut.png - 16x16/edit-delete.png - 16x16/edit-paste.png - 16x16/edit-redo.png - 16x16/edit-undo.png - 16x16/format-justify-center.png - 16x16/format-justify-fill.png - 16x16/format-justify-left.png - 16x16/format-justify-right.png - 16x16/format-text-bold.png - 16x16/format-text-italic.png - 16x16/format-text-underline.png - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 16x16@2/[email protected] - 32x32/document-new.png - 32x32/document-open.png - 32x32/document-print.png - 32x32/document-save.png - 32x32/edit-copy.png - 32x32/edit-cut.png - 32x32/edit-delete.png - 32x32/edit-paste.png - 32x32/edit-redo.png - 32x32/edit-undo.png - 32x32/format-justify-center.png - 32x32/format-justify-fill.png - 32x32/format-justify-left.png - 32x32/format-justify-right.png - 32x32/format-text-bold.png - 32x32/format-text-italic.png - 32x32/format-text-underline.png - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 32x32@2/[email protected] - 128x128/document-new.png - 128x128/document-open.png - 128x128/document-print.png - 128x128/document-save.png - 128x128/edit-copy.png - 128x128/edit-cut.png - 128x128/edit-delete.png - 128x128/edit-paste.png - 128x128/edit-redo.png - 128x128/edit-undo.png - 128x128/format-justify-center.png - 128x128/format-justify-fill.png - 128x128/format-justify-left.png - 128x128/format-justify-right.png - 128x128/format-text-bold.png - 128x128/format-text-italic.png - 128x128/format-text-underline.png - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 128x128@2/[email protected] - 256x256/document-new.png - 256x256/document-open.png - 256x256/document-print.png - 256x256/document-save.png - 256x256/edit-copy.png - 256x256/edit-cut.png - 256x256/edit-delete.png - 256x256/edit-paste.png - 256x256/edit-redo.png - 256x256/edit-undo.png - 256x256/format-justify-center.png - 256x256/format-justify-fill.png - 256x256/format-justify-left.png - 256x256/format-justify-right.png - 256x256/format-text-bold.png - 256x256/format-text-italic.png - 256x256/format-text-underline.png - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - 256x256@2/[email protected] - scalable/document-new.svg - scalable/document-open.svg - scalable/document-print.svg - scalable/document-save.svg - scalable/edit-copy.svg - scalable/edit-cut.svg - scalable/edit-delete.svg - scalable/edit-paste.svg - scalable/edit-redo.svg - scalable/edit-undo.svg - scalable/format-justify-center.svg - scalable/format-justify-fill.svg - scalable/format-justify-left.svg - scalable/format-justify-right.svg - scalable/format-text-bold.svg - scalable/format-text-italic.svg - scalable/format-text-underline.svg -) - -qt_internal_add_resource(ExampleIconsPrivate "example_icons" - PREFIX - "/qt-project.org/icons/example_icons" - FILES - ${icons_resource_files} -) - diff --git a/src/assets/icons/README b/src/assets/icons/README deleted file mode 100644 index 26d94e9ff13..00000000000 --- a/src/assets/icons/README +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (C) 2023 The Qt Company Ltd. -SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -Setting up a project for using Example icon library - -1. Add ExampleIconsPrivate component to your project CMakeList.txt file - ... - find_package(Qt6 - REQUIRED COMPONENTS Core Gui Widgets ExampleIconsPrivate - ) - - target_link_libraries(imageviewer PRIVATE - Qt6::Core - Qt6::Gui - Qt6::Widgets - Qt6::ExampleIconsPrivate - ) - ... - -2. Load the theme - ... - QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << u":/qt-project.org/icons"_s); - QIcon::setFallbackThemeName(u"example_icons"_s); - ... - -3. Use the icons - ... - const QIcon openIcon = QIcon::fromTheme("document-open"); - ... diff --git a/src/assets/icons/index.theme b/src/assets/icons/index.theme deleted file mode 100644 index e389719e01c..00000000000 --- a/src/assets/icons/index.theme +++ /dev/null @@ -1,46 +0,0 @@ -[Icon Theme] -Name=example_icons - -Directories=16x16,16x16@2,32x32,32x32@2,128x128,128x128@2,256x256,256x256@2,scalable - -[16x16] -Size=16 -Type=Fixed - -[16x16@2] -Size=16 -Scale=2 -Type=Fixed - -[32x32] -Size=32 -Type=Fixed - -[32x32@2] -Size=32 -Scale=2 -Type=Fixed - -[128x128] -Size=128 -Type=Fixed - -[128x128@2] -Size=128 -Scale=2 -Type=Fixed - -[256x256] -Size=256 -Type=Fixed - -[256x256@2] -Size=256 -Scale=2 -Type=Fixed - -[scalable] -Size=512 -Type=Scalable -MinSize=16 -MaxSize=512 diff --git a/src/assets/icons/scalable/document-new.svg b/src/assets/icons/scalable/document-new.svg deleted file mode 100644 index b926a7b0e11..00000000000 --- a/src/assets/icons/scalable/document-new.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_New" d="m15,13c0,.28-.22.5-.5.5h-2v2c0,.28-.22.5-.5.5s-.5-.22-.5-.5v-2h-2c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h2v-2c0-.28.22-.5.5-.5s.5.22.5.5v2h2c.28,0,.5.22.5.5Zm-12,2h5v1H3c-1.1,0-2-.9-2-2V2C1,.9,1.9,0,3,0h7c1.14,1.14,2.93,2.93,4,4v5h-1v-4h-2c-1.1,0-2-.9-2-2V1H3c-.55,0-1,.45-1,1v12c0,.55.45,1,1,1ZM10,1.41v1.59c0,.55.45,1,1,1h1.59l-.82-.82-1.76-1.76Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-open.svg b/src/assets/icons/scalable/document-open.svg deleted file mode 100644 index 778c1b7c6e9..00000000000 --- a/src/assets/icons/scalable/document-open.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Open" d="m14.5,5h-1.5v-1c0-1.1-.9-2-2-2h-5L4,0h-2C.9,0,0,.9,0,2v12c0,1.1.9,2,2,2h9s0,0,0,0c.12,0,.25-.01.36-.03.98-.16,1.94-.94,2.24-1.87l2.29-7.19c.33-1.05-.29-1.91-1.39-1.91ZM1,14V2c0-.55.45-1,1-1h1.59l1.71,1.71c.19.19.44.29.71.29h5c.55,0,1,.45,1,1v1h-5.5c-1.1,0-2.27.86-2.61,1.91l-2.29,7.19c-.09.28-.11.54-.07.78-.32-.17-.54-.49-.54-.87Zm13.94-7.4l-2.29,7.19c-.2.63-.99,1.21-1.65,1.21H3c-.2,0-.36-.05-.43-.15-.07-.1-.07-.26-.01-.45l2.29-7.19c.2-.63.99-1.21,1.65-1.21h8c.2,0,.36.05.43.15.07.1.07.26.01.45Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-print.svg b/src/assets/icons/scalable/document-print.svg deleted file mode 100644 index fb8436af81a..00000000000 --- a/src/assets/icons/scalable/document-print.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Print" d="m14,4h-2v-2c0-1.1-.9-2-2-2h-4c-1.1,0-2,.9-2,2v2h-2c-1.1,0-2,.9-2,2v4c0,1.1.9,2,2,2h2v2c0,1.1.9,2,2,2h4c1.1,0,2-.9,2-2v-2h2c1.1,0,2-.9,2-2v-4c0-1.1-.9-2-2-2ZM5,2c0-.55.45-1,1-1h4c.55,0,1,.45,1,1v2h-6v-2Zm5,13h-4c-.55,0-1-.45-1-1v-5h6v5c0,.55-.45,1-1,1Zm5-5c0,.55-.45,1-1,1h-2v-2h.5c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-.5s-8,0-8,0h0s-.5,0-.5,0c-.28,0-.5.22-.5.5s.22.5.5.5h.5v2h-2c-.55,0-1-.45-1-1v-4c0-.55.45-1,1-1h12c.55,0,1,.45,1,1v4Zm-5,1.5c0,.28-.22.5-.5.5h-3c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h3c.28,0,.5.22.5.5Zm0,2c0,.28-.22.5-.5.5h-3c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h3c.28,0,.5.22.5.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-save.svg b/src/assets/icons/scalable/document-save.svg deleted file mode 100644 index 03675f4dab0..00000000000 --- a/src/assets/icons/scalable/document-save.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Save" d="m16,11h0V2.29c0-1.26-1.02-2.29-2.29-2.29H2.29C1.02,0,0,1.02,0,2.29v8.71h0v1h0v1.71c0,1.26,1.02,2.29,2.29,2.29h11.43c1.26,0,2.29-1.02,2.29-2.29v-1.71h0v-1ZM5.71,1h5.29v4.5c0,.28-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5V1h.71ZM1,2.29c0-.71.58-1.29,1.29-1.29h1.71v4.5c0,.83.67,1.5,1.5,1.5h5c.83,0,1.5-.67,1.5-1.5V1h1.71c.71,0,1.29.58,1.29,1.29v8.71H1V2.29Zm14,11.43c0,.71-.58,1.29-1.29,1.29H2.29c-.71,0-1.29-.58-1.29-1.29v-1.71h14v1.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-copy.svg b/src/assets/icons/scalable/edit-copy.svg deleted file mode 100644 index db53ff11624..00000000000 --- a/src/assets/icons/scalable/edit-copy.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Copy" d="m12,0h-5c-1.1,0-2,.9-2,2h1c0-.55.45-1,1-1h4v2c0,1.1.9,2,2,2h2v6c0,.55-.45,1-1,1h-2v1h2c1.1,0,2-.9,2-2v-7c-1.07-1.07-2.86-2.86-4-4Zm1,4c-.55,0-1-.45-1-1v-1.59l1.29,1.29,1.29,1.29h-1.59Zm-6-1H2c-1.1,0-2,.9-2,2v9c0,1.1.9,2,2,2h7c1.1,0,2-.9,2-2v-7c-1.07-1.07-2.86-2.86-4-4Zm3,6v5c0,.55-.45,1-1,1H2c-.55,0-1-.45-1-1V5c0-.55.45-1,1-1h4v2c0,1.1.9,2,2,2h2v1Zm-2-2c-.55,0-1-.45-1-1v-1.59l1.29,1.29,1.29,1.29h-1.59Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-cut.svg b/src/assets/icons/scalable/edit-cut.svg deleted file mode 100644 index 7f75d0b829d..00000000000 --- a/src/assets/icons/scalable/edit-cut.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Cut" d="m15.22,12.05l-7.39-3.55,7.39-3.55c.25-.12.35-.42.23-.67-.12-.25-.42-.35-.67-.23l-8.12,3.9-2.48-1.19c1.07-.46,1.81-1.52,1.81-2.75,0-1.66-1.34-3-3-3S0,2.34,0,4c0,1.28.81,2.36,1.93,2.8.1.05.22.1.35.15l3.23,1.55-3.23,1.55c-.1.05-.18.09-.26.13-1.18.41-2.02,1.51-2.02,2.82,0,1.66,1.34,3,3,3s3-1.34,3-3c0-1.24-.75-2.29-1.81-2.75l2.48-1.19,8.12,3.9c.07.03.14.05.22.05.19,0,.37-.1.45-.28.12-.25.01-.55-.23-.67ZM3,2c1.1,0,2,.9,2,2s-.9,2-2,2-2-.9-2-2,.9-2,2-2Zm2,11c0,1.1-.9,2-2,2s-2-.9-2-2,.9-2,2-2,2,.9,2,2Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-delete.svg b/src/assets/icons/scalable/edit-delete.svg deleted file mode 100644 index 15d1a9c7fd6..00000000000 --- a/src/assets/icons/scalable/edit-delete.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Delete" d="m8,0C3.58,0,0,3.58,0,8s3.58,8,8,8,8-3.58,8-8S12.42,0,8,0Zm0,15c-3.86,0-7-3.14-7-7S4.14,1,8,1s7,3.14,7,7-3.14,7-7,7Zm2.83-9.12l-2.12,2.12,2.12,2.12c.2.2.2.51,0,.71-.1.1-.23.15-.35.15s-.26-.05-.35-.15l-2.12-2.12-2.12,2.12c-.1.1-.23.15-.35.15s-.26-.05-.35-.15c-.2-.2-.2-.51,0-.71l2.12-2.12-2.12-2.12c-.2-.2-.2-.51,0-.71s.51-.2.71,0l2.12,2.12,2.12-2.12c.2-.2.51-.2.71,0s.2.51,0,.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-paste.svg b/src/assets/icons/scalable/edit-paste.svg deleted file mode 100644 index 57e94d917d2..00000000000 --- a/src/assets/icons/scalable/edit-paste.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Paste" d="m14.09,6.09c-.71-.74-1.44-1.5-2.09-2.09h-3.78c-1.23,0-2.22.9-2.22,2v8c0,1.1.99,2,2.22,2h5.56c1.23,0,2.22-.9,2.22-2v-6c-.52-.52-1.21-1.21-1.91-1.91Zm-2.09-.68l1.38,1.38c.41.43.8.83,1.18,1.21h-1.56c-.55,0-1-.45-1-1v-1.59Zm3,5.59v3c0,.55-.55,1-1.22,1h-5.56c-.67,0-1.22-.45-1.22-1V6c0-.55.55-1,1.22-1h2.78v2c0,1.1.9,2,2,2h2v2Zm-6.5,0h5c.28,0,.5.22.5.5s-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5s.22-.5.5-.5ZM5.5,3h1c.83,0,1.5-.67,1.5-1.5v-.5h2c.55,0,1,.45,1,1v1h1v-1C12,.9,11.1,0,10,0h-2s-4,0-4,0h0s-2,0-2,0C.9,0,0,.9,0,2v10c0,1.1.9,2,2,2h3v-1h-3c-.55,0-1-.45-1-1V2c0-.55.45-1,1-1h2v.5c0,.83.67,1.5,1.5,1.5Zm1.5-1.5c0,.28-.22.5-.5.5h-1c-.28,0-.5-.22-.5-.5v-.5h2v.5Zm7,12c0,.28-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h5c.28,0,.5.22.5.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-redo.svg b/src/assets/icons/scalable/edit-redo.svg deleted file mode 100644 index 92d60e1dd8c..00000000000 --- a/src/assets/icons/scalable/edit-redo.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Redo" d="m15.85,4.68l-3.54-3.54c-.2-.2-.51-.2-.71,0s-.2.51,0,.71l2.68,2.68H2.5c-1.38,0-2.5,1.12-2.5,2.5v4c0,1.38,1.12,2.5,2.5,2.5h7c.28,0,.5-.22.5-.5s-.22-.5-.5-.5H2.5c-.83,0-1.5-.67-1.5-1.5v-4c0-.83.67-1.5,1.5-1.5h11.79l-2.68,2.68c-.2.2-.2.51,0,.71.1.1.23.15.35.15s.26-.05.35-.15l3.54-3.54c.2-.2.2-.51,0-.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-undo.svg b/src/assets/icons/scalable/edit-undo.svg deleted file mode 100644 index 91731bb86fa..00000000000 --- a/src/assets/icons/scalable/edit-undo.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Undo" d="m13.5,4.54H1.71l2.68-2.68c.2-.2.2-.51,0-.71s-.51-.2-.71,0L.15,4.68c-.2.2-.2.51,0,.71l3.54,3.54c.1.1.23.15.35.15s.26-.05.35-.15c.2-.2.2-.51,0-.71l-2.68-2.68h11.79c.83,0,1.5.67,1.5,1.5v4c0,.83-.67,1.5-1.5,1.5h-7c-.28,0-.5.22-.5.5s.22.5.5.5h7c1.38,0,2.5-1.12,2.5-2.5v-4c0-1.38-1.12-2.5-2.5-2.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-center.svg b/src/assets/icons/scalable/format-justify-center.svg deleted file mode 100644 index 9822c95f2fa..00000000000 --- a/src/assets/icons/scalable/format-justify-center.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignCenter" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm-2,3.5h0c0-.28-.22-.5-.5-.5H3c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm2.5,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm-2.5-12h0c0-.28-.22-.5-.5-.5H3c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-fill.svg b/src/assets/icons/scalable/format-justify-fill.svg deleted file mode 100644 index 2fa7ddfa40d..00000000000 --- a/src/assets/icons/scalable/format-justify-fill.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignJustify" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm.5,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0-12h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-left.svg b/src/assets/icons/scalable/format-justify-left.svg deleted file mode 100644 index 99d666428d9..00000000000 --- a/src/assets/icons/scalable/format-justify-left.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignLeft" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm-4.5,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm5,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5ZM11,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-right.svg b/src/assets/icons/scalable/format-justify-right.svg deleted file mode 100644 index 7041f5e3f88..00000000000 --- a/src/assets/icons/scalable/format-justify-right.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignRight" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm.5,3.5h0c0-.28-.22-.5-.5-.5H5.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm0,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0-12h0c0-.28-.22-.5-.5-.5H5.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-bold.svg b/src/assets/icons/scalable/format-text-bold.svg deleted file mode 100644 index c0f43e0a692..00000000000 --- a/src/assets/icons/scalable/format-text-bold.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_Bold" d="m12.06,7.75c.65-.36,1.13-.81,1.44-1.35.3-.55.46-1.29.46-2.24,0-1.45-.4-2.5-1.21-3.16-.81-.66-2.03-.99-3.67-.99H3v16h6.26c3.39,0,5.09-1.51,5.09-4.53,0-1.88-.76-3.12-2.29-3.71ZM5.59,2.24h3.34c1.59,0,2.38.74,2.38,2.22,0,1.57-.76,2.36-2.29,2.36h-3.43V2.24Zm5.45,10.98c-.43.36-1.07.54-1.93.54h-3.53v-4.74h3.48c.75,0,1.37.17,1.87.53.5.35.75.96.75,1.83s-.21,1.49-.64,1.85Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-italic.svg b/src/assets/icons/scalable/format-text-italic.svg deleted file mode 100644 index 43df7ca54dc..00000000000 --- a/src/assets/icons/scalable/format-text-italic.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon id="iconTextEditor_Italic" points="6.63 0 6.18 2.01 8.18 2.01 5.45 13.99 3.45 13.99 3 16 9.56 16 10.01 13.99 8.01 13.99 10.74 2.01 12.74 2.01 13.19 0 6.63 0"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-underline.svg b/src/assets/icons/scalable/format-text-underline.svg deleted file mode 100644 index 62778fc579e..00000000000 --- a/src/assets/icons/scalable/format-text-underline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_Underline" d="m3.38,11.88c-.92-.75-1.38-1.94-1.38-3.57V0h2.44v8.35c0,1.79,1.02,2.69,3.06,2.69s3.06-.9,3.06-2.69V0h2.44v8.31c0,1.63-.46,2.82-1.39,3.57-.92.75-2.3,1.12-4.13,1.12s-3.2-.37-4.11-1.12Zm10.12,3.12H1.5c-.28,0-.5.22-.5.5s.22.5.5.5h12c.28,0,.5-.22.5-.5s-.22-.5-.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 4d83bbf794e..249b9b8c1ee 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -16,7 +16,11 @@ qt_internal_extend_sbom(WrapZLIB::WrapZLIB if(ANDROID) set(corelib_extra_cmake_files - "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidMacros.cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidMacros.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidDynamicFeatureHelpers.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidGradleHelpers.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidPermissionHelpers.cmake" + ) endif() if(WASM) set(corelib_extra_cmake_files diff --git a/src/corelib/Qt6AndroidDynamicFeatureHelpers.cmake b/src/corelib/Qt6AndroidDynamicFeatureHelpers.cmake new file mode 100644 index 00000000000..e8a81bde1d7 --- /dev/null +++ b/src/corelib/Qt6AndroidDynamicFeatureHelpers.cmake @@ -0,0 +1,152 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +# Collects the dynamic features for the target + +function(_qt_internal_android_add_dynamic_feature_deployment target) + get_target_property(dynamic_features ${target} _qt_android_dynamic_features) + if(NOT dynamic_features) + return() + endif() + + foreach(dynamic_feature IN LISTS dynamic_features) + get_target_property(is_imported ${dynamic_feature} IMPORTED) + if(is_imported) + message(FATAL_ERROR "Imported target ${dynamic_feature} can not be a" + " 'DYNAMIC_FEATURE'.") + endif() + + get_target_property(type ${dynamic_feature} TYPE) + if(NOT type STREQUAL "SHARED_LIBRARY") + message(FATAL_ERROR "Cannot make ${dynamic_feature} 'DYNAMIC_FEATURE'." + " The target must be the 'SHARED_LIBRARY'.") + endif() + + get_target_property(android_type ${dynamic_feature} _qt_android_target_type) + if(NOT android_type STREQUAL "" AND NOT android_type MATCHES "-NOTFOUND$" + AND NOT android_type STREQUAL "DYNAMIC_FEATURE") + message(FATAL_ERROR "Cannot make ${dynamic_feature} 'DYNAMIC_FEATURE'," + " it's already '${android_type}'. The target must be the plain 'SHARED_LIBRARY'.") + endif() + + # Mark the target as DYNAMIC_FEATURE, since we used it in this role once. + set_target_properties(${dynamic_feature} PROPERTIES _qt_android_target_type DYNAMIC_FEATURE) + _qt_internal_set_android_dynamic_feature_gradle_defaults(${dynamic_feature}) + + _qt_internal_android_get_dynamic_feature_deployment_dir(dynamic_feature_deployment_dir + ${target} ${dynamic_feature}) + _qt_internal_android_generate_target_build_gradle(${dynamic_feature} + DEPLOYMENT_DIR "${dynamic_feature_deployment_dir}") + _qt_internal_android_generate_dynamic_feature_manifest(${target} ${dynamic_feature}) + _qt_internal_android_copy_dynamic_feature(${target} ${dynamic_feature}) + endforeach() +endfunction() + +# Sets the default values of the gradle properties for the Android dynamic feature target. +function(_qt_internal_set_android_dynamic_feature_gradle_defaults target) + _qt_internal_android_java_dir(android_java_dir) + + # TODO: make androidx.core:core versionc configurable. + # Currently, it is hardcoded to 1.16.0. + set(implementation_dependencies "project(':app')" "'androidx.core:core:1.16.0'") + + set_target_properties(${target} PROPERTIES + _qt_android_gradle_java_source_dirs "src;java" + _qt_android_gradle_aidl_source_dirs "src;aidl" + _qt_android_gradle_res_source_dirs "res" + _qt_android_gradle_resources_source_dirs "resources" + _qt_android_gradle_renderscript_source_dirs "src" + _qt_android_gradle_assets_source_dirs "assets" + _qt_android_gradle_jniLibs_source_dirs "libs" + _qt_android_manifest "AndroidManifest.xml" + _qt_android_gradle_implementation_dependencies "${implementation_dependencies}" + ) +endfunction() + +# Copies the dynamic feature library to the respective gradle build tree. +function(_qt_internal_android_copy_dynamic_feature target dynamic_feature) + if(NOT TARGET ${dynamic_feature}) + message(FATAL_ERROR "${dynamic_feature} is not a target.") + endif() + + _qt_internal_android_get_dynamic_feature_deployment_dir(dynamic_feature_deployment_dir + ${target} ${dynamic_feature}) + + set(dynamic_feature_libs_dir "${dynamic_feature_deployment_dir}/libs/${CMAKE_ANDROID_ARCH_ABI}") + get_target_property(output_name ${dynamic_feature} OUTPUT_NAME) + if(NOT output_name) + get_target_property(suffix "${dynamic_feature}" SUFFIX) + set(output_name "lib${dynamic_feature}${suffix}") + endif() + set(output_file_path "${dynamic_feature_libs_dir}/${output_name}") + _qt_internal_copy_file_if_different_command(copy_command + "$<TARGET_FILE:${dynamic_feature}>" + "${output_file_path}" + ) + add_custom_command(OUTPUT ${output_file_path} + COMMAND ${copy_command} + DEPENDS ${dynamic_feature} + COMMENT "Copying ${dynamic_feature} dynamic feature to ${target} deployment directory" + ) + add_custom_target(${target}_deploy_dynamic_features DEPENDS "${output_file_path}") +endfunction() + +# Generates the feature name strings and copy them to the respective deployment directory. +function(_qt_internal_android_generate_dynamic_feature_names target) + get_target_property(dynamic_features ${target} _qt_android_dynamic_features) + if(NOT dynamic_features) + return() + endif() + + # Collect the titles + string(JOIN "\n" content + "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<resources>" + ) + foreach(feature IN LISTS dynamic_features) + string(APPEND content "\n <string name=\"${feature}_title\">${feature}</string>") + endforeach() + string(APPEND content "\n</resources>") + + _qt_internal_android_get_target_deployment_dir(deployment_dir ${target}) + # TODO: androiddeployqt wipes the android build directory. Generate feature_names.xml target + # build dir and copy after androiddeployqt run. We should skip feature_names.xml copying when + # androiddeployqt is not involved into the deployment process anymore. + # + # set(output_file "${deployment_dir}/res/values/feature_names.xml") + set(output_file "$<TARGET_PROPERTY:${target},BINARY_DIR>/res/values/feature_names.xml") + _qt_internal_configure_file(GENERATE OUTPUT "${output_file}" CONTENT "${content}") + set(output_file_in_deployment_dir "${deployment_dir}/res/values/feature_names.xml") + add_custom_command(OUTPUT "${output_file_in_deployment_dir}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${output_file}" + "${output_file_in_deployment_dir}" + DEPENDS "${output_file}" ${target}_android_deploy_aux + VERBATIM + ) + add_custom_target(${target}_copy_feature_names + DEPENDS + "${output_file_in_deployment_dir}" + ) +endfunction() + +# Returns the dynamic feature deployment directory in the target build tree. +function(_qt_internal_android_get_dynamic_feature_deployment_dir out_var target dynamic_feature) + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(${out_var} "${android_build_dir}/${dynamic_feature}" PARENT_SCOPE) +endfunction() + +# Generates the AndroidManifest.xml file for the dynamic_feature. +function(_qt_internal_android_generate_dynamic_feature_manifest target dynamic_feature) + set(android_manifest_filename AndroidManifest.xml) + _qt_internal_android_get_dynamic_feature_deployment_dir(dynamic_feature_deployment_dir ${target} + ${dynamic_feature}) + + _qt_internal_android_get_template_path(template_file ${target} + "dynamic_feature/${android_manifest_filename}") + + set(APP_TARGET "${target}") + set(TITLE_VAR "@string/${dynamic_feature}_title") + + set(output_file "${dynamic_feature_deployment_dir}/AndroidManifest.xml") + _qt_internal_configure_file(CONFIGURE OUTPUT "${output_file}" INPUT "${template_file}") +endfunction() diff --git a/src/corelib/Qt6AndroidGradleHelpers.cmake b/src/corelib/Qt6AndroidGradleHelpers.cmake new file mode 100644 index 00000000000..09184c93b69 --- /dev/null +++ b/src/corelib/Qt6AndroidGradleHelpers.cmake @@ -0,0 +1,631 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Returns the path to the template file from either user defined template directory, or +# Qt default template directory. +function(_qt_internal_android_get_template_path out_var target template_name) + if(template_name STREQUAL "") + message(FATAL_ERROR "Template name is empty." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + + _qt_internal_android_template_dir(template_directory) + get_filename_component(template_directory "${template_directory}" ABSOLUTE) + + # The paths are ordered according to their priority, from highest to lowest. + set(possible_paths + "${template_directory}/${template_name}.in" + ) + + get_target_property(android_target_type ${target} _qt_android_target_type) + if(android_target_type STREQUAL "APPLICATION") + _qt_internal_android_get_package_source_dir(user_template_directory ${target}) + get_filename_component(user_template_directory "${user_template_directory}" ABSOLUTE) + + # Add user template with the higher priority + list(PREPEND possible_paths "${user_template_directory}/${template_name}.in") + endif() + + set(template_path "") + foreach(possible_path IN LISTS possible_paths) + if(EXISTS "${possible_path}") + set(template_path "${possible_path}") + break() + endif() + endforeach() + + if(template_path STREQUAL "") + message(FATAL_ERROR "'${template_name}' is not found." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + + set(${out_var} "${template_path}" PARENT_SCOPE) +endfunction() + +# Generates the settings.gradle file for the target. Writes the result to the target android build +# directory. +function(_qt_internal_android_generate_bundle_settings_gradle target) + set(settings_gradle_filename "settings.gradle") + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(settings_gradle_file "${android_build_dir}/${settings_gradle_filename}") + + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${settings_gradle_file}" IN_LIST deployment_files) + return() + endif() + + _qt_internal_android_get_template_path(template_file ${target} "${settings_gradle_filename}") + + set(android_app_name "$<TARGET_PROPERTY:${target},QT_ANDROID_APP_NAME>") + string(JOIN "" ROOT_PROJECT_NAME + "$<IF:$<BOOL:${android_app_name}>," + "${android_app_name}," + "${target}" + ">" + ) + + set(target_dynamic_features "$<TARGET_PROPERTY:${target},_qt_android_dynamic_features>") + set(include_prefix "include(\":") + set(include_suffix "\")") + set(include_glue "${include_suffix}\n${include_prefix}") + string(JOIN "" SUBPROJECTS + "$<$<BOOL:${target_dynamic_features}>:" + "${include_prefix}" + "$<JOIN:${target_dynamic_features},${include_glue}>" + "${include_suffix}" + ">" + ) + + _qt_internal_configure_file(GENERATE OUTPUT ${settings_gradle_file} + INPUT "${template_file}") + set_property(TARGET ${target} APPEND PROPERTY _qt_android_deployment_files + "${settings_gradle_file}") +endfunction() + +# Generates the source sets for the target. +function(_qt_internal_android_get_gradle_source_sets out_var target) + set(known_types java aidl res resources renderscript assets jniLibs) + set(source_set "") + set(indent " ") + foreach(type IN LISTS known_types) + set(source_dirs + "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_android_gradle_${type}_source_dirs>>") + string(JOIN "" source_set + "${source_set}" + "$<$<BOOL:${source_dirs}>:" + "${indent}${type}.srcDirs = ['$<JOIN:${source_dirs},'$<COMMA> '>']\n" + ">" + ) + endforeach() + + set(manifest + "$<TARGET_PROPERTY:${target},_qt_android_manifest>") + string(JOIN "" source_set + "${source_set}" + "$<$<BOOL:${manifest}>:" + "${indent}manifest.srcFile '${manifest}'\n" + ">" + ) + set(${out_var} "${source_set}" PARENT_SCOPE) +endfunction() + +# Generates the gradle dependency list for the target. +function(_qt_internal_android_get_gradle_dependencies out_var target) + # Use dependencies from file tree by default + set(known_dependencies + "implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])") + foreach(dep_type implementation api) + string(JOIN "\n " dep_prefix + "\n //noinspection GradleDependency" + "${dep_type} " + ) + set(dep_postfix "") + set(dep_property "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_android_gradle_${dep_type}_dependencies>>") + string(JOIN "" known_dependencies + "${known_dependencies}" + "$<$<BOOL:${dep_property}>:" + "${dep_prefix}$<JOIN:${dep_property},${dep_postfix}${dep_prefix}>${dep_postfix}" + ">" + ) + endforeach() + set(${out_var} "${known_dependencies}" PARENT_SCOPE) +endfunction() + +# Sets the default values of the gradle properties for the Android executable target. +function(_qt_internal_set_android_application_gradle_defaults target) + _qt_internal_android_java_dir(android_java_dir) + + set(target_dynamic_features "$<TARGET_PROPERTY:${target},_qt_android_dynamic_features>") + string(JOIN "" implementation_dependencies + "$<$<BOOL:${target_dynamic_features}>:'com.google.android.play:feature-delivery:2.1.0'>" + ) + # TODO: make androidx.core:core version configurable. + # Currently, it is hardcoded to 1.16.0. + list(APPEND implementation_dependencies "'androidx.core:core:1.16.0'") + + set_target_properties(${target} PROPERTIES + _qt_android_gradle_java_source_dirs "${android_java_dir}/src;src;java" + _qt_android_gradle_aidl_source_dirs "${android_java_dir}/src;src;aidl" + _qt_android_gradle_res_source_dirs "${android_java_dir}/res;res" + _qt_android_gradle_resources_source_dirs "resources" + _qt_android_gradle_renderscript_source_dirs "src" + _qt_android_gradle_assets_source_dirs "assets" + _qt_android_gradle_jniLibs_source_dirs "libs" + _qt_android_manifest "AndroidManifest.xml" + _qt_android_gradle_implementation_dependencies "${implementation_dependencies}" + ) +endfunction() + +# Generates the build.gradle file for the target. Writes the result to the target app deployment +# directory. +function(_qt_internal_android_generate_target_build_gradle target) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "DEPLOYMENT_DIR" "") + + if(NOT arg_DEPLOYMENT_DIR) + message(FATAL_ERROR "DEPLOYMENT_DIR is not specified.") + endif() + + set(build_gradle_filename "build.gradle") + set(out_file "${arg_DEPLOYMENT_DIR}/${build_gradle_filename}") + + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${out_file}" IN_LIST deployment_files) + return() + endif() + + # TODO: The current build.gradle.in templates hardcodes couple values that needs to be + # configurable in the future. For example the buildscript dependencies, or the use of + # androidx.core:core:1.13.1 and the dependency for all user applications. + + _qt_internal_android_get_gradle_property(PACKAGE_NAME ${target} + QT_ANDROID_PACKAGE_NAME "org.qtproject.example.$<MAKE_C_IDENTIFIER:${target}>") + + _qt_internal_android_get_target_sdk_build_tools_revision(ANDROID_BUILD_TOOLS_VERSION + ${target}) + + _qt_internal_detect_latest_android_platform(ANDROID_COMPILE_SDK_VERSION) + if(NOT ANDROID_COMPILE_SDK_VERSION) + message(FATAL_ERROR "Unable to detect the android platform in ${ANDROID_SDK_ROOT}. " + "Please check your Android SDK installation.") + endif() + + _qt_internal_android_get_gradle_source_sets(SOURCE_SETS ${target}) + _qt_internal_android_get_gradle_dependencies(GRADLE_DEPENDENCIES ${target}) + + _qt_internal_android_get_gradle_property(min_sdk_version ${target} + QT_ANDROID_MIN_SDK_VERSION "28") + + _qt_internal_android_get_gradle_property(target_sdk_version ${target} + QT_ANDROID_TARGET_SDK_VERSION "34") + + set(target_abis "$<TARGET_PROPERTY:${target},_qt_android_abis>") + set(target_abi_list "$<JOIN:${target_abis};${CMAKE_ANDROID_ARCH_ABI},'$<COMMA> '>") + + string(JOIN "\n " DEFAULT_CONFIG_VALUES + "resConfig 'en'" + "minSdkVersion ${min_sdk_version}" + "targetSdkVersion ${target_sdk_version}" + "ndk.abiFilters = ['${target_abi_list}']" + ) + + set(target_dynamic_features "$<TARGET_PROPERTY:${target},_qt_android_dynamic_features>") + set(include_prefix "\":") + set(include_suffix "\"") + set(include_glue "${include_suffix}$<COMMA>${include_prefix}") + string(APPEND ANDROID_DEPLOYMENT_EXTRAS + "$<$<BOOL:${target_dynamic_features}>:dynamicFeatures = [" + "${include_prefix}" + "$<JOIN:${target_dynamic_features},${include_glue}>" + "${include_suffix}]" + ">" + ) + + get_target_property(android_target_type ${target} _qt_android_target_type) + if(android_target_type STREQUAL "APPLICATION") + set(GRADLE_PLUGIN_TYPE "com.android.application") + set(template_subdir "app") + elseif(android_target_type STREQUAL "DYNAMIC_FEATURE") + set(GRADLE_PLUGIN_TYPE "com.android.dynamic-feature") + set(template_subdir "dynamic_feature") + else() + message(FATAL_ERROR "Unsupported target type for android bundle deployment ${target}") + endif() + + _qt_internal_android_get_template_path(template_file ${target} + "${template_subdir}/${build_gradle_filename}") + _qt_internal_configure_file(GENERATE + OUTPUT "${out_file}" + INPUT "${template_file}" + ) + set_property(TARGET ${target} APPEND PROPERTY _qt_android_deployment_files "${out_file}") +endfunction() + +# Prepares the artifacts for the gradle build of the target. +function(_qt_internal_android_prepare_gradle_build target) + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + _qt_internal_android_get_target_deployment_dir(deployment_dir ${target}) + + _qt_internal_android_copy_gradle_files(${target} "${android_build_dir}") + _qt_internal_android_copy_target_package_sources(${target}) + + _qt_internal_android_generate_bundle_gradle_properties(${target}) + _qt_internal_android_generate_bundle_settings_gradle(${target}) + _qt_internal_android_generate_bundle_local_properties(${target}) + _qt_internal_android_generate_target_build_gradle(${target} DEPLOYMENT_DIR "${deployment_dir}") + _qt_internal_android_generate_target_gradle_properties(${target} + DEPLOYMENT_DIR "${deployment_dir}") + _qt_internal_android_generate_target_android_manifest(${target} + DEPLOYMENT_DIR "${deployment_dir}") + + + _qt_internal_android_add_gradle_build(${target} apk) + _qt_internal_android_add_gradle_build(${target} aab) + + # Make global apk, aab, and aar targets depend on the respective targets. + _qt_internal_android_add_global_package_dependencies(${target}) + _qt_internal_create_global_apk_all_target_if_needed() +endfunction() + +# Adds the modern gradle build targets. +# These targets use the settings.gradle based build directory structure. +function(_qt_internal_android_add_gradle_build target type) + _qt_internal_android_get_deployment_type_option(android_deployment_type_option + "assembleRelease" "assembleDebug") + + _qt_internal_android_gradlew_name(gradlew_file_name) + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(gradlew "${android_build_dir}/${gradlew_file_name}") + + set(extra_args "") + if(type STREQUAL "aab") + set(extra_args "bundle") + endif() + + set(package_file_path "${android_build_dir}/${target}.${type}") + + _qt_internal_android_package_path(package_build_dir ${target} ${type}) + _qt_internal_android_get_deployment_type_option(deployment_type_suffix + "release" "debug") + set(package_build_file_path + "${package_build_dir}/${deployment_type_suffix}/app-${deployment_type_suffix}.${type}") + + set(extra_deps "") + if(TARGET ${target}_copy_feature_names) + list(APPEND extra_deps ${target}_copy_feature_names) + endif() + + if(TARGET ${target}_deploy_dynamic_features) + list(APPEND extra_deps ${target}_deploy_dynamic_features) + endif() + + set(gradle_scripts "$<TARGET_PROPERTY:${target},_qt_android_deployment_files>") + add_custom_command(OUTPUT "${package_file_path}" + BYPRODUCTS "${package_build_file_path}" + COMMAND + "${gradlew}" ${android_deployment_type_option} ${extra_args} + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + "${package_build_file_path}" "${package_file_path}" + DEPENDS + ${target} + ${gradle_scripts} + ${target}_copy_gradle_files + ${target}_android_deploy_aux + ${extra_deps} + WORKING_DIRECTORY + "${android_build_dir}" + VERBATIM + ) + + add_custom_target(${target}_make_${type} DEPENDS "${package_file_path}") +endfunction() + +# Returns the path to the android executable package either apk or aab. +function(_qt_internal_android_package_path out_var target type) + set(supported_package_types apk aab) + if(NOT type IN_LIST supported_package_types) + message(FATAL_ERROR "Invalid package type, supported types: ${supported_package_types}") + endif() + + # aab packages are located in the bundle directory + if(type STREQUAL "aab") + set(type "bundle") + endif() + + _qt_internal_android_get_target_deployment_dir(deployment_dir ${target}) + + set(${out_var} "${deployment_dir}/build/outputs/${type}" PARENT_SCOPE) +endfunction() + +# Returns the path to the gradle build directory. +function(_qt_internal_android_gradle_template_dir out_var) + if(PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD) + set(${out_var} "${QtBase_SOURCE_DIR}/src/3rdparty/gradle" PARENT_SCOPE) + else() + set(${out_var} "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DATA}/src/3rdparty/gradle" PARENT_SCOPE) + endif() +endfunction() + +# Returns the path to the android java dir. +function(_qt_internal_android_java_dir out_var) + if(PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD) + set(${out_var} "${QtBase_SOURCE_DIR}/src/android/java" PARENT_SCOPE) + else() + set(${out_var} "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DATA}/src/android/java" PARENT_SCOPE) + endif() +endfunction() + +# Returns the platform-spefic name of the gradlew script. +function(_qt_internal_android_gradlew_name out_var) + if(CMAKE_HOST_WIN32) + set(gradlew_file_name "gradlew.bat") + else() + set(gradlew_file_name "gradlew") + endif() + + set(${out_var} "${gradlew_file_name}" PARENT_SCOPE) +endfunction() + +# Return the path to the gradlew script. +function(_qt_internal_android_gradlew_path out_var target) + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(${out_var} "${android_build_dir}/${gradlew_file_name}" PARENT_SCOPE) +endfunction() + +# Returns the generator expression for the gradle_property value. Defaults to the default_value +# argument. +function(_qt_internal_android_get_gradle_property out_var target target_property default_value) + set(target_property_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},${target_property}>>") + string(JOIN "" result + "$<IF:$<BOOL:${target_property_genex}>," + "${target_property_genex}," + "${default_value}" + ">" + ) + set(${out_var} "${result}" PARENT_SCOPE) +endfunction() + +# Generates gradle.properties for the specific target. Usually contains the +# target build type(executable, dynamic feature, library). +function(_qt_internal_android_generate_target_gradle_properties target) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "DEPLOYMENT_DIR" "") + + if(NOT arg_DEPLOYMENT_DIR) + message(FATAL_ERROR "DEPLOYMENT_DIR is not specified.") + endif() + + set(gradle_properties_file_name "gradle.properties") + set(out_file "${arg_DEPLOYMENT_DIR}/${gradle_properties_file_name}") + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${out_file}" IN_LIST deployment_files) + return() + endif() + + _qt_internal_android_get_template_path(template_file ${target} + "app/${gradle_properties_file_name}") + _qt_internal_configure_file(CONFIGURE + OUTPUT "${out_file}" + INPUT "${template_file}" + ) + set_property(TARGET ${target} APPEND PROPERTY _qt_android_deployment_files "${out_file}") +endfunction() + +# Constucts generator expression that returns either target property or the default value +function(_qt_internal_android_get_manifest_property out_var target property default) + set(target_property "$<TARGET_PROPERTY:${target},${property}>") + string(JOIN "" out_genex + "$<IF:$<BOOL:${target_property}>," + "${target_property}," + "${default}" + ">" + ) + + set(${out_var} "${out_genex}" PARENT_SCOPE) +endfunction() + +# Generates the target AndroidManifest.xml +function(_qt_internal_android_generate_target_android_manifest target) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "DEPLOYMENT_DIR" "") + + if(NOT arg_DEPLOYMENT_DIR) + message(FATAL_ERROR "DEPLOYMENT_DIR is not specified.") + endif() + + set(android_manifest_filename "AndroidManifest.xml") + set(out_file "${arg_DEPLOYMENT_DIR}/${android_manifest_filename}") + + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${out_file}" IN_LIST deployment_files) + return() + endif() + + _qt_internal_android_get_template_path(template_file ${target} + "app/${android_manifest_filename}") + set(temporary_file "${out_file}.tmp") + + # The file cannot be generated at cmake configure time, because androiddeployqt + # will override it at build time. We use this trick with temporary file to override + # it after the aux run of androiddeployqt. + add_custom_command(OUTPUT "${out_file}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${temporary_file}" + "${out_file}" + DEPENDS + "${template_file}" + "${temporary_file}" + ${target}_android_deploy_aux + ) + + _qt_internal_android_get_manifest_property(APP_PACKAGE_NAME ${target} + QT_ANDROID_PACKAGE_NAME "org.qtproject.example.$<MAKE_C_IDENTIFIER:${target}>") + _qt_internal_android_get_manifest_property(APP_NAME ${target} + QT_ANDROID_APP_NAME "${target}") + _qt_internal_android_get_manifest_property(APP_VERSION_CODE ${target} + QT_ANDROID_VERSION_CODE "1") + _qt_internal_android_get_manifest_property(APP_VERSION_NAME ${target} + QT_ANDROID_VERSION_NAME "1") + _qt_internal_android_get_manifest_property(APP_LIB_NAME ${target} OUTPUT_NAME "${target}") + + # For application icon we substitute the whole attribute definition, but not only value + # otherwise it leads to the Manifest processing issue. + set(target_property "$<TARGET_PROPERTY:${target},QT_ANDROID_APP_ICON>") + string(JOIN "" APP_ICON + "$<$<BOOL:${target_property}>:" + "android:icon=\"${target_property}\"" + ">" + ) + + file(READ "${template_file}" manifest_content) + string(REPLACE ">" "$<ANGLE-R>" manifest_content "${manifest_content}") + string(REPLACE ";" "$<SEMICOLON>" manifest_content "${manifest_content}") + string(REPLACE "," "$<COMMA>" manifest_content "${manifest_content}") + + _qt_internal_android_convert_permissions(APP_PERMISSIONS ${target} XML) + + set(feature_prefix "\n <uses-feature android:name=\"") + set(feature_suffix " \" android:required=\"false\" /$<ANGLE-R>") + set(feature_property "$<TARGET_PROPERTY:${target},QT_ANDROID_FEATURES>") + string(JOIN "" APP_FEATURES + "$<$<BOOL:${feature_property}>:" + "${feature_prefix}" + "$<JOIN:${feature_property},${feature_suffix},${feature_prefix}>" + "${feature_suffix}" + ">" + ) + + set(APP_ARGUMENTS "${QT_ANDROID_APPLICATION_ARGUMENTS}") + + _qt_internal_configure_file(GENERATE OUTPUT "${temporary_file}" + CONTENT "${manifest_content}") + + set_property(TARGET ${target} APPEND PROPERTY + _qt_android_deployment_files "${out_file}" "${temporary_file}") +endfunction() + +# Generates the top-level gradle.properties in the android-build directory +# The file contains the information about the versions of the android build +# tools, the list of supported ABIs. +function(_qt_internal_android_generate_bundle_gradle_properties target) + set(EXTRA_PROPERTIES "") + + set(gradle_properties_file_name "gradle.properties") + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(out_file "${android_build_dir}/${gradle_properties_file_name}") + + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${out_file}" IN_LIST deployment_files) + return() + endif() + + _qt_internal_android_get_template_path(template_file ${target} "${gradle_properties_file_name}") + _qt_internal_configure_file(CONFIGURE + OUTPUT "${out_file}" + INPUT "${template_file}" + ) + set_property(TARGET ${target} APPEND PROPERTY _qt_android_deployment_files "${out_file}") +endfunction() + +# Generates the local.properties for gradle builds. Contains the path to the +# Android SDK root. +function(_qt_internal_android_generate_bundle_local_properties target) + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + set(out_file "${android_build_dir}/local.properties") + + # Skip generating the file if it's already provided by user. + get_target_property(deployment_files ${target} _qt_android_deployment_files) + if("${out_file}" IN_LIST deployment_files) + return() + endif() + + file(TO_CMAKE_PATH "${ANDROID_SDK_ROOT}" ANDROID_SDK_ROOT_NATIVE) + _qt_internal_configure_file(CONFIGURE OUTPUT "${out_file}" + CONTENT "sdk.dir=${ANDROID_SDK_ROOT_NATIVE}\n") +endfunction() + +# Copies the customized Android package sources to the Android build directory +function(_qt_internal_android_copy_target_package_sources target) + _qt_internal_android_get_package_source_dir(package_source_dir ${target}) + + if(NOT package_source_dir) + return() + endif() + get_filename_component(package_source_dir "${package_source_dir}" ABSOLUTE) + + # Collect deployment files from use-defined package source directory + file(GLOB_RECURSE package_files + LIST_DIRECTORIES false + RELATIVE "${package_source_dir}" + "${package_source_dir}/*" + ) + + # Do not copy files that we treat as CMake templates, having '.in' extention. + # + # TODO: If it ever will be an issue we may exclude only templates that are + # known by our build system. + list(FILTER package_files EXCLUDE REGEX ".+\\.in$") + + _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + list(TRANSFORM package_files PREPEND "${android_build_dir}/" OUTPUT_VARIABLE out_package_files) + list(TRANSFORM package_files PREPEND "${package_source_dir}/" OUTPUT_VARIABLE in_package_files) + + if(in_package_files) + # TODO: Add cmake < 3.26 support + if(CMAKE_VERSION VERSION_LESS 3.26) + message(FATAL_ERROR "The use of QT_ANDROID_PACKAGE_SOURCE_DIR property with + the QT_USE_ANDROID_MODERN_BUNDLE option enabled requires CMake version >= 3.26.") + endif() + set(copy_commands COMMAND "${CMAKE_COMMAND}" -E copy_directory_if_different + "${package_source_dir}" "${android_build_dir}") + else() + # We actually have nothing to deploy. + return() + endif() + + add_custom_command(OUTPUT ${out_package_files} + ${copy_commands} + DEPENDS + ${in_package_files} + VERBATIM + ) + + set_target_properties(${target} PROPERTIES _qt_android_deployment_files "${out_package_files}") +endfunction() + +# Copies gradle scripts to a build directory. +function(_qt_internal_android_copy_gradle_files target output_directory) + _qt_internal_android_gradlew_name(gradlew_file_name) + _qt_internal_android_gradle_template_dir(gradle_template_dir) + + set(gradlew_file_src "${gradle_template_dir}/${gradlew_file_name}") + set(gradlew_file_dst "${output_directory}/${gradlew_file_name}") + + add_custom_command(OUTPUT "${gradlew_file_dst}" + COMMAND + ${CMAKE_COMMAND} -E copy_if_different "${gradlew_file_src}" "${gradlew_file_dst}" + DEPENDS "${gradlew_file_src}" + COMMENT "Copying gradlew script for ${target}" + VERBATIM + ) + + # TODO: make a more precise directory copying + set(gradle_dir_src "${gradle_template_dir}/gradle") + set(gradle_dir_dst "${output_directory}/gradle") + add_custom_command(OUTPUT "${gradle_dir_dst}" + COMMAND + ${CMAKE_COMMAND} -E copy_directory "${gradle_dir_src}" "${gradle_dir_dst}" + DEPENDS "${gradle_dir_src}" + COMMENT "Copying gradle support files for ${target}" + VERBATIM + ) + + add_custom_target(${target}_copy_gradle_files + DEPENDS + "${gradlew_file_dst}" + "${gradle_dir_dst}" + ) +endfunction() diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake index aa0e5bfed24..ed97a42c83f 100644 --- a/src/corelib/Qt6AndroidMacros.cmake +++ b/src/corelib/Qt6AndroidMacros.cmake @@ -120,6 +120,29 @@ function(_qt_internal_generate_android_permissions_json out_result target) set(${out_result} "${result}" PARENT_SCOPE) endfunction() +# Add the specific dynamic library as the dynamic feature for the Android application target. +function(qt6_add_android_dynamic_features target) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "FEATURE_TARGETS") + if(NOT QT_USE_ANDROID_MODERN_BUNDLE) + message(FATAL_ERROR "qt6_add_android_dynamic_features is only supported with" + " 'QT_USE_ANDROID_MODERN_BUNDLE' enabled.") + endif() + if(NOT TARGET ${target}) + message(FATAL_ERROR "${target} is not a target. Cannot add the dynamic features.") + endif() + get_target_property(android_type ${target} _qt_android_target_type) + if(NOT android_type STREQUAL "APPLICATION") + message(FATAL_ERROR "${target} is not an android executable target." + " Cannot add the dynamic features.") + endif() + if(arg_FEATURE_TARGETS) + set_property(TARGET ${target} + APPEND PROPERTY _qt_android_dynamic_features ${arg_FEATURE_TARGETS}) + else() + message(WARNING "No dynamic features provided.") + endif() +endfunction() + # Generate the deployment settings json file for a cmake target. function(qt6_android_generate_deployment_settings target) # Information extracted from mkspecs/features/android/android_deployment_settings.prf @@ -324,8 +347,9 @@ function(qt6_android_generate_deployment_settings target) __qt_internal_collect_plugin_library_files_v2("${target}" "${plugin_targets}" plugin_targets) string(APPEND file_contents " \"android-deploy-plugins\":\"${plugin_targets}\",\n") - _qt_internal_generate_android_permissions_json(permissions_json_array "${target}") - string(APPEND file_contents " \"permissions\": ${permissions_json_array},\n") + + _qt_internal_android_convert_permissions(permissions_genex ${target} JSON) + string(APPEND file_contents " \"permissions\": ${permissions_genex},\n") # App binary string(APPEND file_contents @@ -415,44 +439,6 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) endfunction() endif() -function(_qt_internal_add_android_permission target) - if(NOT TARGET ${target}) - message(FATAL_ERROR "Empty or invalid target for adding Android permission: (${target})") - endif() - - cmake_parse_arguments(arg "" "NAME" "ATTRIBUTES" ${ARGN}) - - if(NOT arg_NAME) - message(FATAL_ERROR "NAME for adding Android permission cannot be empty (${target})") - endif() - - set(permission_entry "${arg_NAME}") - - if(arg_ATTRIBUTES) - # Permission with additional attributes - list(LENGTH arg_ATTRIBUTES attributes_len) - math(EXPR attributes_modulus "${attributes_len} % 2") - if(NOT (attributes_len GREATER 1 AND attributes_modulus EQUAL 0)) - message(FATAL_ERROR "Android permission: ${arg_NAME} attributes: ${arg_ATTRIBUTES} must" - " be name-value pairs (for example: minSdkVersion 30)") - endif() - # Combine name-value pairs - set(index 0) - set(attributes "") - while(index LESS attributes_len) - list(GET arg_ATTRIBUTES ${index} name) - math(EXPR index "${index} + 1") - list(GET arg_ATTRIBUTES ${index} value) - string(APPEND attributes "android:${name}=\'${value}\' ") - math(EXPR index "${index} + 1") - endwhile() - set(permission_entry "${permission_entry}\;${attributes}") - endif() - - # Append the permission to the target's property - set_property(TARGET ${target} APPEND PROPERTY QT_ANDROID_PERMISSIONS "${permission_entry}") -endfunction() - function(qt6_add_android_permission target) _qt_internal_add_android_permission(${ARGV}) endfunction() @@ -519,18 +505,6 @@ function(qt6_android_add_apk_target target) ">" ) - # Make global apk and aab targets depend on the current apk target. - if(TARGET aab) - add_dependencies(aab ${target}_make_aab) - endif() - if(TARGET aar) - add_dependencies(aar ${target}_make_aar) - endif() - if(TARGET apk) - add_dependencies(apk ${target}_make_apk) - _qt_internal_create_global_apk_all_target_if_needed() - endif() - _qt_internal_android_get_deployment_tool(deployment_tool) # No need to use genex for the BINARY_DIR since it's read-only. @@ -719,6 +693,10 @@ function(qt6_android_add_apk_target target) ) add_dependencies(${target}_make_aab ${target}_prepare_apk_dir) + # Make global apk, aab, and aar targets depend on the respective targets. + _qt_internal_android_add_global_package_dependencies(${target}) + _qt_internal_create_global_apk_all_target_if_needed() + if(QT_IS_ANDROID_MULTI_ABI_EXTERNAL_PROJECT) # When building per-ABI external projects we only need to copy ABI-specific libraries and # resources to the "main" ABI android build folder. @@ -1661,13 +1639,26 @@ endfunction() # module and is executed implicitly when configuring user projects. function(_qt_internal_android_executable_finalizer target) set_property(TARGET ${target} PROPERTY _qt_android_executable_finalizer_called TRUE) + set_property(TARGET ${target} PROPERTY _qt_android_in_finalizer "EXECUTABLE") _qt_internal_expose_android_package_source_dir_to_ide(${target}) _qt_internal_configure_android_multiabi_target("${target}") qt6_android_generate_deployment_settings("${target}") - qt6_android_add_apk_target("${target}") + if(QT_USE_ANDROID_MODERN_BUNDLE) + _qt_internal_android_generate_dynamic_feature_names("${target}") + _qt_internal_android_add_dynamic_feature_deployment("${target}") + + _qt_internal_android_prepare_gradle_build("${target}") + _qt_internal_android_add_aux_deployment("${target}") + + _qt_internal_collect_apk_dependencies_defer() + _qt_internal_collect_apk_imported_dependencies_defer("${target}") + else() + qt6_android_add_apk_target("${target}") + endif() _qt_internal_android_create_runner_wrapper("${target}") + set_property(TARGET ${target} PROPERTY _qt_android_in_finalizer "") endfunction() # Helper to add the android executable finalizer. @@ -1737,8 +1728,9 @@ function(_qt_internal_android_app_runner_arguments target out_runner_path out_ar set(${out_runner_path} "${runner_dir}/qt-android-runner.py" PARENT_SCOPE) _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + _qt_internal_android_get_platform_tools_path(platform_tools) set(${out_arguments} - "--adb" "${ANDROID_SDK_ROOT}/platform-tools/adb" + "--adb" "${platform_tools}/adb" "--build-path" "${android_build_dir}" "--apk" "${android_build_dir}/${target}.apk" PARENT_SCOPE @@ -1754,8 +1746,13 @@ function(_qt_internal_android_get_target_android_build_dir out_build_dir target) endif() endfunction() +function(_qt_internal_android_get_target_deployment_dir out_deploy_dir target) + _qt_internal_android_get_target_android_build_dir(build_dir ${target}) + set(${out_deploy_dir} "${build_dir}/app" PARENT_SCOPE) +endfunction() + function(_qt_internal_expose_android_package_source_dir_to_ide target) - get_target_property(android_package_source_dir ${target} QT_ANDROID_PACKAGE_SOURCE_DIR) + _qt_internal_android_get_package_source_dir(android_package_source_dir ${target}) if(android_package_source_dir) get_target_property(target_source_dir ${target} SOURCE_DIR) if(NOT IS_ABSOLUTE "${android_package_source_dir}") @@ -1778,6 +1775,71 @@ function(_qt_internal_expose_android_package_source_dir_to_ide target) endif() endfunction() +function(_qt_internal_android_add_aux_deployment target) + cmake_parse_arguments(arg "" "OUTPUT_TARGET_NAME;DEPLOYMENT_DIRECTORY" "EXTRA_ARGS" ${ARGN}) + _qt_internal_validate_all_args_are_parsed(arg) + + string(JOIN "" deployment_file + "$<GENEX_EVAL:" + "$<TARGET_PROPERTY:${target},QT_ANDROID_DEPLOYMENT_SETTINGS_FILE>" + ">" + ) + + _qt_internal_android_get_deployment_tool(deployment_tool) + if(arg_DEPLOYMENT_DIRECTORY) + set(deployment_dir "${arg_DEPLOYMENT_DIRECTORY}") + else() + _qt_internal_android_get_target_deployment_dir(deployment_dir ${target}) + endif() + + cmake_policy(PUSH) + if(POLICY CMP0116) + # Without explicitly setting this policy to NEW, we get a warning + # even though we ensure there's actually no problem here. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/21959 + cmake_policy(SET CMP0116 NEW) + set(relative_to_dir ${CMAKE_CURRENT_BINARY_DIR}) + else() + set(relative_to_dir ${CMAKE_BINARY_DIR}) + endif() + + set(target_file_copy_relative_path + "libs/${CMAKE_ANDROID_ARCH_ABI}/$<TARGET_FILE_NAME:${target}>") + _qt_internal_copy_file_if_different_command(copy_command + "$<TARGET_FILE:${target}>" + "${deployment_dir}/${target_file_copy_relative_path}" + ) + + _qt_internal_android_get_use_terminal_for_deployment(uses_terminal) + + # TODO: We use androiddeployqt to collect target depdenencies and produce the lib.xml file + # which autoloads the collected libraries. Should be done using GRE and transitive properties + # in the future. + set(libs_xml "${deployment_dir}/res/values/libs.xml") + add_custom_command(OUTPUT "${libs_xml}" + COMMAND ${copy_command} + COMMAND "${deployment_tool}" + --input "${deployment_file}" + --output "${deployment_dir}" + --builddir "${relative_to_dir}" + --aux-mode + ${arg_EXTRA_ARGS} + #TODO: Support signing + COMMENT "Deploying Android artifacts for ${target}" + DEPENDS "${target}" "${deployment_file}" + VERBATIM + ${uses_terminal} + ) + + if(NOT arg_OUTPUT_TARGET_NAME) + set(arg_OUTPUT_TARGET_NAME ${target}_android_deploy_aux) + endif() + + add_custom_target(${arg_OUTPUT_TARGET_NAME} DEPENDS "${libs_xml}") + + cmake_policy(POP) +endfunction() + # Enables the terminal usage for the add_custom_command calls when verbose deployment is enabled. function(_qt_internal_android_get_use_terminal_for_deployment out_var) if(QT_ENABLE_VERBOSE_DEPLOYMENT) @@ -1822,6 +1884,45 @@ function(_qt_internal_android_get_deployment_type_option out_var release_flag de endif() endfunction() +# Returns the path to the android template directory, that are used by CMake +# deployment procedures. +function(_qt_internal_android_template_dir out_var) + if(PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD) + set(${out_var} "${QtBase_SOURCE_DIR}/src/android/templates_cmake" PARENT_SCOPE) + else() + set(${out_var} + "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DATA}/src/android/templates_cmake" PARENT_SCOPE) + endif() +endfunction() + +# Return the path to the target template directory if it's set for the target. +# Then this path is stored in the target QT_ANDROID_PACKAGE_SOURCE_DIR property +# and can only be effectively read in android finalizers. +function(_qt_internal_android_get_package_source_dir out_var target) + get_target_property(in_finalizer ${target} _qt_android_in_finalizer) + if(NOT in_finalizer) + message(FATAL_ERROR "The '_qt_internal_android_get_package_source_dir' function is" + " called outside the Android finalizer." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + get_target_property(package_src_dir ${target} QT_ANDROID_PACKAGE_SOURCE_DIR) + if(NOT package_src_dir) + set(package_src_dir "") + endif() + set(${out_var} "${package_src_dir}" PARENT_SCOPE) +endfunction() + +# Add target_make_<apk|aab> as the depednecy for the respective global apk/aab +# target. +function(_qt_internal_android_add_global_package_dependencies target) + foreach(type apk aab aar) + # Make global apk and aab targets depend on the current apk target. + if(TARGET ${type} AND TARGET ${target}_make_${type}) + add_dependencies(${type} ${target}_make_${type}) + endif() + endforeach() +endfunction() + function(_qt_internal_android_get_target_abis out_abis target) get_target_property(target_abis ${target} QT_ANDROID_ABIS) if(target_abis) @@ -1840,5 +1941,10 @@ function(_qt_internal_android_get_target_abis out_abis target) set(${out_abis} "${android_abis}" PARENT_SCOPE) endfunction() +# Returns the path to the Android platform-tools(adb is located there). +function(_qt_internal_android_get_platform_tools_path out_var) + set(${out_var} "${ANDROID_SDK_ROOT}/platform-tools" PARENT_SCOPE) +endfunction() + set(QT_INTERNAL_ANDROID_TARGET_BUILD_DIR_SUPPORT ON CACHE INTERNAL "Indicates that Qt supports per-target Android build directories") diff --git a/src/corelib/Qt6AndroidPermissionHelpers.cmake b/src/corelib/Qt6AndroidPermissionHelpers.cmake new file mode 100644 index 00000000000..7f851e14667 --- /dev/null +++ b/src/corelib/Qt6AndroidPermissionHelpers.cmake @@ -0,0 +1,126 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Generates the generator expression that converts the 'target' +# QT_ANDROID_PERMISSIONS property to the specific 'type'. +# +# It's expected that each element in QT_ANDROID_PERMISSIONS list has specific +# format: +# <name>\;<permission>\\\;<extra1>\;<value>\\\;<extra2>\;<value> +# +# Synopsis +# _qt_internal_android_convert_permissions(out_var target <JSON|XML>) +# +# Arguments +# +# `out_var` +# The name of the variable where the resulting generator expression is +# stored. +# +# `target` +# The name of the target. +# +# `JSON` +# Generate JSON array known by androiddeployqt. +# +# `XML` +# Generate XML content compatible with AndroidManifest.xml. +function(_qt_internal_android_convert_permissions out_var target type) + set(permissions_property "$<TARGET_PROPERTY:${target},QT_ANDROID_PERMISSIONS>") + set(permissions_genex "$<$<BOOL:${permissions_property}>:") + if(type STREQUAL "JSON") + set(pref "{ \"") + set(post "\" }") + set(indent "\n ") + string(APPEND permissions_genex + "[${indent}$<JOIN:" + "$<JOIN:" + "${pref}$<JOIN:" + "${permissions_property}," + "${post}$<COMMA>${indent}${pref}" + ">${post}," + "\": \"" + ">," + "\"$<COMMA> \"" + ">\n ]" + ) + elseif(type STREQUAL "XML") + set(pref "<uses-permission\n android:") + set(post "' /$<ANGLE-R>\n") + string(APPEND permissions_genex + "$<JOIN:" + "$<JOIN:" + "${pref}$<JOIN:" + "${permissions_property}," + "${post}${pref}" + ">${post}\n," + "='" + ">," + "' android:" + ">" + ) + else() + message(FATAL_ERROR "Invalid type ${type}. Supported types: JSON, XML") + endif() + string(APPEND permissions_genex ">") + + set(${out_var} "${permissions_genex}" PARENT_SCOPE) +endfunction() + +# Add the specific Android permission to the target. The permission is stored +# in the QT_ANDROID_PERMISSIONS property(the property is not a public API) +# and has the following format: +# <name>\;<permission>\\\;<extra1>\;<value>\\\;<extra2>\;<value> +# +# Synopsis +# _qt_internal_add_android_permission(target NAME <permission> +# ATTRIBUTES <extra1> <value1> +# [<extra2> <value2>]... +# ) +# +# Arguments +# +# `target` +# The Android target. +# +# `NAME` +# The permission name. E.g. 'android.permission.CAMERA'. +# +# `ATTRIBUTES` +# Extra permission attribute key-value pairs. +# See https://developer.android.com/guide/topics/manifest/uses-permission-element +# for details. +function(_qt_internal_add_android_permission target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "Empty or invalid target for adding Android permission: (${target})") + endif() + + cmake_parse_arguments(arg "" "NAME" "ATTRIBUTES" ${ARGN}) + + if(NOT arg_NAME) + message(FATAL_ERROR "NAME for adding Android permission cannot be empty (${target})") + endif() + + set(permission_entry "name\;${arg_NAME}") + if(arg_ATTRIBUTES) + # Permission with additional attributes + list(LENGTH arg_ATTRIBUTES attributes_len) + math(EXPR attributes_modulus "${attributes_len} % 2") + if(NOT (attributes_len GREATER 1 AND attributes_modulus EQUAL 0)) + message(FATAL_ERROR "Android permission: ${arg_NAME} attributes: ${arg_ATTRIBUTES}" + " must be name-value pairs (for example: minSdkVersion 30)") + endif() + # Combine name-value pairs + set(index 0) + while(index LESS attributes_len) + list(GET arg_ATTRIBUTES ${index} name) + math(EXPR index "${index} + 1") + list(GET arg_ATTRIBUTES ${index} value) + string(APPEND permission_entry "\\\;${name}\;${value}") + math(EXPR index "${index} + 1") + endwhile() + endif() + + # Append the permission to the target's property + set_property(TARGET ${target} APPEND PROPERTY QT_ANDROID_PERMISSIONS "${permission_entry}") +endfunction() diff --git a/src/corelib/Qt6CoreConfigExtras.cmake.in b/src/corelib/Qt6CoreConfigExtras.cmake.in index 15405197a61..8a88d558fa8 100644 --- a/src/corelib/Qt6CoreConfigExtras.cmake.in +++ b/src/corelib/Qt6CoreConfigExtras.cmake.in @@ -30,6 +30,10 @@ _qt_internal_setup_deploy_support() if(ANDROID_PLATFORM) include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") + include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") + include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") + include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") + _qt_internal_create_global_android_targets() _qt_internal_collect_default_android_abis() if(__qt_Core_targets_file_included) diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 7b522f99abb..5bc85ae7f7a 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -683,6 +683,9 @@ function(_qt_internal_create_executable target) ) qt6_android_apply_arch_suffix("${target}") + if(QT_USE_ANDROID_MODERN_BUNDLE) + _qt_internal_set_android_application_gradle_defaults(${target}) + endif() else() cmake_policy(PUSH) __qt_internal_set_cmp0156() diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index d74894e1e42..4388122b7b9 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QAbstractAnimation diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h index 69a30556a3a..b4b43e64a1d 100644 --- a/src/corelib/animation/qabstractanimation.h +++ b/src/corelib/animation/qabstractanimation.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QABSTRACTANIMATION_H #define QABSTRACTANIMATION_H diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index d6c245f36f0..51c635f1bed 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QABSTRACTANIMATION_P_H #define QABSTRACTANIMATION_P_H diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp index d2572a7462b..ae96069e86f 100644 --- a/src/corelib/animation/qanimationgroup.cpp +++ b/src/corelib/animation/qanimationgroup.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QAnimationGroup diff --git a/src/corelib/animation/qanimationgroup.h b/src/corelib/animation/qanimationgroup.h index 412e2d442ea..07d24ae1f73 100644 --- a/src/corelib/animation/qanimationgroup.h +++ b/src/corelib/animation/qanimationgroup.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QANIMATIONGROUP_H #define QANIMATIONGROUP_H diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h index 334f780968a..a09bc6ebcc6 100644 --- a/src/corelib/animation/qanimationgroup_p.h +++ b/src/corelib/animation/qanimationgroup_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QANIMATIONGROUP_P_H #define QANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 86e9417b595..0b43a73434f 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QParallelAnimationGroup diff --git a/src/corelib/animation/qparallelanimationgroup.h b/src/corelib/animation/qparallelanimationgroup.h index 77bc6eabac7..9442f4f7355 100644 --- a/src/corelib/animation/qparallelanimationgroup.h +++ b/src/corelib/animation/qparallelanimationgroup.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QPARALLELANIMATIONGROUP_H #define QPARALLELANIMATIONGROUP_H diff --git a/src/corelib/animation/qparallelanimationgroup_p.h b/src/corelib/animation/qparallelanimationgroup_p.h index 62c53d36097..482b9555c7d 100644 --- a/src/corelib/animation/qparallelanimationgroup_p.h +++ b/src/corelib/animation/qparallelanimationgroup_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QPARALLELANIMATIONGROUP_P_H #define QPARALLELANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp index 344b21946e3..74e22e2f053 100644 --- a/src/corelib/animation/qpauseanimation.cpp +++ b/src/corelib/animation/qpauseanimation.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QPauseAnimation diff --git a/src/corelib/animation/qpauseanimation.h b/src/corelib/animation/qpauseanimation.h index f661459f835..bf7863a171c 100644 --- a/src/corelib/animation/qpauseanimation.h +++ b/src/corelib/animation/qpauseanimation.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QPAUSEANIMATION_H #define QPAUSEANIMATION_H diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index 04f048af753..b4c6b6ff8a6 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QPropertyAnimation diff --git a/src/corelib/animation/qpropertyanimation.h b/src/corelib/animation/qpropertyanimation.h index 038c202b8f3..590a6ddaf15 100644 --- a/src/corelib/animation/qpropertyanimation.h +++ b/src/corelib/animation/qpropertyanimation.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QPROPERTYANIMATION_H #define QPROPERTYANIMATION_H diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h index ef5534cd9c3..c1918ae1bf8 100644 --- a/src/corelib/animation/qpropertyanimation_p.h +++ b/src/corelib/animation/qpropertyanimation_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QPROPERTYANIMATION_P_H #define QPROPERTYANIMATION_P_H diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp index 260481dbef5..d11249ca7ed 100644 --- a/src/corelib/animation/qsequentialanimationgroup.cpp +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default /*! \class QSequentialAnimationGroup diff --git a/src/corelib/animation/qsequentialanimationgroup.h b/src/corelib/animation/qsequentialanimationgroup.h index 6786078170d..b7f9c1b17a1 100644 --- a/src/corelib/animation/qsequentialanimationgroup.h +++ b/src/corelib/animation/qsequentialanimationgroup.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QSEQUENTIALANIMATIONGROUP_H #define QSEQUENTIALANIMATIONGROUP_H diff --git a/src/corelib/animation/qsequentialanimationgroup_p.h b/src/corelib/animation/qsequentialanimationgroup_p.h index cbdf204d0a6..131902b5aa5 100644 --- a/src/corelib/animation/qsequentialanimationgroup_p.h +++ b/src/corelib/animation/qsequentialanimationgroup_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QSEQUENTIALANIMATIONGROUP_P_H #define QSEQUENTIALANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index be5c09519e9..bd12d5dae95 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #include "qvariantanimation.h" #include "qvariantanimation_p.h" diff --git a/src/corelib/animation/qvariantanimation.h b/src/corelib/animation/qvariantanimation.h index 4bdb9713578..172ee0d6090 100644 --- a/src/corelib/animation/qvariantanimation.h +++ b/src/corelib/animation/qvariantanimation.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QVARIANTANIMATION_H #define QVARIANTANIMATION_H diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h index 0ac238a882b..5fdd9666dba 100644 --- a/src/corelib/animation/qvariantanimation_p.h +++ b/src/corelib/animation/qvariantanimation_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QVARIANTANIMATION_P_H #define QVARIANTANIMATION_P_H diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 909041cf44b..096dce9e84e 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -591,6 +591,7 @@ qt_config_compile_test(cpp_winrt runtimeobject CODE "// Including winrt/base.h causes an error in some configurations (Windows 10 SDK + c++20) +# include <guiddef.h> // required by clang-cl: https://github.com/microsoft/cppwinrt/issues/1179 # include <winrt/base.h> int main(void) diff --git a/src/corelib/doc/snippets/qprocess/CMakeLists.txt b/src/corelib/doc/snippets/qprocess/CMakeLists.txt index 07b66d0b36b..d410d479f7f 100644 --- a/src/corelib/doc/snippets/qprocess/CMakeLists.txt +++ b/src/corelib/doc/snippets/qprocess/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_UNITY_BUILD OFF) + add_library(snippets_qprocess OBJECT qprocess-createprocessargumentsmodifier.cpp qprocess-simpleexecution.cpp diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 6f995fe082e..5a0be1d895e 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -893,6 +893,12 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM { Q_CHECK_FILE_NAME(entry, false); + // Detection of WasDeletedAttribute is imperfect: in general, if we can + // successfully stat() or access() a file, it hasn't been deleted (though + // there are exceptions, like /proc/XXX/fd/ entries on Linux). So we have + // to restore this flag in case we fail to stat() anything. + auto hadBeenDeleted = data.entryFlags & QFileSystemMetaData::WasDeletedAttribute; + #if defined(Q_OS_DARWIN) if (what & (QFileSystemMetaData::BundleType | QFileSystemMetaData::CaseSensitive)) { if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) @@ -1103,6 +1109,11 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM if (entryErrno != 0) { what &= ~QFileSystemMetaData::LinkType; // don't clear link: could be broken symlink data.clearFlags(what); + + // see comment at the top + data.entryFlags |= hadBeenDeleted; + data.knownFlagsMask |= hadBeenDeleted; + return false; } return true; diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 0886f63b810..9c1bb45dd2d 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1309,8 +1309,11 @@ QByteArray QIODevice::readAll() The newline character ('\\n') is included in the buffer. If a newline is not encountered before maxSize - 1 bytes are read, a - newline will not be inserted into the buffer. On windows newline - characters are replaced with '\\n'. + newline will not be inserted into the buffer. + + \note Newline translation(e.g., converting \r to \n) is performed + only when the device is opened for reading with QIODevice::Text + flag. Note that on sequential devices, data may not be immediately available, which may result in a partial line being returned. By calling the diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index c1f01267949..038db4d857e 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2017 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qtemporaryfile.h" @@ -881,6 +882,11 @@ void QTemporaryFile::setFileTemplate(const QString &name) be used for a similar purpose too, particularly if the destination file is not temporary. + \note Calling rename() does not disable autoRemove. If you want the renamed + file to persist, you must call setAutoRemove and set it to \c false after + calling rename(). Otherwise, the file will be deleted when the QTemporaryFile + object is destroyed. + \sa QSaveFile, QSaveFile::commit(), QFile::rename() */ bool QTemporaryFile::rename(const QString &newName) diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 565ce04c6ce..a4fbf69da8b 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -3660,17 +3660,29 @@ QList<QUrl> QUrl::fromStringList(const QStringList &urls, ParsingMode mode) */ size_t qHash(const QUrl &url, size_t seed) noexcept { + QtPrivate::QHashCombineWithSeed hasher(seed); + + // non-commutative, we must hash the port first if (!url.d) - return qHash(-1, seed); // the hash of an unset port (-1) - - return qHash(url.d->scheme) ^ - qHash(url.d->userName) ^ - qHash(url.d->password) ^ - qHash(url.d->host) ^ - qHash(url.d->port, seed) ^ - qHash(url.d->path) ^ - qHash(url.d->query) ^ - qHash(url.d->fragment); + return hasher(0, -1); + size_t state = hasher(0, url.d->port); + + if (url.d->hasScheme()) + state = hasher(state, url.d->scheme); + if (url.d->hasUserInfo()) { + // see presentSections(), appendUserName(), etc. + state = hasher(state, url.d->userName); + state = hasher(state, url.d->password); + } + if (url.d->hasHost() || url.d->isLocalFile()) // for XDG compatibility + state = hasher(state, url.d->host); + if (url.d->hasPath()) + state = hasher(state, url.d->path); + if (url.d->hasQuery()) + state = hasher(state, url.d->query); + if (url.d->hasFragment()) + state = hasher(state, url.d->fragment); + return state; } static QUrl adjustFtpPath(QUrl url) diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index a19e39685f1..3b92c5f48f2 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2189,7 +2189,7 @@ bool QCoreApplication::installTranslator(QTranslator *translationFile) QCoreApplicationPrivate *d = self->d_func(); { - QMutexLocker locker(&d->translateMutex); + QWriteLocker locker(&d->translateMutex); d->translators.prepend(translationFile); } @@ -2221,7 +2221,7 @@ bool QCoreApplication::removeTranslator(QTranslator *translationFile) if (!QCoreApplicationPrivate::checkInstance("removeTranslator")) return false; QCoreApplicationPrivate *d = self->d_func(); - QMutexLocker locker(&d->translateMutex); + QWriteLocker locker(&d->translateMutex); if (d->translators.removeAll(translationFile)) { #ifndef QT_NO_QOBJECT locker.unlock(); @@ -2306,7 +2306,7 @@ QString QCoreApplication::translate(const char *context, const char *sourceText, if (self) { QCoreApplicationPrivate *d = self->d_func(); - QMutexLocker locker(&d->translateMutex); + QReadLocker locker(&d->translateMutex); if (!d->translators.isEmpty()) { QList<QTranslator*>::ConstIterator it; QTranslator *translationFile; @@ -2332,23 +2332,13 @@ QString qtTrId(const char *id, int n) return QCoreApplication::translate(nullptr, id, nullptr, n); } -/*! - \internal - Returns a locked mutex handle if \a translator is registered in QCoreApplication, - and might be therefore queried for translations from other threads. - Returns an unlocked/dummy QMutexLocker otherwise. - */ -std::unique_lock<QMutex> QCoreApplicationPrivate::mutexLockerForTranslator(QTranslator *translator) +bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator) { if (!QCoreApplication::self) - return std::unique_lock<QMutex>(); - + return false; QCoreApplicationPrivate *d = QCoreApplication::self->d_func(); - std::unique_lock<QMutex> locker(d->translateMutex); - if (!d->translators.contains(translator)) - locker.unlock(); - - return locker; + QReadLocker locker(&d->translateMutex); + return d->translators.contains(translator); } #else diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 4cb6254e98d..0027b1ad57f 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -30,12 +30,10 @@ #include "private/qcore_mac_p.h" #endif -#ifndef QT_NO_TRANSLATION -#include <mutex> -#endif - QT_BEGIN_NAMESPACE +typedef QList<QTranslator*> QTranslatorList; + class QAbstractEventDispatcher; #ifndef QT_NO_QOBJECT @@ -149,9 +147,9 @@ public: static bool is_app_closing; #endif #ifndef QT_NO_TRANSLATION - QList<QTranslator*> translators; - QMutex translateMutex; - static std::unique_lock<QMutex> mutexLockerForTranslator(QTranslator *translator); + QTranslatorList translators; + QReadWriteLock translateMutex; + static bool isTranslatorInstalled(QTranslator *translator); #endif static bool setuidAllowed; diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 60bcdedba13..7fe36d37bce 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -2080,6 +2080,17 @@ bool QMetaPropertyBuilder::isBindable() const else return false; } +/*! + Returns \c true if the property is required. + The default is \c false. + */ +bool QMetaPropertyBuilder::isRequired() const +{ + if (auto d = d_func()) + return d->flag(Bindable); + else + return false; +} /*! Sets this property to readable if \a value is true. @@ -2236,6 +2247,15 @@ void QMetaPropertyBuilder::setBindable(bool value) } /*! + Sets the\c REQUIRED flag on this property to \a value + */ +void QMetaPropertyBuilder::setRequired(bool value) +{ + if (auto d = d_func()) + d->setFlag(Required, value); +} + +/*! Returns the revision of this property. \sa setRevision() diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index cf64e51c30c..36cd08d908c 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -216,6 +216,7 @@ public: bool isFinal() const; bool isAlias() const; bool isBindable() const; + bool isRequired() const; void setReadable(bool value); void setWritable(bool value); @@ -230,6 +231,7 @@ public: void setFinal(bool value); void setAlias(bool value); void setBindable(bool value); + void setRequired(bool value); int revision() const; void setRevision(int revision); diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 1242a40f185..45c947004f5 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -981,12 +981,6 @@ void QMetaType::unregisterMetaType(QMetaType type) than the QMetaType \a rhs, otherwise returns \c false. */ -/*! \internal */ -bool QMetaTypeModuleHelper::convert(const void *, int, void *, int) const -{ - return false; -} - static constexpr auto createStaticTypeToIdMap() { #define QT_ADD_STATIC_METATYPE(MetaTypeName, MetaTypeId, RealName) \ @@ -1049,8 +1043,8 @@ static bool qIntegerConversionFromFPHelper(From from, To *to) return true; } -// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class -static constexpr struct : QMetaTypeModuleHelper +namespace { +struct QCoreVariantHelper : QMetaTypeModuleHelper { template<typename T, typename LiteralWrapper = std::conditional_t<std::is_same_v<T, QString>, QLatin1StringView, const char *>> @@ -1060,7 +1054,8 @@ static constexpr struct : QMetaTypeModuleHelper return !(str.isEmpty() || str == LiteralWrapper("0") || str == LiteralWrapper("false")); } - const QtPrivate::QMetaTypeInterface *interfaceForType(int type) const override { + static const QtPrivate::QMetaTypeInterface *interfaceForType(int type) + { switch (type) { QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_METATYPE_CONVERT_ID_TO_TYPE) QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_CONVERT_ID_TO_TYPE) @@ -1072,11 +1067,14 @@ static constexpr struct : QMetaTypeModuleHelper } } -#ifndef QT_BOOTSTRAPPED - bool convert(const void *from, int fromTypeId, void *to, int toTypeId) const override + static bool convert(const void *from, int fromTypeId, void *to, int toTypeId) { Q_ASSERT(fromTypeId != toTypeId); +#ifdef QT_BOOTSTRAPPED + Q_UNUSED(from); + Q_UNUSED(to); +#else // canConvert calls with two nullptr bool onlyCheck = (from == nullptr && to == nullptr); @@ -1747,26 +1745,28 @@ QT_WARNING_DISABLE_CLANG("-Wtautological-compare") QT_WARNING_POP } +#endif // !QT_BOOTSTRAPPED return false; } -#endif // !QT_BOOTSTRAPPED -} metatypeHelper = {}; +}; +} // unnamed namespace -Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper = nullptr; -Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper = nullptr; +Q_CONSTINIT Q_CORE_EXPORT QMetaTypeModuleHelper qMetaTypeGuiHelper = {}; +Q_CONSTINIT Q_CORE_EXPORT QMetaTypeModuleHelper qMetaTypeWidgetsHelper = {}; -static const QMetaTypeModuleHelper *qModuleHelperForType(int type) +#ifndef QT_BOOTSTRAPPED +static bool tryConvertBuiltinTypes(const void *from, int fromTypeId, void *to, int toTypeId) { + int type = qMax(fromTypeId, toTypeId); if (type <= QMetaType::LastCoreType) - return &metatypeHelper; + return QCoreVariantHelper::convert(from, fromTypeId, to, toTypeId); if (type >= QMetaType::FirstGuiType && type <= QMetaType::LastGuiType) - return qMetaTypeGuiHelper; + return qMetaTypeGuiHelper.convert(from, fromTypeId, to, toTypeId); else if (type >= QMetaType::FirstWidgetsType && type <= QMetaType::LastWidgetsType) - return qMetaTypeWidgetsHelper; - return nullptr; + return qMetaTypeWidgetsHelper.convert(from, fromTypeId, to, toTypeId); + return false; } -#ifndef QT_BOOTSTRAPPED template<typename T, typename Key> class QMetaTypeFunctionRegistry { @@ -2471,10 +2471,8 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, int fromTypeId = fromType.id(); int toTypeId = toType.id(); - if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) { - if (moduleHelper->convert(from, fromTypeId, to, toTypeId)) - return true; - } + if (tryConvertBuiltinTypes(from, fromTypeId, to, toTypeId)) + return true; const auto f = customTypesConversionRegistry()->function({fromTypeId, toTypeId}); if (f) return (*f)(from, to); @@ -2676,10 +2674,9 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType) if (fromTypeId == toTypeId) return true; - if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) { - if (moduleHelper->convert(nullptr, fromTypeId, nullptr, toTypeId)) - return true; - } + if (tryConvertBuiltinTypes(nullptr, fromTypeId, nullptr, toTypeId)) + return true; + const ConverterFunction * const f = customTypesConversionRegistry()->function(std::make_pair(fromTypeId, toTypeId)); if (f) @@ -2879,8 +2876,12 @@ void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeN static const QtPrivate::QMetaTypeInterface *interfaceForStaticType(int typeId) { Q_ASSERT(typeId < QMetaType::User); - if (auto moduleHelper = qModuleHelperForType(typeId)) - return moduleHelper->interfaceForType(typeId); + if (typeId <= QMetaType::LastCoreType) + return QCoreVariantHelper::interfaceForType(typeId); + if (typeId >= QMetaType::FirstGuiType && typeId <= QMetaType::LastGuiType) + return qMetaTypeGuiHelper.interfaceForType(typeId); + if (typeId >= QMetaType::FirstWidgetsType && typeId <= QMetaType::LastWidgetsType) + return qMetaTypeWidgetsHelper.interfaceForType(typeId); return nullptr; } @@ -2909,40 +2910,18 @@ bool QMetaType::isRegistered(int type) return interfaceForTypeNoWarning(type) != nullptr; } -namespace { -enum NormalizeTypeMode { - DontNormalizeType, - TryNormalizeType -}; -} -template <NormalizeTypeMode tryNormalizedType> static inline -const QtPrivate::QMetaTypeInterface *qMetaTypeTypeImpl(QByteArrayView name) +static const QtPrivate::QMetaTypeInterface *findMetaTypeByName(QByteArrayView name) { - if (name.isEmpty()) - return nullptr; - + Q_PRE(!name.isEmpty()); int type = qMetaTypeStaticType(name); if (type != QMetaType::UnknownType) { return interfaceForStaticType(type); #ifndef QT_BOOTSTRAPPED - } else { - QReadLocker locker(&customTypeRegistry()->lock); + } else if (customTypeRegistry.exists()) { + QReadLocker locker(&customTypeRegistry->lock); auto it = customTypeRegistry->aliases.constFind(name); if (it != customTypeRegistry->aliases.constEnd()) return it.value().data(); - -#ifndef QT_NO_QOBJECT - if (tryNormalizedType) { - const char *typeName = name.constData(); - const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); - type = qMetaTypeStaticType(normalizedTypeName); - if (type == QMetaType::UnknownType) { - return customTypeRegistry->aliases.value(normalizedTypeName).data(); - } else { - return interfaceForStaticType(type); - } - } -#endif #endif } return nullptr; @@ -2969,7 +2948,9 @@ const QtPrivate::QMetaTypeInterface *qMetaTypeTypeImpl(QByteArrayView name) */ int qMetaTypeTypeInternal(QByteArrayView name) { - const QtPrivate::QMetaTypeInterface *iface = qMetaTypeTypeImpl<DontNormalizeType>(name); + const QtPrivate::QMetaTypeInterface *iface = nullptr; + if (!name.isEmpty()) + iface = findMetaTypeByName(name); return iface ? iface->typeId.loadRelaxed() : QMetaType::UnknownType; } @@ -3138,7 +3119,20 @@ QMetaType QMetaType::underlyingType() const */ QMetaType QMetaType::fromName(QByteArrayView typeName) { - return QMetaType(qMetaTypeTypeImpl<TryNormalizeType>(typeName)); + if (typeName.isEmpty()) + return QMetaType(); + + const QtPrivate::QMetaTypeInterface *iface = findMetaTypeByName(typeName); + if (iface) + return QMetaType(iface); + +#if !defined(QT_NO_QOBJECT) + const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName.constData()); + if (normalizedTypeName != typeName) + iface = findMetaTypeByName(normalizedTypeName); +#endif + + return QMetaType(iface); } /*! diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index c05bce5fc8f..b39e1de0be2 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -40,25 +40,29 @@ QT_BEGIN_NAMESPACE assign_and_return \ } -class Q_CORE_EXPORT QMetaTypeModuleHelper +struct QMetaTypeModuleHelper { - Q_DISABLE_COPY_MOVE(QMetaTypeModuleHelper) -protected: - QMetaTypeModuleHelper() = default; - ~QMetaTypeModuleHelper() = default; -public: - Q_WEAK_OVERLOAD // prevent it from entering the ABI and rendering constexpr useless static constexpr auto makePair(int from, int to) -> quint64 { return (quint64(from) << 32) + quint64(to); } - virtual const QtPrivate::QMetaTypeInterface *interfaceForType(int) const = 0; - virtual bool convert(const void *, int, void *, int) const; + static const QtPrivate::QMetaTypeInterface *interfaceForType_dummy(int) + { + return nullptr; + } + + static bool convert_dummy(const void *, int, void *, int) + { + return false; + } + + decltype(&interfaceForType_dummy) interfaceForType = &interfaceForType_dummy; + decltype(&convert_dummy) convert = &convert_dummy; }; -extern Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper; -extern Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper; +extern Q_CORE_EXPORT QMetaTypeModuleHelper qMetaTypeGuiHelper; +extern Q_CORE_EXPORT QMetaTypeModuleHelper qMetaTypeWidgetsHelper; namespace QtMetaTypePrivate { template<typename T> diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 47483dc6470..c8348aba16a 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -39,6 +39,8 @@ #include <vector> #include <memory> +#include <QtCore/qmutex.h> + QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcTranslator, "qt.core.qtranslator") @@ -259,6 +261,8 @@ class QTranslatorPrivate : public QObjectPrivate public: enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96, Language = 0xa7 }; + mutable QMutex lock; + QTranslatorPrivate() : #if defined(QT_USE_MMAP) used_mmap(0), @@ -463,12 +467,7 @@ bool QTranslator::load(const QString & filename, const QString & directory, const QString & suffix) { Q_D(QTranslator); - - std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); - if (locker.owns_lock()) - QCoreApplication::postEvent(QCoreApplication::instance(), - new QEvent(QEvent::LanguageChange)); - + QMutexLocker locker(&d->lock); d->clear(); QString prefix; @@ -744,12 +743,7 @@ bool QTranslator::load(const QLocale & locale, const QString & suffix) { Q_D(QTranslator); - - std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); - if (locker.owns_lock()) - QCoreApplication::postEvent(QCoreApplication::instance(), - new QEvent(QEvent::LanguageChange)); - + QMutexLocker locker(&d->lock); d->clear(); return d->load_translation(locale, filename, prefix, directory, suffix); } @@ -769,12 +763,7 @@ bool QTranslator::load(const QLocale & locale, bool QTranslator::load(const uchar *data, int len, const QString &directory) { Q_D(QTranslator); - - std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); - if (locker.owns_lock()) - QCoreApplication::postEvent(QCoreApplication::instance(), - new QEvent(QEvent::LanguageChange)); - + QMutexLocker locker(&d->lock); d->clear(); if (!data || len < MagicLength || memcmp(data, magic, MagicLength)) @@ -1048,6 +1037,7 @@ searchDependencies: void QTranslatorPrivate::clear() { + Q_Q(QTranslator); if (unmapPointer && unmapLength) { #if defined(QT_USE_MMAP) if (used_mmap) { @@ -1075,9 +1065,15 @@ void QTranslatorPrivate::clear() language.clear(); filePath.clear(); + + if (QCoreApplicationPrivate::isTranslatorInstalled(q)) + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::LanguageChange)); } /*! + \threadsafe + Returns the translation for the key (\a context, \a sourceText, \a disambiguation). If none is found, also tries (\a context, \a sourceText, ""). If that still fails, returns a null string. @@ -1099,7 +1095,14 @@ QString QTranslator::translate(const char *context, const char *sourceText, cons int n) const { Q_D(const QTranslator); - return d->do_translate(context, sourceText, disambiguation, n); + + // Return early to avoid a deadlock in case translate() is called + // from a code path triggered by QTranslator::load() + if (!d->lock.tryLock()) + return QString(); + QString result = d->do_translate(context, sourceText, disambiguation, n); + d->lock.unlock(); + return result; } /*! diff --git a/src/corelib/platform/windows/qt_winrtbase_p.h b/src/corelib/platform/windows/qt_winrtbase_p.h index fb7366f93de..79c2bdf6b1c 100644 --- a/src/corelib/platform/windows/qt_winrtbase_p.h +++ b/src/corelib/platform/windows/qt_winrtbase_p.h @@ -18,6 +18,7 @@ #include <QtCore/qglobal.h> #if QT_CONFIG(cpp_winrt) +# include <guiddef.h> // required by clang-cl: https://github.com/microsoft/cppwinrt/issues/1179 # include <winrt/base.h> # include <QtCore/private/qfactorycacheregistration_p.h> // Workaround for Windows SDK bug. diff --git a/src/corelib/thread/qatomicwait.cpp b/src/corelib/thread/qatomicwait.cpp index 3d70333ca7b..25ca771643b 100644 --- a/src/corelib/thread/qatomicwait.cpp +++ b/src/corelib/thread/qatomicwait.cpp @@ -4,6 +4,7 @@ #include "qatomicwait_p.h" #include <qendian.h> +#include "qfutex_p.h" #include "qwaitcondition_p.h" #include <array> @@ -96,8 +97,21 @@ struct QAtomicWaitLocks }; } // unnamed namespace +static inline void checkFutexUse() +{ +#if !defined(QATOMICWAIT_USE_FALLBACK) + if (QtFutex::futexAvailable()) { + // This will disable the code and data on systems where futexes are + // always available (currently: FreeBSD, Linux, Windows). + qFatal("Implementation should have used futex!"); + } +#endif +} + static QAtomicWaitLocks &atomicLocks() noexcept { + checkFutexUse(); + static QAtomicWaitLocks global {}; return global; } diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index bd3b269c0b7..832d87332b0 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -120,8 +120,8 @@ void QBasicMutex::destroyInternal(void *ptr) Locks the mutex. If another thread has locked the mutex then this call will block until that thread has unlocked it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. + If the mutex was already locked by the current thread, this call will + never return, causing a \e dead-lock. \sa unlock() */ @@ -140,9 +140,6 @@ void QBasicMutex::destroyInternal(void *ptr) If the lock was obtained, the mutex must be unlocked with unlock() before another thread can successfully lock it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. - \sa lock(), unlock() */ @@ -157,9 +154,6 @@ void QBasicMutex::destroyInternal(void *ptr) If the lock was obtained, the mutex must be unlocked with unlock() before another thread can successfully lock it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. - \sa lock(), unlock() */ @@ -172,9 +166,6 @@ void QBasicMutex::destroyInternal(void *ptr) If the lock was obtained, the mutex must be unlocked with unlock() before another thread can successfully lock it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. - \sa lock(), unlock() */ @@ -202,9 +193,6 @@ void QBasicMutex::destroyInternal(void *ptr) If the lock was obtained, the mutex must be unlocked with unlock() before another thread can successfully lock it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. - \sa lock(), unlock() */ @@ -222,9 +210,6 @@ void QBasicMutex::destroyInternal(void *ptr) If the lock was obtained, the mutex must be unlocked with unlock() before another thread can successfully lock it. - Calling this function multiple times on the same mutex from the - same thread will cause a \e dead-lock. - \sa lock(), unlock() */ diff --git a/src/corelib/tools/qatomicscopedvaluerollback.h b/src/corelib/tools/qatomicscopedvaluerollback.h index 8f653acba5e..929a2e2d466 100644 --- a/src/corelib/tools/qatomicscopedvaluerollback.h +++ b/src/corelib/tools/qatomicscopedvaluerollback.h @@ -11,6 +11,7 @@ #include <QtCore/qtconfigmacros.h> #include <atomic> +#include <type_traits> QT_BEGIN_NAMESPACE @@ -122,6 +123,33 @@ template <typename T> QAtomicScopedValueRollback(QBasicAtomicPointer<T> &, std::memory_order) -> QAtomicScopedValueRollback<T*>; +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T>, bool> = true> +QAtomicScopedValueRollback(std::atomic<T>&, V) + -> QAtomicScopedValueRollback<T>; +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T>, bool> = true> +QAtomicScopedValueRollback(std::atomic<T>&, V, std::memory_order) + -> QAtomicScopedValueRollback<T>; + +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T>, bool> = true> +QAtomicScopedValueRollback(QBasicAtomicInteger<T>&, V) + -> QAtomicScopedValueRollback<T>; +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T>, bool> = true> +QAtomicScopedValueRollback(QBasicAtomicInteger<T>&, V, std::memory_order) + -> QAtomicScopedValueRollback<T>; + +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T*>, bool> = true> +QAtomicScopedValueRollback(QBasicAtomicPointer<T>&, V) + -> QAtomicScopedValueRollback<T*>; +template <typename T, typename V, + std::enable_if_t<std::is_convertible_v<V, T*>, bool> = true> +QAtomicScopedValueRollback(QBasicAtomicPointer<T>&, V, std::memory_order) + -> QAtomicScopedValueRollback<T*>; + QT_END_NAMESPACE #endif // QATOMICASCOPEDVALUEROLLBACK_H diff --git a/src/corelib/tools/qfreelist_p.h b/src/corelib/tools/qfreelist_p.h index cc12fb9c8d0..ecefd1f55df 100644 --- a/src/corelib/tools/qfreelist_p.h +++ b/src/corelib/tools/qfreelist_p.h @@ -199,27 +199,27 @@ inline typename QFreeList<T, ConstantsType>::ReferenceType QFreeList<T, Constant template <typename T, typename ConstantsType> inline int QFreeList<T, ConstantsType>::next() { - int id, newid, at; - ElementType *v; + int newid; + int id = _next.loadAcquire(); do { - id = _next.loadAcquire(); - - at = id & ConstantsType::IndexMask; + int at = id & ConstantsType::IndexMask; const int block = blockfor(at); - v = _v[block].loadAcquire(); + ElementType *v = _v[block].loadAcquire(); if (!v) { - v = allocate((id & ConstantsType::IndexMask) - at, ConstantsType::Sizes[block]); - if (!_v[block].testAndSetRelease(nullptr, v)) { + ElementType* const alloced = allocate((id & ConstantsType::IndexMask) - at, + ConstantsType::Sizes[block]); + if (_v[block].testAndSetRelease(nullptr, alloced, v)) { + v = alloced; + } else { // race with another thread lost - delete[] v; - v = _v[block].loadAcquire(); + delete[] alloced; Q_ASSERT(v != nullptr); } } newid = v[at].next.loadRelaxed() | (id & ~ConstantsType::IndexMask); - } while (!_next.testAndSetRelease(id, newid)); + } while (!_next.testAndSetOrdered(id, newid, id)); // qDebug("QFreeList::next(): returning %d (_next now %d, serial %d)", // id & ConstantsType::IndexMask, // newid & ConstantsType::IndexMask, diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index cbe217db11e..3e44f5f3442 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -298,19 +298,32 @@ bool qHashEquals(const T1 &a, const T2 &b) } namespace QtPrivate { -template <typename Mixer> struct QHashCombiner : private Mixer +template <typename Mixer> struct QHashCombinerWithSeed : private Mixer { - using result_type = typename Mixer::result_type ; + using result_type = typename Mixer::result_type; + size_t seed; + constexpr QHashCombinerWithSeed(result_type s) noexcept : seed(s) {} + + template <typename T> + constexpr result_type operator()(result_type result, const T &t) const + noexcept(noexcept(qHash(t, seed))) + { + return Mixer::operator()(result, qHash(t, seed)); + } +}; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) - // Qt 6.x didn't use to pass the seed; bootstrap has no seed +// Earlier Qt 6.x versions of qHashMulti() failed to pass the seed as the seed +// argument of qHash(), so this class exists for compatibility with user and +// inline code that relies on the old behavior. For Qt 7, we'll replace with +// the above version, except for the bootstrapped tools (which have no seed). +template <typename Mixer> struct QHashCombiner : private Mixer +{ + using result_type = typename Mixer::result_type; + static constexpr size_t seed = 0; constexpr QHashCombiner(result_type) noexcept {} Q_DECL_DEPRECATED_X("pass the seed argument") constexpr QHashCombiner() noexcept {} -#else - size_t seed; - constexpr QHashCombiner(result_type s) : seed(s) noexcept {} -#endif template <typename T> constexpr result_type operator()(result_type result, const T &t) const @@ -319,6 +332,9 @@ template <typename Mixer> struct QHashCombiner : private Mixer return Mixer::operator()(result, qHash(t, seed)); } }; +#else +template <typename Mixer> using QHashCombiner = QHashCombinerWithSeed<Mixer>; +#endif struct QHashCombineMixer { @@ -330,6 +346,7 @@ struct QHashCombineMixer } }; using QHashCombine = QHashCombiner<QHashCombineMixer>; +using QHashCombineWithSeed = QHashCombinerWithSeed<QHashCombineMixer>; struct QHashCombineCommutativeMixer : std::plus<size_t> { @@ -341,6 +358,7 @@ struct QHashCombineCommutativeMixer : std::plus<size_t> typedef size_t result_type; }; using QHashCombineCommutative = QHashCombiner<QHashCombineCommutativeMixer>; +using QHashCombineCommutativeWithSeed = QHashCombinerWithSeed<QHashCombineCommutativeMixer>; template <typename... T> using QHashMultiReturnType = decltype( diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp index c6462dfed7b..6da0c4d387b 100644 --- a/src/gui/kernel/qguivariant.cpp +++ b/src/gui/kernel/qguivariant.cpp @@ -52,14 +52,13 @@ QT_BEGIN_NAMESPACE namespace { - -// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class struct QVariantGuiHelper : QMetaTypeModuleHelper { #define QT_IMPL_METATYPEINTERFACE_GUI_TYPES(MetaTypeName, MetaTypeId, RealName) \ QT_METATYPE_INTERFACE_INIT(RealName), - const QtPrivate::QMetaTypeInterface *interfaceForType(int type) const override { + static const QtPrivate::QMetaTypeInterface *interfaceForType(int type) + { switch (type) { QT_FOR_EACH_STATIC_GUI_CLASS(QT_METATYPE_CONVERT_ID_TO_TYPE) default: return nullptr; @@ -67,7 +66,7 @@ struct QVariantGuiHelper : QMetaTypeModuleHelper } #undef QT_IMPL_METATYPEINTERFACE_GUI_TYPES - bool convert(const void *from, int fromTypeId, void *to, int toTypeId) const override + static bool convert(const void *from, int fromTypeId, void *to, int toTypeId) { Q_ASSERT(fromTypeId != toTypeId); @@ -133,14 +132,14 @@ struct QVariantGuiHelper : QMetaTypeModuleHelper return false; } }; - -static constexpr QVariantGuiHelper qVariantGuiHelper; - } // namespace used to hide QVariant handler void qRegisterGuiVariant() { - qMetaTypeGuiHelper = &qVariantGuiHelper; + qMetaTypeGuiHelper = QMetaTypeModuleHelper{ + &QVariantGuiHelper::interfaceForType, + &QVariantGuiHelper::convert, + }; } Q_CONSTRUCTOR_FUNCTION(qRegisterGuiVariant) diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp index afe6de35305..caf4cb7fe5b 100644 --- a/src/gui/kernel/qscreen.cpp +++ b/src/gui/kernel/qscreen.cpp @@ -34,6 +34,12 @@ QT_BEGIN_NAMESPACE \note Both physical and logical DPI are expressed in device-independent dots. Multiply by QScreen::devicePixelRatio() to get device-dependent density. + To obtain a QScreen object, use QGuiApplication::primaryScreen() for the + primary screen, or QGuiApplication::screens() to get a list of all screens. + + \sa QGuiApplication::primaryScreen() + \sa QGuiApplication::screens() + \inmodule QtGui */ diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp index ca62798ddfd..dfcea928fd8 100644 --- a/src/gui/kernel/qtestsupport_gui.cpp +++ b/src/gui/kernel/qtestsupport_gui.cpp @@ -24,8 +24,19 @@ QT_BEGIN_NAMESPACE /*! \since 5.0 + \overload - Returns \c true, if \a window is active within \a timeout milliseconds. Otherwise returns \c false. + The \a timeout is in milliseconds. +*/ +bool QTest::qWaitForWindowActive(QWindow *window, int timeout) +{ + return qWaitForWindowActive(window, QDeadlineTimer{timeout, Qt::TimerType::PreciseTimer}); +} + +/*! + \since 6.10 + + Returns \c true, if \a window is active within \a timeout. Otherwise returns \c false. The method is useful in tests that call QWindow::show() and rely on the window actually being active (i.e. being visible and having focus) before proceeding. @@ -38,7 +49,7 @@ QT_BEGIN_NAMESPACE \sa qWaitForWindowExposed(), qWaitForWindowFocused(), QWindow::isActive() */ -Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout) +bool QTest::qWaitForWindowActive(QWindow *window, QDeadlineTimer timeout) { if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) { qWarning() << "qWaitForWindowActive was called on a platform that doesn't support window" @@ -52,6 +63,17 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout) } /*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowActive(QWindow *window) +{ + return qWaitForWindowActive(window, Internal::defaultTryTimeout); +} + +/*! \since 6.7 Returns \c true, if \a window is the focus window within \a timeout. Otherwise returns \c false. @@ -73,9 +95,31 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowFocused(QWindow *window, QDeadlineTimer t } /*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowFocused(QWindow *window) +{ + return qWaitForWindowFocused(window, Internal::defaultTryTimeout); +} + +/*! \since 5.0 + \overload + + The \a timeout is in milliseconds. +*/ +bool QTest::qWaitForWindowExposed(QWindow *window, int timeout) +{ + return qWaitForWindowExposed(window, std::chrono::milliseconds(timeout)); +} - Returns \c true, if \a window is exposed within \a timeout milliseconds. Otherwise returns \c false. +/*! + \since 6.10 + + Returns \c true, if \a window is exposed within \a timeout. Otherwise returns \c false. The method is useful in tests that call QWindow::show() and rely on the window actually being being visible before proceeding. @@ -86,11 +130,22 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowFocused(QWindow *window, QDeadlineTimer t \sa qWaitForWindowActive(), QWindow::isExposed() */ -Q_GUI_EXPORT bool QTest::qWaitForWindowExposed(QWindow *window, int timeout) +bool QTest::qWaitForWindowExposed(QWindow *window, QDeadlineTimer timeout) { return QTest::qWaitFor([&]() { return window->isExposed(); }, timeout); } +/*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowExposed(QWindow *window) +{ + return qWaitForWindowExposed(window, Internal::defaultTryTimeout); +} + namespace QTest { QTouchEventSequence::~QTouchEventSequence() diff --git a/src/gui/kernel/qtestsupport_gui.h b/src/gui/kernel/qtestsupport_gui.h index 951d9df1c7c..39278c2f034 100644 --- a/src/gui/kernel/qtestsupport_gui.h +++ b/src/gui/kernel/qtestsupport_gui.h @@ -23,12 +23,16 @@ Q_GUI_EXPORT bool qt_handleTouchEventv2(QWindow *w, const QPointingDevice *devic namespace QTest { -[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, - int timeout = static_cast<int>(Internal::defaultTryTimeout.count())); -[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window, - QDeadlineTimer timeout = Internal::defaultTryTimeout); -[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, - int timeout = static_cast<int>(Internal::defaultTryTimeout.count())); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, QDeadlineTimer timeout); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window); + +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window); + +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, QDeadlineTimer timeout); +[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window); Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType = QInputDevice::DeviceType::TouchScreen, QInputDevice::Capabilities caps = QInputDevice::Capability::Position); diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 66f1cf2a985..148d40b8361 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -784,7 +784,7 @@ bool QTransform::operator==(const QTransform &o) const */ size_t qHash(const QTransform &key, size_t seed) noexcept { - QtPrivate::QHashCombine hash(seed); + QtPrivate::QHashCombineWithSeed hash(seed); seed = hash(seed, key.m11()); seed = hash(seed, key.m12()); seed = hash(seed, key.m21()); diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index b9312f6b304..7a3d4974e53 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -6543,7 +6543,7 @@ bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind size_t qHash(const QRhiShaderResourceBinding &b, size_t seed) noexcept { const QRhiShaderResourceBinding::Data *d = QRhiImplementation::shaderResourceBindingData(b); - QtPrivate::QHashCombine hash(seed); + QtPrivate::QHashCombineWithSeed hash(seed); seed = hash(seed, d->binding); seed = hash(seed, d->stage); seed = hash(seed, d->type); diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index 5e967c62e40..1234d01b6ec 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -812,7 +812,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) noexcept size_t qHash(const QShader &s, size_t seed) noexcept { if (s.d) { - QtPrivate::QHashCombine hash(seed); + QtPrivate::QHashCombineWithSeed hash(seed); seed = hash(seed, s.stage()); if (!s.d->shaders.isEmpty()) { seed = hash(seed, s.d->shaders.firstKey()); diff --git a/src/gui/text/qfontdatabase_p.h b/src/gui/text/qfontdatabase_p.h index 4cd6996aab8..27fb34aecdd 100644 --- a/src/gui/text/qfontdatabase_p.h +++ b/src/gui/text/qfontdatabase_p.h @@ -51,7 +51,7 @@ inline bool operator!=(const QtFontFallbacksCacheKey &lhs, const QtFontFallbacks inline size_t qHash(const QtFontFallbacksCacheKey &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash(seed); + QtPrivate::QHashCombineWithSeed hash(seed); seed = hash(seed, key.family); seed = hash(seed, int(key.style)); seed = hash(seed, int(key.styleHint)); diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 6949eec9560..b78d2829704 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -822,6 +822,12 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const recvResult = getsockopt(socketDescriptor, SOL_SOCKET, SO_NREAD, &value, &valuelen); if (recvResult != -1) recvResult = value; +#elif defined(Q_OS_VXWORKS) + // VxWorks: use ioctl(FIONREAD) to query the number of bytes available + int available = 0; + int ioctlResult = ::ioctl(socketDescriptor, FIONREAD, &available); + if (ioctlResult != -1) + recvResult = available; #else // We need to grow the buffer to fit the entire datagram. // We start at 1500 bytes (the MTU for Ethernet V2), which should catch diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp index 9f19e649f85..59265daa127 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp @@ -10,6 +10,7 @@ #include <private/qeglfskmsintegration_p.h> #include <QtCore/QLoggingCategory> +#include <QtCore/qtimer.h> #include <QtCore/private/qcore_unix_p.h> #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 38b3bdadd35..3fe837a2c03 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1219,6 +1219,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow(). if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) return false; + [[fallthrough]]; case QtWindows::FocusOutEvent: handleFocusEvent(et, platformWindow); return true; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 81a3f1d37b7..a4bb59cf42c 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1191,6 +1191,11 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry, bool QWindowsGeometryHint::handleCalculateSize(const QWindow *window, const QMargins &customMargins, const MSG &msg, LRESULT *result) { + // Prevent adding any border for frameless window + if (msg.wParam && window->flags() & Qt::FramelessWindowHint) { + *result = 0; + return true; + } // Return 0 to remove the window's border const bool clientAreaExpanded = window->flags() & Qt::ExpandedClientAreaHint; if (msg.wParam && clientAreaExpanded) { diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 3f40dea5eb9..e2e4fce357e 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -1662,128 +1662,123 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op } case CE_ItemViewItem: { if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { - if (qobject_cast<const QAbstractItemView *>(widget)) { - QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); - QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); - QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); - // draw the background - proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget); + // draw the background + proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget); - const QRect &rect = vopt->rect; - const bool isRtl = option->direction == Qt::RightToLeft; - bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || - vopt->viewItemPosition == QStyleOptionViewItem::Invalid; - bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; - bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End; + const QRect &rect = vopt->rect; + const bool isRtl = option->direction == Qt::RightToLeft; + bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || + vopt->viewItemPosition == QStyleOptionViewItem::Invalid; + bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; + bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End; - // the tree decoration already painted the left side of the rounded rect - if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) && - vopt->showDecorationSelected) { - isFirst = false; - if (onlyOne) { - onlyOne = false; - isLast = true; - } + // the tree decoration already painted the left side of the rounded rect + if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) && + vopt->showDecorationSelected) { + isFirst = false; + if (onlyOne) { + onlyOne = false; + isLast = true; } + } - if (isRtl) { - if (isFirst) { - isFirst = false; - isLast = true; - } else if (isLast) { - isFirst = true; - isLast = false; - } + if (isRtl) { + if (isFirst) { + isFirst = false; + isLast = true; + } else if (isLast) { + isFirst = true; + isLast = false; } - const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver); - if (highlightCurrent) { - const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - if (highContrastTheme) - painter->setBrush(vopt->palette.highlight()); - else - painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); - QWidget *editorWidget = view ? view->indexWidget(view->currentIndex()) : nullptr; - if (editorWidget) { - QPalette pal = editorWidget->palette(); - QColor editorBgColor = vopt->backgroundBrush == Qt::NoBrush ? vopt->palette.color(widget->backgroundRole()) : vopt->backgroundBrush.color(); - editorBgColor.setAlpha(255); - pal.setColor(editorWidget->backgroundRole(), editorBgColor); - editorWidget->setPalette(pal); - } - } else { - painter->setBrush(vopt->backgroundBrush); + } + const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver); + if (highlightCurrent) { + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); + if (highContrastTheme) + painter->setBrush(vopt->palette.highlight()); + else + painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + QWidget *editorWidget = view ? view->indexWidget(view->currentIndex()) : nullptr; + if (editorWidget) { + QPalette pal = editorWidget->palette(); + QColor editorBgColor = vopt->backgroundBrush == Qt::NoBrush ? vopt->palette.color(widget->backgroundRole()) : vopt->backgroundBrush.color(); + editorBgColor.setAlpha(255); + pal.setColor(editorWidget->backgroundRole(), editorBgColor); + editorWidget->setPalette(pal); } - painter->setPen(Qt::NoPen); + } else { + painter->setBrush(vopt->backgroundBrush); + } + painter->setPen(Qt::NoPen); - if (onlyOne) { - painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)), - secondLevelRoundingRadius, secondLevelRoundingRadius); - } else if (isFirst) { - painter->save(); - painter->setClipRect(rect); - painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)), - secondLevelRoundingRadius, secondLevelRoundingRadius); - painter->restore(); - } else if (isLast) { - painter->save(); - painter->setClipRect(rect); - painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)), - secondLevelRoundingRadius, secondLevelRoundingRadius); - painter->restore(); - } else { - painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2))); - } + if (onlyOne) { + painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)), + secondLevelRoundingRadius, secondLevelRoundingRadius); + } else if (isFirst) { + painter->save(); + painter->setClipRect(rect); + painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)), + secondLevelRoundingRadius, secondLevelRoundingRadius); + painter->restore(); + } else if (isLast) { + painter->save(); + painter->setClipRect(rect); + painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)), + secondLevelRoundingRadius, secondLevelRoundingRadius); + painter->restore(); + } else { + painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2))); + } - // draw the check mark - if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { - QStyleOptionViewItem option(*vopt); - option.rect = checkRect; - option.state = option.state & ~QStyle::State_HasFocus; + // draw the check mark + if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { + QStyleOptionViewItem option(*vopt); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; - switch (vopt->checkState) { - case Qt::Unchecked: - option.state |= QStyle::State_Off; - break; - case Qt::PartiallyChecked: - option.state |= QStyle::State_NoChange; - break; - case Qt::Checked: - option.state |= QStyle::State_On; - break; - } - proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget); + switch (vopt->checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + option.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + option.state |= QStyle::State_On; + break; } + proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget); + } - // draw the icon - QIcon::Mode mode = QIcon::Normal; - if (!(vopt->state & QStyle::State_Enabled)) - mode = QIcon::Disabled; - else if (vopt->state & QStyle::State_Selected) - mode = QIcon::Selected; - QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; - vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); - - painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() - : vopt->palette.text().color()); - d->viewItemDrawText(painter, vopt, textRect); - - // paint a vertical marker for QListView - if (vopt->state & State_Selected) { - if (const QListView *lv = qobject_cast<const QListView *>(widget); - lv && lv->viewMode() != QListView::IconMode && !highContrastTheme) { - painter->setPen(vopt->palette.accent().color()); - const auto xPos = isRtl ? rect.right() - 1 : rect.left(); - const QLineF lines[2] = { - QLineF(xPos, rect.y() + 2, xPos, rect.y() + rect.height() - 2), - QLineF(xPos + 1, rect.y() + 2, xPos + 1, rect.y() + rect.height() - 2), - }; - painter->drawLines(lines, 2); - } + // draw the icon + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (vopt->state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + + painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() + : vopt->palette.text().color()); + d->viewItemDrawText(painter, vopt, textRect); + + // paint a vertical marker for QListView + if (vopt->state & State_Selected) { + if (const QListView *lv = qobject_cast<const QListView *>(widget); + lv && lv->viewMode() != QListView::IconMode && !highContrastTheme) { + painter->setPen(vopt->palette.accent().color()); + const auto xPos = isRtl ? rect.right() - 1 : rect.left(); + const QLineF lines[2] = { + QLineF(xPos, rect.y() + 2, xPos, rect.y() + rect.height() - 2), + QLineF(xPos + 1, rect.y() + 2, xPos + 1, rect.y() + rect.height() - 2), + }; + painter->drawLines(lines, 2); } - } else { - QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); - d->viewItemDrawText(painter, vopt, textRect); } } break; @@ -2217,8 +2212,9 @@ void QWindows11Style::polish(QWidget* widget) pal.setColor(scrollarea->viewport()->backgroundRole(), Qt::transparent); scrollarea->viewport()->setPalette(pal); scrollarea->viewport()->setProperty("_q_original_background_palette", originalPalette); - if (qobject_cast<QTableView *>(widget)) - widget->setAttribute(Qt::WA_Hover, true); + // QTreeView & QListView are already set in the base windowsvista style + if (auto table = qobject_cast<QTableView *>(widget)) + table->viewport()->setAttribute(Qt::WA_Hover, true); } } diff --git a/src/sql/models/qsqlquerymodel.cpp b/src/sql/models/qsqlquerymodel.cpp index 6f4611d9139..d8e10343854 100644 --- a/src/sql/models/qsqlquerymodel.cpp +++ b/src/sql/models/qsqlquerymodel.cpp @@ -561,9 +561,9 @@ QSqlError QSqlQueryModel::lastError() const \sa lastError() */ -void QSqlQueryModel::setLastError(const QSqlError &error) const +void QSqlQueryModel::setLastError(const QSqlError &error) { - Q_D(const QSqlQueryModel); + Q_D(QSqlQueryModel); d->error = error; } diff --git a/src/sql/models/qsqlquerymodel.h b/src/sql/models/qsqlquerymodel.h index 24877118865..72b9b053f03 100644 --- a/src/sql/models/qsqlquerymodel.h +++ b/src/sql/models/qsqlquerymodel.h @@ -79,7 +79,7 @@ protected: virtual void queryChange(); virtual QModelIndex indexInQuery(const QModelIndex &item) const; - void setLastError(const QSqlError &error) const; + void setLastError(const QSqlError &error); QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent = nullptr); }; diff --git a/src/testlib/3rdparty/valgrind/qt_attribution.json b/src/testlib/3rdparty/valgrind/qt_attribution.json index 3146fd82c57..262bc8380b4 100644 --- a/src/testlib/3rdparty/valgrind/qt_attribution.json +++ b/src/testlib/3rdparty/valgrind/qt_attribution.json @@ -11,7 +11,7 @@ "Description": "An instrumentation framework for building dynamic analysis tools.", "Homepage": "http://valgrind.org/", - "Version": "3.24.0", + "Version": "3.25.1", "PURL": "pkg:generic/valgrind@$<VERSION>?download_url=https://valgrind.org/", "License": "BSD 4-clause \"Original\" or \"Old\" License", "LicenseId": "BSD-4-Clause", diff --git a/src/testlib/3rdparty/valgrind/valgrind_p.h b/src/testlib/3rdparty/valgrind/valgrind_p.h index 77ca3ad8692..ab49937ccd4 100644 --- a/src/testlib/3rdparty/valgrind/valgrind_p.h +++ b/src/testlib/3rdparty/valgrind/valgrind_p.h @@ -89,7 +89,7 @@ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) */ #define __VALGRIND_MAJOR__ 3 -#define __VALGRIND_MINOR__ 24 +#define __VALGRIND_MINOR__ 25 #include <stdarg.h> @@ -126,6 +126,7 @@ #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_nanomips_linux +#undef PLAT_riscv64_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris @@ -172,6 +173,8 @@ # define PLAT_mips32_linux 1 #elif defined(__linux__) && defined(__nanomips__) # define PLAT_nanomips_linux 1 +#elif defined(__linux__) && defined(__riscv) && (__riscv_xlen == 64) +# define PLAT_riscv64_linux 1 #elif defined(__sun) && defined(__i386__) # define PLAT_x86_solaris 1 #elif defined(__sun) && defined(__x86_64__) @@ -1129,6 +1132,87 @@ typedef } while (0) #endif + +/* ----------------------- riscv64-linux ------------------------ */ + +#if defined(PLAT_riscv64_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + ".option push\n\t" \ + ".option norvc\n\t" \ + "srli zero, zero, 3\n\t" \ + "srli zero, zero, 13\n\t" \ + "srli zero, zero, 51\n\t" \ + "srli zero, zero, 61\n\t" + +#define __SPECIAL_INSTRUCTION_POSTAMBLE \ + ".option pop\n\t" \ + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile("mv a3, %1\n\t" /*default*/ \ + "mv a4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* a3 = client_request ( a4 ) */ \ + "or a0, a0, a0\n\t" \ + __SPECIAL_INSTRUCTION_POSTAMBLE \ + "mv %0, a3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" ((unsigned long int)(_zzq_default)), \ + "r" (&_zzq_args[0]) \ + : "memory", "a3", "a4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* a3 = guest_NRADDR */ \ + "or a1, a1, a1\n\t" \ + __SPECIAL_INSTRUCTION_POSTAMBLE \ + "mv %0, a3" \ + : "=r" (__addr) \ + : \ + : "memory", "a3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir t0 */ \ + "or a2, a2, a2\n\t" \ + __SPECIAL_INSTRUCTION_POSTAMBLE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or a3, a3, a3\n\t" \ + __SPECIAL_INSTRUCTION_POSTAMBLE \ + : : : "memory" \ + ); \ + } while (0) + +#endif /* PLAT_riscv64_linux */ + /* Insert assembly code for other platforms here... */ #endif /* NVALGRIND */ @@ -6606,6 +6690,456 @@ typedef #endif /* PLAT_mips64_linux */ +/* ----------------------- riscv64-linux ----------------------- */ + +#if defined(PLAT_riscv64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "ra", \ + "t0", "t1", "t2", "t3", "t4", "t5", "t6", \ + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", \ + "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", \ + "ft8", "ft9", "ft10", "ft11", \ + "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7" + +/* s11 is callee-saved, so we can use it to save and restore sp around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mv s11, sp\n\t" \ + "andi sp, sp, 0xfffffffffffffff0\n\t" +#define VALGRIND_RESTORE_STACK \ + "mv sp, s11\n\t" + +/* These CALL_FN_ macros assume that on riscv64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld a7, 64(%1) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "addi sp, sp, -16 \n\t" \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld a7, 64(%1) \n\t" \ + "ld t0, 72(%1) \n\t" \ + "sd t0, 0(sp) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "addi sp, sp, -16 \n\t" \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld a7, 64(%1) \n\t" \ + "ld t0, 72(%1) \n\t" \ + "sd t0, 0(sp) \n\t" \ + "ld t0, 80(%1) \n\t" \ + "sd t0, 8(sp) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "addi sp, sp, -32 \n\t" \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld a7, 64(%1) \n\t" \ + "ld t0, 72(%1) \n\t" \ + "sd t0, 0(sp) \n\t" \ + "ld t0, 80(%1) \n\t" \ + "sd t0, 8(sp) \n\t" \ + "ld t0, 88(%1) \n\t" \ + "sd t0, 16(sp) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "addi sp, sp, -32 \n\t" \ + "ld a0, 8(%1) \n\t" \ + "ld a1, 16(%1) \n\t" \ + "ld a2, 24(%1) \n\t" \ + "ld a3, 32(%1) \n\t" \ + "ld a4, 40(%1) \n\t" \ + "ld a5, 48(%1) \n\t" \ + "ld a6, 56(%1) \n\t" \ + "ld a7, 64(%1) \n\t" \ + "ld t0, 72(%1) \n\t" \ + "sd t0, 0(sp) \n\t" \ + "ld t0, 80(%1) \n\t" \ + "sd t0, 8(sp) \n\t" \ + "ld t0, 88(%1) \n\t" \ + "sd t0, 16(sp) \n\t" \ + "ld t0, 96(%1) \n\t" \ + "sd t0, 24(sp) \n\t" \ + "ld t0, 0(%1) \n\t" /* target->t0 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_T0 \ + VALGRIND_RESTORE_STACK \ + "mv %0, a0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS, "s11" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_riscv64_linux */ + /* ------------------------------------------------------------------ */ /* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ /* */ @@ -7162,6 +7696,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_nanomips_linux +#undef PLAT_riscv64_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index e93c3038fca..a27c52f5a99 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -1475,10 +1475,23 @@ bool readInputFile(Options *options) for (const QJsonValue &value : permissions) { if (value.isObject()) { QJsonObject permissionObj = value.toObject(); - QString name = permissionObj.value("name"_L1).toString(); + QString name; QString extras; - if (permissionObj.contains("extras"_L1)) - extras = permissionObj.value("extras"_L1).toString().trimmed(); + for (auto it = permissionObj.begin(); it != permissionObj.end(); ++it) { + if (it.key() == "name"_L1) { + name = it.value().toString(); + } else { + extras.append(" android:"_L1) + .append(it.key()) + .append("=\""_L1) + .append(it.value().toString()) + .append("\""_L1); + } + } + if (name.isEmpty()) { + fprintf(stderr, "Missing permission 'name' in permission specification"); + return false; + } options->applicationPermissions.insert(name, extras); } } @@ -1580,8 +1593,11 @@ bool copyAndroidTemplate(const Options &options) if (options.verbose) fprintf(stdout, "Copying Android package template.\n"); - if (!copyGradleTemplate(options)) - return false; + if (!options.auxMode) { + // Gradle is not configured and is not running in aux mode + if (!copyGradleTemplate(options)) + return false; + } if (!copyAndroidTemplate(options, "/src/android/templates"_L1)) return false; diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index eb4c84fbd75..564a332c091 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -40,6 +40,7 @@ struct Options QString buildPath; QString manifestPath; QString adbCommand{"adb"_L1}; + QString bundletoolPath; QString serial; QString makeCommand; QString package; @@ -48,7 +49,7 @@ struct Options QString stdoutFileName; QHash<QString, QString> outFiles; QStringList amStarttestArgs; - QString apkPath; + QString packagePath; QString ndkStackPath; QList<QStringList> preTestRunAdbCommands; bool showLogcatOutput = false; @@ -114,6 +115,24 @@ static bool execAdbCommand(const QStringList &args, QByteArray *output = nullptr return execCommand(g_options.adbCommand, argsWithSerial, output, verbose); } +static bool execBundletoolCommand(const QStringList &args, QByteArray *output = nullptr, + bool verbose = true) +{ + QString java("java"_L1); + QStringList argsFull = QStringList() << "-jar"_L1 << g_options.bundletoolPath << args; + return execCommand(java, argsFull, output, verbose); +} + +static void setPackagePath(const QString &path) +{ + if (!g_options.packagePath.isEmpty()) { + qCritical("Both --aab and --apk options provided. This is not supported."); + g_options.helpRequested = true; + return; + } + g_options.packagePath = path; +} + static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = true) { auto args = QProcess::splitCommand(command); @@ -133,6 +152,11 @@ static bool parseOptions() g_options.helpRequested = true; else g_options.adbCommand = arguments.at(++i); + } else if (argument.compare("--bundletool"_L1, Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.bundletoolPath = arguments.at(++i); } else if (argument.compare("--path"_L1, Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) g_options.helpRequested = true; @@ -152,7 +176,12 @@ static bool parseOptions() if (i + 1 == arguments.size()) g_options.helpRequested = true; else - g_options.apkPath = arguments.at(++i); + setPackagePath(arguments.at(++i)); + } else if (argument.compare("--aab"_L1, Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + setPackagePath(arguments.at(++i)); } else if (argument.compare("--activity"_L1, Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) g_options.helpRequested = true; @@ -200,7 +229,7 @@ static bool parseOptions() for (;i < arguments.size(); ++i) g_options.testArgsList << arguments.at(i); - if (g_options.helpRequested || g_options.buildPath.isEmpty() || g_options.apkPath.isEmpty()) + if (g_options.helpRequested || g_options.buildPath.isEmpty() || g_options.packagePath.isEmpty()) return false; g_options.serial = qEnvironmentVariable("ANDROID_SERIAL"); @@ -222,52 +251,57 @@ static bool parseOptions() static void printHelp() { - qWarning( "Syntax: %s <options> -- [TESTARGS] \n" - "\n" - " Runs a Qt for Android test on an emulator or a device. Specify a device\n" - " using the environment variables ANDROID_SERIAL or ANDROID_DEVICE_SERIAL.\n" - " Returns the number of failed tests, -1 on test runner deployment related\n" - " failures or zero on success." - "\n" - " Mandatory arguments:\n" - " --path <path>: The path where androiddeployqt builds the android package.\n" - "\n" - " --make <make cmd>: make command to create an APK, for example:\n" - " \"cmake --build <build-dir> --target <target>_make_apk\".\n" - "\n" - " --apk <apk path>: The test apk path. The apk has to exist already, if it\n" - " does not exist the make command must be provided for building the apk.\n" - "\n" - " Optional arguments:\n" - " --adb <adb cmd>: The Android ADB command. If missing the one from\n" - " $PATH will be used.\n" - "\n" - " --activity <acitvity>: The Activity to run. If missing the first\n" - " activity from AndroidManifest.qml file will be used.\n" - "\n" - " --timeout <seconds>: Timeout to run the test. Default is 10 minutes.\n" - "\n" - " --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\n" - "\n" - " --show-logcat: Print Logcat output to stdout. If an ANR occurs during\n" - " the test run, logs from the system_server process are included.\n" - " This argument is implied if a test crashes.\n" - "\n" - " --ndk-stack: Path to ndk-stack tool that symbolizes crash stacktraces.\n" - " By default, ANDROID_NDK_ROOT env var is used to deduce the tool path.\n" - "\n" - " -- Arguments that will be passed to the test application.\n" - "\n" - " --verbose: Prints out information during processing.\n" - "\n" - " --pre-test-adb-command <command>: call the adb <command> after\n" - " installation and before the test run.\n" - "\n" - " --manifest <path>: Custom path to the AndroidManifest.xml.\n" - "\n" - " --help: Displays this information.\n", - qPrintable(QCoreApplication::arguments().at(0)) - ); + qWarning("Syntax: %s <options> -- [TESTARGS] \n" + "\n" + " Runs a Qt for Android test on an emulator or a device. Specify a device\n" + " using the environment variables ANDROID_SERIAL or ANDROID_DEVICE_SERIAL.\n" + " Returns the number of failed tests, -1 on test runner deployment related\n" + " failures or zero on success." + "\n" + " Mandatory arguments:\n" + " --path <path>: The path where androiddeployqt builds the android package.\n" + "\n" + " --make <make cmd>: make command to create an APK, for example:\n" + " \"cmake --build <build-dir> --target <target>_make_apk\".\n" + "\n" + " --apk <apk path>: The test apk path. The apk has to exist already, if it\n" + " does not exist the make command must be provided for building the apk.\n" + "\n" + " --aab <aab path>: The test aab path. The aab has to exist already, if it\n" + " does not exist the make command must be provided for building the aab.\n" + "\n" + " Optional arguments:\n" + " --adb <adb cmd>: The Android ADB command. If missing the one from\n" + " $PATH will be used.\n" + "\n" + " --activity <acitvity>: The Activity to run. If missing the first\n" + " activity from AndroidManifest.qml file will be used.\n" + "\n" + " --timeout <seconds>: Timeout to run the test. Default is 10 minutes.\n" + "\n" + " --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\n" + "\n" + " --show-logcat: Print Logcat output to stdout. If an ANR occurs during\n" + " the test run, logs from the system_server process are included.\n" + " This argument is implied if a test crashes.\n" + "\n" + " --ndk-stack: Path to ndk-stack tool that symbolizes crash stacktraces.\n" + " By default, ANDROID_NDK_ROOT env var is used to deduce the tool path.\n" + "\n" + " -- Arguments that will be passed to the test application.\n" + "\n" + " --verbose: Prints out information during processing.\n" + "\n" + " --pre-test-adb-command <command>: call the adb <command> after\n" + " installation and before the test run.\n" + "\n" + " --manifest <path>: Custom path to the AndroidManifest.xml.\n" + "\n" + " --bundletool <bundletool path>: The path to Android bundletool.\n" + " See https://developer.android.com/tools/bundletool for details.\n" + "\n" + " --help: Displays this information.\n", + qPrintable(QCoreApplication::arguments().at(0))); } static QString packageNameFromAndroidManifest(const QString &androidManifestPath) @@ -850,10 +884,10 @@ int main(int argc, char *argv[]) return EXIT_ERROR; } - if (!QFile::exists(g_options.apkPath)) { + if (!QFile::exists(g_options.packagePath)) { qCritical("No apk \"%s\" found after running the make command. " "Check the provided path and the make command.", - qPrintable(g_options.apkPath)); + qPrintable(g_options.packagePath)); return EXIT_ERROR; } @@ -886,11 +920,22 @@ int main(int argc, char *argv[]) // do not install or run packages while another test is running testRunnerLock.acquire(); - const QStringList installArgs = { "install"_L1, "-r"_L1, "-g"_L1, g_options.apkPath }; - g_testInfo.isPackageInstalled.store(execAdbCommand(installArgs, nullptr)); - if (!g_testInfo.isPackageInstalled) - return EXIT_ERROR; + if (g_options.packagePath.endsWith(".apk"_L1)) { + const QStringList installArgs = { "install"_L1, "-r"_L1, "-g"_L1, g_options.packagePath }; + g_testInfo.isPackageInstalled.store(execAdbCommand(installArgs, nullptr)); + if (!g_testInfo.isPackageInstalled) + return EXIT_ERROR; + } else if (g_options.packagePath.endsWith(".aab"_L1)) { + QFileInfo aab(g_options.packagePath); + const auto apksFilePath = aab.absoluteDir().absoluteFilePath(aab.baseName() + ".apks"_L1); + if (!execBundletoolCommand({ "build-apks"_L1, "--bundle"_L1, g_options.packagePath, + "--output"_L1, apksFilePath, "--local-testing"_L1, + "--overwrite"_L1 })) + return EXIT_ERROR; + if (!execBundletoolCommand({ "install-apks"_L1, "--apks"_L1, apksFilePath })) + return EXIT_ERROR; + } // Call additional adb command if set after installation and before starting the test for (const auto &command : g_options.preTestRunAdbCommands) { QByteArray output; diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index b2fa08e406f..06912d41582 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -3671,10 +3671,8 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool executePostedLayout(); - if (noSectionMemoryUsage() && - (hasAutoResizeSections() || (useGlobalMode && globalResizeMode != QHeaderView::Fixed))) { + if (noSectionMemoryUsage() && (hasAutoResizeSections() || globalMode != QHeaderView::Fixed)) setHeaderMode(HeaderMode::FlexibleWithSectionMemoryUsage); - } if (sectionCount() == 0 ) return; diff --git a/src/widgets/kernel/qtestsupport_widgets.cpp b/src/widgets/kernel/qtestsupport_widgets.cpp index 5a7200e58aa..ce40ba8c6dd 100644 --- a/src/widgets/kernel/qtestsupport_widgets.cpp +++ b/src/widgets/kernel/qtestsupport_widgets.cpp @@ -31,6 +31,17 @@ static bool qWaitForWidgetWindow(QWidget *w, Predicate predicate, QDeadlineTimer /*! \since 5.0 + \overload + + The \a timeout is in milliseconds. +*/ +bool QTest::qWaitForWindowActive(QWidget *widget, int timeout) +{ + return qWaitForWindowActive(widget, QDeadlineTimer{timeout, Qt::TimerType::PreciseTimer}); +} + +/*! + \since 6.10 Returns \c true if \a widget is active within \a timeout milliseconds. Otherwise returns \c false. @@ -45,7 +56,7 @@ static bool qWaitForWidgetWindow(QWidget *w, Predicate predicate, QDeadlineTimer \sa qWaitForWindowExposed(), QWidget::isActiveWindow() */ -Q_WIDGETS_EXPORT bool QTest::qWaitForWindowActive(QWidget *widget, int timeout) +bool QTest::qWaitForWindowActive(QWidget *widget, QDeadlineTimer timeout) { if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) { qWarning() << "qWaitForWindowActive was called on a platform that doesn't support window" @@ -57,9 +68,19 @@ Q_WIDGETS_EXPORT bool QTest::qWaitForWindowActive(QWidget *widget, int timeout) } return qWaitForWidgetWindow(widget, [&](QWindow *window) { return window->isActive(); }, - QDeadlineTimer{timeout, Qt::TimerType::PreciseTimer}); + timeout); } +/*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowActive(QWidget *widget) +{ + return qWaitForWindowActive(widget, Internal::defaultTryTimeout); +} /*! \since 6.7 @@ -86,7 +107,30 @@ Q_WIDGETS_EXPORT bool QTest::qWaitForWindowFocused(QWidget *widget, QDeadlineTim } /*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowFocused(QWidget *widget) +{ + return qWaitForWindowFocused(widget, Internal::defaultTryTimeout); +} + +/*! \since 5.0 + \overload + + The \a timeout is in milliseconds. +*/ +bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout) +{ + return qWaitForWindowExposed(widget, std::chrono::milliseconds(timeout)); +} + + +/*! + \since 6.10 Returns \c true if \a widget is exposed within \a timeout milliseconds. Otherwise returns \c false. @@ -99,11 +143,22 @@ Q_WIDGETS_EXPORT bool QTest::qWaitForWindowFocused(QWidget *widget, QDeadlineTim \sa qWaitForWindowActive(), QWidget::isVisible(), QWindow::isExposed() */ -Q_WIDGETS_EXPORT bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout) +bool QTest::qWaitForWindowExposed(QWidget *widget, QDeadlineTimer timeout) { return qWaitForWidgetWindow(widget, [&](QWindow *window) { return window->isExposed(); }, - QDeadlineTimer{timeout, Qt::TimerType::PreciseTimer}); + timeout); +} + +/*! + \since 6.10 + \overload + + This function uses the default timeout of 5 seconds. +*/ +bool QTest::qWaitForWindowExposed(QWidget *widget) +{ + return qWaitForWindowExposed(widget, Internal::defaultTryTimeout); } namespace QTest { diff --git a/src/widgets/kernel/qtestsupport_widgets.h b/src/widgets/kernel/qtestsupport_widgets.h index b49e68db651..4b5e5ff7772 100644 --- a/src/widgets/kernel/qtestsupport_widgets.h +++ b/src/widgets/kernel/qtestsupport_widgets.h @@ -14,9 +14,16 @@ class QWidget; namespace QTest { -[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowActive(QWidget *widget, int timeout = 5000); -[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowFocused(QWidget *widget, QDeadlineTimer timeout = std::chrono::seconds{5}); -[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowExposed(QWidget *widget, int timeout = 5000); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowActive(QWidget *widget, int timeout); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowActive(QWidget *widget, QDeadlineTimer timeout); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowActive(QWidget *widget); + +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowFocused(QWidget *widget, QDeadlineTimer timeout); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowFocused(QWidget *widget); + +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowExposed(QWidget *widget, int timeout); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowExposed(QWidget *widget, QDeadlineTimer timeout); +[[nodiscard]] Q_WIDGETS_EXPORT bool qWaitForWindowExposed(QWidget *widget); class Q_WIDGETS_EXPORT QTouchEventWidgetSequence : public QTouchEventSequence { diff --git a/src/widgets/kernel/qwidgetsvariant.cpp b/src/widgets/kernel/qwidgetsvariant.cpp index 17a19aa780d..c843927d366 100644 --- a/src/widgets/kernel/qwidgetsvariant.cpp +++ b/src/widgets/kernel/qwidgetsvariant.cpp @@ -11,18 +11,16 @@ QT_BEGIN_NAMESPACE namespace { - -// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class -static constexpr struct : QMetaTypeModuleHelper +struct QVariantWidgetsHelper : QMetaTypeModuleHelper { - const QtPrivate::QMetaTypeInterface *interfaceForType(int type) const override { + static const QtPrivate::QMetaTypeInterface *interfaceForType(int type) + { switch (type) { QT_FOR_EACH_STATIC_WIDGETS_CLASS(QT_METATYPE_CONVERT_ID_TO_TYPE) default: return nullptr; } } -} qVariantWidgetsHelper; - +}; #undef QT_IMPL_METATYPEINTERFACE_WIDGETS_TYPES @@ -30,7 +28,9 @@ static constexpr struct : QMetaTypeModuleHelper void qRegisterWidgetsVariant() { - qMetaTypeWidgetsHelper = &qVariantWidgetsHelper; + qMetaTypeWidgetsHelper = QMetaTypeModuleHelper{ + &QVariantWidgetsHelper::interfaceForType, + }; } Q_CONSTRUCTOR_FUNCTION(qRegisterWidgetsVariant) diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index e9cc250c517..c341a025115 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -591,7 +591,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) painter->setPen(highlightedOutline); else - painter->setPen(colorScheme() == Qt::ColorScheme::Dark ? outline.lighter(150) + painter->setPen(colorScheme() == Qt::ColorScheme::Dark ? outline.lighter(240) : outline.lighter(110)); painter->drawRect(rect); diff --git a/src/widgets/styles/qfusionstyle_p_p.h b/src/widgets/styles/qfusionstyle_p_p.h index 67abdd74dc8..62641609d59 100644 --- a/src/widgets/styles/qfusionstyle_p_p.h +++ b/src/widgets/styles/qfusionstyle_p_p.h @@ -70,10 +70,7 @@ public: } if (pal.window().style() == Qt::TexturePattern) return QColor(0, 0, 0, 160); - if (colorScheme() == Qt::ColorScheme::Dark) - return pal.window().color().lighter(140); - else - return pal.window().color().darker(140); + return pal.window().color().darker(140); } QColor highlightedOutline(const QPalette &pal) const { diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp index f5d92094f54..c1ce675d75a 100644 --- a/src/widgets/widgets/qdockarealayout.cpp +++ b/src/widgets/widgets/qdockarealayout.cpp @@ -3327,7 +3327,8 @@ int QDockAreaLayout::separatorMove(const QList<int> &separator, const QPoint &or { int delta = 0; const auto dockPosition = static_cast<QInternal::DockPosition>(separator.last()); - const bool isHorizontal = dockPosition == QInternal::LeftDock || dockPosition == QInternal::TopDock; + const bool isHorizontal = + dockPosition == QInternal::LeftDock || dockPosition == QInternal::RightDock; const bool isLeftOrTop = dockPosition == QInternal::LeftDock || dockPosition == QInternal::TopDock; const bool separatorIsWithinDock = separator.size() > 1; diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index 781f64a31ab..66c3db2e2f0 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -228,6 +228,7 @@ private slots: void getCharFF(); void remove_and_exists(); void removeOpenFile(); + void removedFileDoesntExist(); void fullDisk(); void writeLargeDataBlock_data(); void writeLargeDataBlock(); @@ -2526,6 +2527,37 @@ void tst_QFile::removeOpenFile() } } +void tst_QFile::removedFileDoesntExist() +{ +#ifdef Q_OS_WIN + QSKIP("Not relevant for Windows - can't remove still-open files"); +#endif + QFile::remove("remove_unclosed.txt"); + QFile f("remove_unclosed.txt"); + QVERIFY(!f.exists()); + bool opened = f.open(QIODevice::ReadWrite | QIODevice::Unbuffered); + QVERIFY(opened); + f.write("blah blah blah"); + + QVERIFY(f.exists()); + + // delete by path, not using f.remove() (that's tested above) + QVERIFY(QFile::remove(f.fileName())); + QVERIFY(!QFile::exists(f.fileName())); + QVERIFY(!f.exists()); + +#ifdef Q_OS_LINUX + QString procPath = u"/proc/self/fd/"_s; + if (QFile::exists(procPath)) { + // reopen the deleted file + procPath += QString::number(f.handle()); + QFile f2(procPath); + QVERIFY(f2.open(QIODevice::ReadOnly)); + QVERIFY(!f2.exists()); + } +#endif +} + void tst_QFile::fullDisk() { QFile file("/dev/full"); diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index 761f0552eec..e2190477441 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -141,6 +141,7 @@ private slots: void exists_data(); void exists(); + void deletedFileLinuxProcExists(); void absolutePath_data(); void absolutePath(); @@ -615,6 +616,34 @@ void tst_QFileInfo::exists() QVERIFY(!exists); } +void tst_QFileInfo::deletedFileLinuxProcExists() +{ +#ifdef Q_OS_LINUX + static const char msg[] = "Hello, World\n"; + QFileInfo fi("/proc/self/fd/"); + if (!fi.isDir()) + QSKIP("/proc appears not to be mounted"); + + QFile f("removed_file.txt"); + QVERIFY(f.open(QIODevice::ReadWrite | QIODevice::Unbuffered)); + f.write(msg, strlen(msg)); + + fi.setFile(fi.filePath() + QString::number(f.handle())); + QVERIFY(fi.exists()); + QCOMPARE(fi.size(), strlen(msg)); + + QFile::remove("removed_file.txt"); + fi.refresh(); + QVERIFY(fi.exists()); + + fi.refresh(); + QCOMPARE(fi.size(), strlen(msg)); // this stats, so may change flags + QVERIFY(fi.exists()); +#else + QSKIP("Linux-only test"); +#endif +} + void tst_QFileInfo::absolutePath_data() { QTest::addColumn<QString>("file"); diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 282c7f2e360..71389abc976 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -513,6 +513,7 @@ void tst_QUrl::comparison2() QCOMPARE(url1.toString(), url2.toString()); QCOMPARE(url1, url2); QCOMPARE(qHash(url1), qHash(url2)); + QCOMPARE(qHash(url1, 1), qHash(url2, 1)); } else if (ordering < 0) { QCOMPARE_LT(url1.toString(), url2.toString()); QCOMPARE_NE(url1, url2); @@ -1387,12 +1388,14 @@ void tst_QUrl::toString_constructed() QUrl parsed(asString); QCOMPARE(url, parsed); QCOMPARE(qHash(url), qHash(parsed)); + QCOMPARE(qHash(url, 1), qHash(parsed, 1)); } // clear it and ensure no memory of the previous state remains url.setUrl(QString()); QCOMPARE(url, QUrl()); QCOMPARE(qHash(url), qHash(QUrl())); + QCOMPARE(qHash(url, 1), qHash(QUrl(), 1)); } void tst_QUrl::toDisplayString_PreferLocalFile_data() @@ -4265,6 +4268,7 @@ void tst_QUrl::setComponents() QUrl recreated(toString); QCOMPARE(copy, recreated); QCOMPARE(qHash(copy), qHash(recreated)); + QCOMPARE(qHash(copy, 1), qHash(recreated, 1)); } else { QVERIFY(copy.toString().isEmpty()); } diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index b1e2e8164af..f2d281ae0a4 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -708,6 +708,8 @@ void tst_QMetaObjectBuilder::property() prop2.setEnumOrFlag(false); \ prop2.setConstant(false); \ prop2.setFinal(false); \ + prop2.setBindable(false); \ + prop2.setRequired(false); \ } while (0) #define COUNT_FLAGS() \ ((prop2.isReadable() ? 1 : 0) + \ @@ -720,7 +722,9 @@ void tst_QMetaObjectBuilder::property() (prop2.hasStdCppSet() ? 1 : 0) + \ (prop2.isEnumOrFlag() ? 1 : 0) + \ (prop2.isConstant() ? 1 : 0) + \ - (prop2.isFinal() ? 1 : 0)) + (prop2.isFinal() ? 1 : 0) + \ + (prop2.isBindable() ? 1 : 0) + \ + (prop2.isRequired() ? 1 : 0)) #define CHECK_FLAG(setFunc,isFunc) \ do { \ CLEAR_FLAGS(); \ diff --git a/tests/auto/corelib/platform/CMakeLists.txt b/tests/auto/corelib/platform/CMakeLists.txt index 3a66ec2eae6..9810947e3c6 100644 --- a/tests/auto/corelib/platform/CMakeLists.txt +++ b/tests/auto/corelib/platform/CMakeLists.txt @@ -5,7 +5,9 @@ if(ANDROID) add_subdirectory(android) add_subdirectory(android_appless) add_subdirectory(androiditemmodel) - add_subdirectory(android_legacy_packaging) + if(NOT QT_USE_ANDROID_MODERN_BUNDLE) + add_subdirectory(android_legacy_packaging) + endif() endif() if(WIN32) add_subdirectory(windows) diff --git a/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle b/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle index cbdd833e2ae..1d796d9b6b9 100644 --- a/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle +++ b/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle @@ -70,8 +70,8 @@ android { abortOnError = false } - // Do not compress Qt binary resources file aaptOptions { + // Do not compress Qt binary resources file noCompress 'rcc' } diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index 3756937c94a..bfc6074689b 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -2470,7 +2470,8 @@ void tst_QLocale::formatTimeZone() // Time definitely in Standard Time const QStringList knownCETus = { u"GMT+1"_s, // ICU - u"CET"_s // Standard abbreviation + u"CET"_s, // Standard abbreviation + u"UTC+0100"_s, // used by Emscripten }; const QString cet = enUS.toString(QDate(2013, 1, 1).startOfDay(), u"t"); QVERIFY2(knownCETus.contains(cet), cet.isEmpty() ? "[empty]" : qPrintable(cet)); @@ -2478,7 +2479,8 @@ void tst_QLocale::formatTimeZone() // Time definitely in Daylight Time const QStringList knownCESTus = { u"GMT+2"_s, // ICU - u"CEST"_s // Standard abbreviation + u"CEST"_s, // Standard abbreviation + u"UTC+0200"_s, // used by Emscripten }; const QString cest = enUS.toString(QDate(2013, 6, 1).startOfDay(), u"t"); QVERIFY2(knownCESTus.contains(cest), cest.isEmpty() ? "[empty]" : qPrintable(cest)); diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp index 89bd1d7ff61..b53362b43e9 100644 --- a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp @@ -5,6 +5,10 @@ #include <QTest> +#include <chrono> + +using namespace std::chrono_literals; + class tst_QAtomicScopedValueRollback : public QObject { Q_OBJECT @@ -15,6 +19,7 @@ private Q_SLOTS: void rollbackToPreviousCommit(); void exceptions(); void earlyExitScope(); + void mixedTypes(); private: void earlyExitScope_helper(int exitpoint, std::atomic<int> &member); }; @@ -132,6 +137,90 @@ void tst_QAtomicScopedValueRollback::earlyExitScope() } } +template <typename T> +struct Wrap { +#if __cpp_deduction_guides < 201907L // no CTAD for aggregates + Q_IMPLICIT Wrap(T &t) : t{t} {} + Q_IMPLICIT Wrap(T &&t) : t{std::move(t)} {} +#endif + T t; + Q_IMPLICIT operator T() const { return t; } +}; + +void tst_QAtomicScopedValueRollback::mixedTypes() +{ + const auto relaxed = std::memory_order_relaxed; + { + std::atomic a{10'000ms}; + { + QAtomicScopedValueRollback rb(a, 5s); + QCOMPARE(a.load(relaxed), 5s); + } + QCOMPARE(a.load(relaxed), 10s); + { + QAtomicScopedValueRollback rb(a, 5s, relaxed); + QCOMPARE(a.load(relaxed), 5s); + } + QCOMPARE(a.load(relaxed), 10s); + } + { + QBasicAtomicInteger a{10'000}; + { + QAtomicScopedValueRollback rb(a, Wrap{5'000}); + QCOMPARE(a.loadRelaxed(), 5'000); + } + QCOMPARE(a.loadRelaxed(), 10'000); + { + QAtomicScopedValueRollback rb(a, Wrap{5'000}, relaxed); + QCOMPARE(a.loadRelaxed(), 5'000); + } + QCOMPARE(a.loadRelaxed(), 10'000); + } + { + QAtomicInteger a{10'000}; + { + QAtomicScopedValueRollback rb(a, Wrap{5'000}); + QCOMPARE(a.loadRelaxed(), 5'000); + } + QCOMPARE(a.loadRelaxed(), 10'000); + { + QAtomicScopedValueRollback rb(a, Wrap{5'000}, relaxed); + QCOMPARE(a.loadRelaxed(), 5'000); + } + QCOMPARE(a.loadRelaxed(), 10'000); + } + { + int i = 10'000; + int j = 5'000; + QBasicAtomicPointer a{&i}; + { + QAtomicScopedValueRollback rb(a, Wrap{&j}); + QCOMPARE(a.loadRelaxed(), &j); + } + QCOMPARE(a.loadRelaxed(), &i); + { + QAtomicScopedValueRollback rb(a, Wrap{&j}, relaxed); + QCOMPARE(a.loadRelaxed(), &j); + } + QCOMPARE(a.loadRelaxed(), &i); + } + { + int i = 10'000; + int j = 5'000; + QAtomicPointer a{&i}; + { + QAtomicScopedValueRollback rb(a, Wrap{&j}); + QCOMPARE(a.loadRelaxed(), &j); + } + QCOMPARE(a.loadRelaxed(), &i); + { + QAtomicScopedValueRollback rb(a, Wrap{&j}, relaxed); + QCOMPARE(a.loadRelaxed(), &j); + } + QCOMPARE(a.loadRelaxed(), &i); + } +} + static void operator*=(std::atomic<int> &lhs, int rhs) { int expected = lhs.load(); diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST index a92d89c6123..4f994fb3059 100644 --- a/tests/auto/gui/kernel/qwindow/BLACKLIST +++ b/tests/auto/gui/kernel/qwindow/BLACKLIST @@ -25,6 +25,5 @@ android windows-10 windows-11 android -rhel [windowExposedAfterReparent] xcb ubuntu-24.04 # QTBUG-129023 diff --git a/tests/auto/other/android/CMakeLists.txt b/tests/auto/other/android/CMakeLists.txt index bcbf5b657d7..0eb2d00b46a 100644 --- a/tests/auto/other/android/CMakeLists.txt +++ b/tests/auto/other/android/CMakeLists.txt @@ -2,3 +2,11 @@ # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(deployment_settings) +add_subdirectory(permissions) + +if(QT_USE_TARGET_ANDROID_BUILD_DIR) + add_subdirectory(package_source_dir) + if(QT_USE_ANDROID_MODERN_BUNDLE) + add_subdirectory(dynamic_feature) + endif() +endif() diff --git a/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp b/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp index 1687ed9de92..571570e370f 100644 --- a/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp +++ b/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp @@ -83,10 +83,11 @@ void tst_android_deployment_settings::DeploymentSettings_data() << "org.qtproject.android_deployment_settings_test"; QTest::newRow("android-app-name") << "android-app-name" << "Android Deployment Settings Test"; - QTest::newRow("permissions") << "permissions" - << "[{\"name\":\"PERMISSION_WITH_ATTRIBUTES\"," - "\"extras\":\"android:minSdkVersion='32' android:maxSdkVersion='34' \"}," - "{\"name\":\"PERMISSION_WITHOUT_ATTRIBUTES\"}]"; + QTest::newRow("permissions") + << "permissions" + << "[{\"maxSdkVersion\":\"34\",\"minSdkVersion\":\"32\",\"name\":\"PERMISSION_WITH_" + "ATTRIBUTES\"},{\"name\":\"PERMISSION_WITHOUT_ATTRIBUTES\"},{\"name\":\"android." + "permission.INTERNET\"},{\"name\":\"android.permission.WRITE_EXTERNAL_STORAGE\"}]"; } void tst_android_deployment_settings::DeploymentSettings() diff --git a/tests/auto/other/android/dynamic_feature/CMakeLists.txt b/tests/auto/other/android/dynamic_feature/CMakeLists.txt new file mode 100644 index 00000000000..b12c2af4dc7 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_android_dynamic_feature LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +add_subdirectory(feature) + +qt_internal_add_test(tst_android_dynamic_feature + SOURCES + tst_android_dynamic_feature.cpp + storeloader/storeloader.h + storeloader/storeloader.cpp + INCLUDE_DIRECTORIES + storeloader + LIBRARIES + Qt6::Test + Qt6::Gui + Qt6::CorePrivate +) + +set_property(TARGET tst_android_dynamic_feature PROPERTY + QT_ANDROID_PACKAGE_NAME "org.qtproject.example.android_dynamic_feature") +set_property(TARGET tst_android_dynamic_feature APPEND PROPERTY + _qt_android_gradle_java_source_dirs "${CMAKE_CURRENT_SOURCE_DIR}/storeloader/java") + +qt6_add_android_dynamic_features(tst_android_dynamic_feature FEATURE_TARGETS + tst_android_dynamic_feature_resources) diff --git a/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt b/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt new file mode 100644 index 00000000000..bfe29cc0f5e --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) + +qt6_add_library(tst_android_dynamic_feature_resources SHARED) +qt6_add_resources(tst_android_dynamic_feature_resources "dynamic_resources" + PREFIX "/dynamic_resources" + FILES "qtlogo.png") + +target_link_libraries(tst_android_dynamic_feature_resources PRIVATE Qt6::Core) diff --git a/tests/auto/other/android/dynamic_feature/feature/qtlogo.png b/tests/auto/other/android/dynamic_feature/feature/qtlogo.png Binary files differnew file mode 100644 index 00000000000..b63f1384b11 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/feature/qtlogo.png diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java new file mode 100644 index 00000000000..5a72351c802 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java @@ -0,0 +1,175 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +package org.qtproject.example.android_dynamic_feature; + +import com.google.android.play.core.splitcompat.SplitCompat; +import com.google.android.play.core.splitinstall.SplitInstallManager; +import com.google.android.play.core.splitinstall.SplitInstallManagerFactory; +import com.google.android.play.core.splitinstall.SplitInstallRequest; +import com.google.android.play.core.splitinstall.SplitInstallHelper; +import com.google.android.play.core.splitinstall.testing.FakeSplitInstallManagerFactory; +import android.content.pm.PackageManager.NameNotFoundException; + +import java.util.Arrays; +import java.util.HashMap; +import java.io.File; +import android.util.Log; +import android.content.Context; +import android.os.Build; + +/** + * Android implementation of store loader. + */ +public class StoreLoader implements StoreLoaderListenerCallback +{ + private static final String TAG = "StoreLoader"; + private final SplitInstallManager m_splitInstallManager; + private HashMap<String, StoreLoaderListener> m_listeners = + new HashMap(); + private Context m_context; + private String m_moduleName; + + public static native void stateChangedNative(String callId, int state); + public static native void errorOccurredNative(String callId, int errorCode, + String errorMessage); + public static native void userConfirmationRequestedNative(String callId, int errorCode, + String errorMessage); + public static native void downloadProgressChangedNative(String callId, long bytes, long total); + public static native void finishedNative(String callId); + + public StoreLoader(Context context) { + m_context = context; + + SplitCompat.install(context); + + m_splitInstallManager = SplitInstallManagerFactory.create(context); + if (m_splitInstallManager == null) + Log.e(TAG,"Constructor: Did not get splitInstallManager"); + } + + /** + * Loads Feature Delivery module from a play store. + */ + public void installModuleFromStore(String moduleName, String callId) { + Log.d(TAG, "installModuleFromStore: " + moduleName + " " + callId); + m_moduleName = moduleName; + + registerListener(callId); + + SplitInstallRequest request = + SplitInstallRequest.newBuilder().addModule(m_moduleName).build(); + if (request == null) + Log.e(TAG, "Null request"); + + if (m_splitInstallManager == null) + Log.e(TAG, "Null m_splitInstallManager"); + + m_splitInstallManager.startInstall(request) + .addOnSuccessListener(sessionId -> { + Log.d(TAG, "Install start call succesfull"); + StoreLoaderListener listener = m_listeners.get(callId); + if (listener != null) { + if (!listener.isCancelled()) + listener.setSessionId(sessionId); + else + m_splitInstallManager.cancelInstall(sessionId); + } else { + Log.d(TAG, "Listener for callId '" + callId + "' is not found"); + } + }); + } + + public void onStateChanged(String callId, int state) { + stateChangedNative(callId, state); + if (state == StoreLoaderListenerCallback.LOADED || + state == StoreLoaderListenerCallback.CANCELED) { + finished(callId); + } + } + + public void onErrorOccurred(String callId, int errorCode, String errorMessage) { + onStateChanged(callId, ERROR); + + Log.e(TAG, "Error occurred " + errorCode + " " + errorMessage); + errorOccurredNative(callId, errorCode, errorMessage); + finished(callId); + } + + public void onUserConfirmationRequested(String callId, int errorCode, String errorMessage) { + Log.d(TAG, "Requires user confirmation " + errorCode); + userConfirmationRequestedNative(callId, errorCode, errorMessage); + } + + public void onDownloadProgressChanged(String callId, long bytes, long total) { + Log.d(TAG, "Downloading " + bytes + "/" + total); + downloadProgressChangedNative(callId, bytes, total); + } + + public void onLoadLibrary(String callId) { + Log.d(TAG, "Load library for the module " + m_moduleName); + + stateChangedNative(callId, StoreLoaderListenerCallback.LOADING); + // update context. + try { + m_context = m_context.createPackageContext(m_context.getPackageName(), 0); + } catch (NameNotFoundException ignored) { + Log.e(TAG, "Could not get package name"); + } + // install splitcompat for new context. + SplitCompat.install(m_context); + // try to load new library + boolean isLoaded = false; + for (String abi : Build.SUPPORTED_ABIS) { + String fullLibraryName = m_moduleName + "_" + abi; + try { + System.loadLibrary(fullLibraryName); + isLoaded = true; + break; + } catch (Exception e) { + Log.d(TAG, "Exception occurred when loading the library " + fullLibraryName + ":" + + e.getClass().getCanonicalName()); + } + } + + if (isLoaded) + onStateChanged(callId, StoreLoaderListenerCallback.LOADED); + else + onErrorOccurred(callId, -1, "Error loading library. Check logcat for details."); + + } + + public void cancelInstall(String callId) { + StoreLoaderListener listener = m_listeners.get(callId); + if (listener == null) { + Log.e(TAG, "The listener for callId " + callId + " is not found"); + return; + } + + if (listener.getSessionId() < 0) + listener.postponeCancel(); + else + m_splitInstallManager.cancelInstall(listener.getSessionId()); + } + + private void finished(String callId) { + unregisterListener(callId); + finishedNative(callId); + } + + private void registerListener(String callId) { + Log.d(TAG, "registerListener"); + StoreLoaderListener listener = new StoreLoaderListener(this, callId); + if (listener != null) { + m_listeners.put(callId, listener); + m_splitInstallManager.registerListener(listener); + } + } + + private void unregisterListener(String callId) { + Log.d(TAG, "unregisterListener"); + StoreLoaderListener listener = m_listeners.remove(callId); + if (listener != null) + m_splitInstallManager.unregisterListener(listener); + } +} diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java new file mode 100644 index 00000000000..3e83a0fa7f9 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java @@ -0,0 +1,92 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +package org.qtproject.example.android_dynamic_feature; + +import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener; +import com.google.android.play.core.splitinstall.SplitInstallSessionState; +import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus; + +import android.util.Log; + +public class StoreLoaderListener implements SplitInstallStateUpdatedListener { + + private final StoreLoaderListenerCallback m_callback; + private final String m_callId; + private int m_installSessionId = -1; + private static final String TAG = "StoreLoaderListener"; + private int m_currentStatus = SplitInstallSessionStatus.UNKNOWN; + private boolean m_postponeCancel = false; + + public StoreLoaderListener(StoreLoaderListenerCallback callback, String callId) { + m_callback = callback; + m_callId = callId; + } + + public void setSessionId(int sessionId) { m_installSessionId = sessionId; } + public int getSessionId() { return m_installSessionId; } + + public void postponeCancel() { m_postponeCancel = true; } + public boolean isCancelled() { return m_postponeCancel; } + + @Override + public void onStateUpdate(SplitInstallSessionState state) { + Log.d(TAG, + "onStateUpdate, status: " + state.status() + " session id: " + state.sessionId()); + if (state.sessionId() != m_installSessionId) { + // Not mine + return; + } + + switch (state.status()) { + case SplitInstallSessionStatus.DOWNLOADING: + Log.d(TAG, + "SplitInstallSessionState: DOWNLOADING " + state.bytesDownloaded() + "/" + + state.totalBytesToDownload()); + if (m_currentStatus != SplitInstallSessionStatus.DOWNLOADING) + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.DOWNLOADING); + + m_callback.onDownloadProgressChanged(m_callId, state.bytesDownloaded(), + state.totalBytesToDownload()); + break; + case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION: + Log.d(TAG, "SplitInstallSessionState: REQUIRES_USER_CONFIRMATION"); + m_callback.onStateChanged(m_callId, + StoreLoaderListenerCallback.REQUIRES_USER_CONFIRMATION); + + m_callback.onUserConfirmationRequested(m_callId, state.errorCode(), "" /*noop for now*/); + break; + case SplitInstallSessionStatus.INSTALLED: + Log.d(TAG, "SplitInstallSessionState: INSTALLED"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.INSTALLED); + m_callback.onLoadLibrary(m_callId); + break; + case SplitInstallSessionStatus.INSTALLING: + Log.d(TAG, "SplitInstallSessionState: INSTALLING"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.INSTALLING); + break; + case SplitInstallSessionStatus.FAILED: + Log.d(TAG, + "SplitInstallSessionState: FAILED with error code: " + state.errorCode()); + m_callback.onErrorOccurred(m_callId, state.errorCode(), "" /*noop for now*/); + break; + case SplitInstallSessionStatus.PENDING: + Log.d(TAG, "SplitInstallSessionState: PENDING"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.PENDING); + break; + case SplitInstallSessionStatus.DOWNLOADED: + Log.d(TAG, "SplitInstallSessionState: DOWNLOADED"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.DOWNLOADED); + break; + case SplitInstallSessionStatus.CANCELED: + Log.d(TAG, "SplitInstallSessionState: CANCELED"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.CANCELED); + break; + case SplitInstallSessionStatus.CANCELING: + Log.d(TAG, "SplitInstallSessionState: CANCELING"); + m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.CANCELING); + break; + } + m_currentStatus = state.status(); + } +} diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java new file mode 100644 index 00000000000..99c7327e328 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java @@ -0,0 +1,26 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +package org.qtproject.example.android_dynamic_feature; + +public interface StoreLoaderListenerCallback { + public static final int UNKNOWN = 0; // Unused but native part has and uses one. + public static final int INITIALIZED = 1; + public static final int PENDING = 2; + public static final int DOWNLOADING = 3; + public static final int DOWNLOADED = 4; + public static final int REQUIRES_USER_CONFIRMATION = 5; + public static final int CANCELING = 6; + public static final int CANCELED = 7; + public static final int INSTALLING = 8; + public static final int INSTALLED = 9; + public static final int LOADING = 10; + public static final int LOADED = 11; + public static final int ERROR = 12; + + public void onStateChanged(String callId, int state); + public void onErrorOccurred(String callId, int errorCode, String errorMessage); + public void onUserConfirmationRequested(String callId, int errorCode, String errorMessage); + public void onDownloadProgressChanged(String callId, long bytes, long total); + public void onLoadLibrary(String callId); +} diff --git a/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp new file mode 100644 index 00000000000..7abce59b79e --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp @@ -0,0 +1,227 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include "storeloader.h" + +#include <QtCore/private/qobject_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qhash.h> +#include <QtCore/qjniobject.h> +#include <QtCore/qlogging.h> +#include <QtCore/qmutex.h> +#include <QtCore/quuid.h> + +#include <jni.h> + +Q_DECLARE_JNI_CLASS(StoreLoader, + "org/qtproject/example/android_dynamic_feature/StoreLoader"); + +class StoreLoaderHandlerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(StoreLoaderHandler) +public: + void setState(StoreLoader::State state) + { + if (m_state == state) + return; + Q_Q(StoreLoaderHandler); + m_state = state; + q->stateChanged(m_state); + } + + const QString &callId() const & noexcept { return m_callId; } + +private: + StoreLoader::State m_state = StoreLoader::State::Unknown; + QString m_callId = QUuid::createUuid().toString(); +}; + +namespace QtPrivate { +QString asString(const jstring &s) +{ + return QJniObject(s).toString(); +} +} // namespace QtPrivate + +namespace { + +class StoreLoaderImpl +{ +public: + StoreLoaderImpl(); + bool registerNatives() const; + + void addHandler(StoreLoaderHandler *handler); + StoreLoaderHandler *findHandler(const jstring &callId); + void removeHandler(const jstring &callId); + + QtJniTypes::StoreLoader loader = nullptr; + +private: + QHash<QString, QPointer<StoreLoaderHandler>> m_handlers; + QMutex m_lock; +}; + +Q_GLOBAL_STATIC(StoreLoaderImpl, loaderInstance) + +void stateChangedNative(JNIEnv *, jobject, jstring callId, int state) +{ + qDebug("State changed %s.", qPrintable(callId)); + + if (auto *handler = loaderInstance->findHandler(callId)) + emit handler->stateChanged(static_cast<StoreLoader::State>(state)); +} + +void errorOccurredNative(JNIEnv *, jobject, jstring callId, int errorCode, jstring errorMessage) +{ + qDebug("Error occurred %s %d %s.", qPrintable(callId), errorCode, qPrintable(errorMessage)); + auto *handler = loaderInstance->findHandler(callId); + if (!handler) + return; + + emit handler->errorOccured(errorCode, QJniObject(errorMessage).toString()); +} + +void userConfirmationRequestedNative(JNIEnv *, jobject, jstring callId, int errorCode, + jstring errorMessage) +{ + qDebug("User confirmation requested %s %d %s.", qPrintable(callId), errorCode, + qPrintable(errorMessage)); + auto *handler = loaderInstance->findHandler(callId); + if (!handler) + return; + + emit handler->confirmationRequest(errorCode, QJniObject(errorMessage).toString()); +} + +void downloadProgressChangedNative(JNIEnv *, jobject, jstring callId, long bytes, long total) +{ + qDebug("Download progress changed %ld/%ld.", bytes, total); + auto *handler = loaderInstance->findHandler(callId); + if (!handler) + return; + + emit handler->downloadProgress(bytes, total); +} + +void finishedNative(JNIEnv *, jobject, jstring callId) +{ + auto *handler = loaderInstance->findHandler(callId); + if (!handler) + return; + + emit handler->finished(); +} + +} // namespace + +Q_DECLARE_JNI_NATIVE_METHOD(stateChangedNative) +Q_DECLARE_JNI_NATIVE_METHOD(errorOccurredNative) +Q_DECLARE_JNI_NATIVE_METHOD(userConfirmationRequestedNative) +Q_DECLARE_JNI_NATIVE_METHOD(downloadProgressChangedNative) +Q_DECLARE_JNI_NATIVE_METHOD(finishedNative) + +StoreLoaderImpl::StoreLoaderImpl() +{ + loader = QJniObject::construct<QtJniTypes::StoreLoader, QtJniTypes::Context>( + QNativeInterface::QAndroidApplication::context()); +} + +bool StoreLoaderImpl::registerNatives() const +{ + static bool result = [] { + return QtJniTypes::StoreLoader::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(stateChangedNative), + Q_JNI_NATIVE_METHOD(errorOccurredNative), + Q_JNI_NATIVE_METHOD(userConfirmationRequestedNative), + Q_JNI_NATIVE_METHOD(downloadProgressChangedNative), + Q_JNI_NATIVE_METHOD(finishedNative), + }); + }(); + + if (!result) + qCritical("Unable to register native methods."); + + return result; +} + +void StoreLoaderImpl::addHandler(StoreLoaderHandler *handler) +{ + Q_ASSERT(handler); + + QMutexLocker lock(&m_lock); + const auto &callId = handler->callId(); + Q_ASSERT_X(m_handlers.constFind(callId) != m_handlers.constEnd(), "StoreLoaderImpl::addHandler", + qPrintable(QString("Handler with callId %1 already exists.").arg(callId))); + + m_handlers[callId] = QPointer(handler); +} + + +StoreLoaderHandler *StoreLoaderImpl::findHandler(const jstring &callId) +{ + QMutexLocker lock(&m_lock); + const auto it = m_handlers.constFind(QJniObject(callId).toString()); + if (it == m_handlers.constEnd()) { + qCritical("The handler for the call %s was not found.", qPrintable(callId)); + return nullptr; + } + + if (it.value().isNull()) { + qCritical("The handler for the call %s expired.", qPrintable(callId)); + m_handlers.erase(it); + } + + return it.value().get(); +} + +void StoreLoaderImpl::removeHandler(const jstring &callId) +{ + QMutexLocker lock(&m_lock); + m_handlers.remove(QJniObject(callId).toString()); +} + +std::unique_ptr<StoreLoaderHandler> +StoreLoader::loadModule(const QString &moduleName) +{ + if (moduleName.isEmpty()) + return {}; + + if (!loaderInstance->registerNatives()) + return {}; + + if (!loaderInstance->loader.isValid()) { + qCritical("StoreLoader not constructed"); + return {}; + } + + auto handlerPtr = std::make_unique<StoreLoaderHandler>( + nullptr, StoreLoaderHandler::PrivateConstructor{}); + loaderInstance->addHandler(handlerPtr.get()); + + const auto &callId = handlerPtr->callId(); + qDebug("Loading module %s, callId: %s.", qPrintable(moduleName), qPrintable(callId)); + + loaderInstance->loader.callMethod<void>("installModuleFromStore", moduleName, callId); + return handlerPtr; +} + +StoreLoaderHandler::StoreLoaderHandler(QObject *parent, PrivateConstructor) + : QObject(*new StoreLoaderHandlerPrivate(), parent) +{ +} + +StoreLoaderHandler::~StoreLoaderHandler() = default; + +const QString &StoreLoaderHandler::callId() const & noexcept +{ + Q_D(const StoreLoaderHandler); + return d->callId(); +} + +void StoreLoaderHandler::cancel() +{ + Q_D(StoreLoaderHandler); + loaderInstance->loader.callMethod<void>("cancelInstall", d->callId()); +} + +#include "moc_storeloader.cpp" diff --git a/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h new file mode 100644 index 00000000000..be4e103207e --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h @@ -0,0 +1,65 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef STORELOADER_H +#define STORELOADER_H + +#include <QtCore/qtconfigmacros.h> +#include <QtCore/qobject.h> + +#include <memory> + +class StoreLoaderHandler; + +namespace StoreLoader { + Q_NAMESPACE +enum class State { + Unknown, + Initialized, + Pending, + Downloading, + Downloaded, + RequiresUserConfirmation, + Canceling, + Canceled, + Installing, + Installed, + Loading, + Loaded, + Error, +}; +Q_ENUM_NS(State) + +std::unique_ptr<StoreLoaderHandler> loadModule(const QString &moduleName); +}; // namespace StoreLoader + +class StoreLoaderHandlerPrivate; + +class StoreLoaderHandler : public QObject +{ + Q_OBJECT + QT_DEFINE_TAG_STRUCT(PrivateConstructor); + +public: + explicit StoreLoaderHandler(QObject *parent, PrivateConstructor); + ~StoreLoaderHandler() override; + + const QString &callId() const & noexcept; + + void cancel(); +signals: + void stateChanged(StoreLoader::State state); + void downloadProgress(qsizetype bytes, qsizetype total); + void errorOccured(int errorCode, const QString &errorString); + void confirmationRequest(int errorCode, const QString &errorString); + void finished(); + +private: + Q_DISABLE_COPY_MOVE(StoreLoaderHandler) + Q_DECLARE_PRIVATE(StoreLoaderHandler) + + friend std::unique_ptr<StoreLoaderHandler> + StoreLoader::loadModule(const QString &); +}; + +#endif // STORELOADER_H diff --git a/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp b/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp new file mode 100644 index 00000000000..739a645c5d5 --- /dev/null +++ b/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <storeloader.h> + +#include <QtTest/QTest> +#include <QtTest/QSignalSpy> + +#include <QtCore/QVariant> + +using namespace Qt::Literals::StringLiterals; + +class QtDynamicFeatureTest : public QObject +{ + Q_OBJECT +public: + QtDynamicFeatureTest(){} + +private Q_SLOTS: + void loadResourcesFeature(); +}; + +void QtDynamicFeatureTest::loadResourcesFeature() +{ + QVERIFY(!QFile::exists(":/dynamic_resources/qtlogo.png")); + + auto handler = StoreLoader::loadModule("tst_android_dynamic_feature_resources"_L1); + + QSignalSpy spy(handler.get(), &StoreLoaderHandler::finished); + + QVERIFY(spy.wait(20000)); + QVERIFY(QFile::exists(":/dynamic_resources/qtlogo.png")); +} + +QTEST_MAIN(QtDynamicFeatureTest) + +#include "tst_android_dynamic_feature.moc" diff --git a/tests/auto/other/android/package_source_dir/CMakeLists.txt b/tests/auto/other/android/package_source_dir/CMakeLists.txt new file mode 100644 index 00000000000..360aa50d87d --- /dev/null +++ b/tests/auto/other/android/package_source_dir/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_android_package_source_dir LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +# Package name is generated and written to AndroidManifest.xml +qt_internal_add_test(tst_package_source_dir_not_defined + SOURCES + tst_android_package_source_dir.cpp + DEFINES + EXPECTED_APP_NAME="tst_package_source_dir_not_defined" +) + +# Package name from user-provided AndroidManifest.xml +qt_internal_add_test(tst_package_source_dir_custom_android_manifest + SOURCES + tst_android_package_source_dir.cpp + DEFINES + EXPECTED_APP_NAME="tst_package_source_dir_custom_android_manifest_my" +) + +if(QT_USE_ANDROID_MODERN_BUNDLE) + set_target_properties(tst_package_source_dir_custom_android_manifest + PROPERTIES + QT_ANDROID_PACKAGE_SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/custom_android_manifest_bundle" + ) +else() + set_target_properties(tst_package_source_dir_custom_android_manifest + PROPERTIES + QT_ANDROID_PACKAGE_SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/custom_android_manifest" + ) +endif() + +if(QT_USE_ANDROID_MODERN_BUNDLE) + # Partial gradle template is only supported by QT_USE_ANDROID_MODERN_BUNDLE + # + # Package name from user-provided gradle.build.in + qt_internal_add_test(tst_package_source_dir_partial_template + SOURCES + tst_android_package_source_dir.cpp + DEFINES + EXPECTED_APP_NAME="tst_package_source_dir_partial_template_my" + ) + set_target_properties(tst_package_source_dir_partial_template + PROPERTIES + QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/partial_template" + ) +endif() diff --git a/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml new file mode 100644 index 00000000000..4f53295e979 --- /dev/null +++ b/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.example.tst_package_source_dir_custom_android_manifest" + android:installLocation="auto" + android:versionCode="1" + android:versionName="1.0"> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <supports-screens + android:anyDensity="true" + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" /> + <application + android:name="org.qtproject.qt.android.bindings.QtApplication" + android:hardwareAccelerated="true" + android:label="tst_package_source_dir_custom_android_manifest_my" + android:requestLegacyExternalStorage="true" + android:allowBackup="true" + android:fullBackupOnly="false"> + <activity + android:name="org.qtproject.qt.android.bindings.QtActivity" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" + android:launchMode="singleTop" + android:screenOrientation="unspecified" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <meta-data + android:name="android.app.lib_name" + android:value="tst_package_source_dir_custom_android_manifest" /> + + <meta-data + android:name="android.app.arguments" + android:value="" /> + </activity> + + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.qtprovider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/qtprovider_paths"/> + </provider> + </application> +</manifest> diff --git a/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml new file mode 100644 index 00000000000..4f53295e979 --- /dev/null +++ b/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.example.tst_package_source_dir_custom_android_manifest" + android:installLocation="auto" + android:versionCode="1" + android:versionName="1.0"> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <supports-screens + android:anyDensity="true" + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" /> + <application + android:name="org.qtproject.qt.android.bindings.QtApplication" + android:hardwareAccelerated="true" + android:label="tst_package_source_dir_custom_android_manifest_my" + android:requestLegacyExternalStorage="true" + android:allowBackup="true" + android:fullBackupOnly="false"> + <activity + android:name="org.qtproject.qt.android.bindings.QtActivity" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" + android:launchMode="singleTop" + android:screenOrientation="unspecified" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <meta-data + android:name="android.app.lib_name" + android:value="tst_package_source_dir_custom_android_manifest" /> + + <meta-data + android:name="android.app.arguments" + android:value="" /> + </activity> + + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.qtprovider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/qtprovider_paths"/> + </provider> + </application> +</manifest> diff --git a/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml new file mode 100644 index 00000000000..838d239e5f4 --- /dev/null +++ b/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.example.tst_package_source_dir_partial_template" + android:installLocation="auto" + android:versionCode="1" + android:versionName="1.0"> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <supports-screens + android:anyDensity="true" + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" /> + <application + android:name="org.qtproject.qt.android.bindings.QtApplication" + android:hardwareAccelerated="true" + android:label="${appName}" + android:requestLegacyExternalStorage="true" + android:allowBackup="true" + android:fullBackupOnly="false"> + <activity + android:name="org.qtproject.qt.android.bindings.QtActivity" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" + android:launchMode="singleTop" + android:screenOrientation="unspecified" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <meta-data + android:name="android.app.lib_name" + android:value="tst_package_source_dir_partial_template" /> + + <meta-data + android:name="android.app.arguments" + android:value="" /> + </activity> + + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.qtprovider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/qtprovider_paths"/> + </provider> + </application> +</manifest> diff --git a/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in b/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in new file mode 100644 index 00000000000..538f1ca6217 --- /dev/null +++ b/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in @@ -0,0 +1,54 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.6.0' + } +} + +apply plugin: '@GRADLE_PLUGIN_TYPE@' + +dependencies { + @GRADLE_DEPENDENCIES@ +} + +android { + namespace '@PACKAGE_NAME@' + compileSdkVersion '@ANDROID_COMPILE_SDK_VERSION@' + buildToolsVersion '@ANDROID_BUILD_TOOLS_VERSION@' + ndkVersion '@ANDROID_NDK_REVISION@' + + defaultConfig { + @DEFAULT_CONFIG_VALUES@ + manifestPlaceholders = [ appName:"tst_package_source_dir_partial_template_my" ] + } + + sourceSets { + main { +@SOURCE_SETS@ + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + aaptOptions { + // Do not compress Qt binary resources file + noCompress 'rcc' + } + + @ANDROID_DEPLOYMENT_EXTRAS@ +} diff --git a/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp b/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp new file mode 100644 index 00000000000..d74d388371f --- /dev/null +++ b/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QCoreApplication> +#include <QJniObject> +#include <QTest> +#include <QDebug> + +using namespace QNativeInterface; + +Q_DECLARE_JNI_CLASS(ApplicationInfo, "android/content/pm/ApplicationInfo") +Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager") +Q_DECLARE_JNI_CLASS(CharSequence, "java/lang/CharSequence") + +class tst_android_package_source_dir : public QObject +{ + Q_OBJECT + +private slots: + void applicationName(); + +private: +}; + +void tst_android_package_source_dir::applicationName() +{ + QJniObject appCtx = QAndroidApplication::context(); + QVERIFY(appCtx.isValid()); + + const auto appInfo = appCtx.callMethod<QtJniTypes::ApplicationInfo>("getApplicationInfo"); + QVERIFY(appInfo.isValid()); + + const auto packageManager = appCtx.callMethod<QtJniTypes::PackageManager>("getPackageManager"); + QVERIFY(packageManager.isValid()); + + const auto appNameLabel = + appInfo.callMethod<QtJniTypes::CharSequence>("loadLabel", packageManager); + QVERIFY(appNameLabel.isValid()); + + const auto appName = appNameLabel.callMethod<jstring>("toString").toString(); + + QCOMPARE_EQ(appName, QString::fromLatin1(EXPECTED_APP_NAME)); +} + +QTEST_MAIN(tst_android_package_source_dir); + +#include "tst_android_package_source_dir.moc" diff --git a/tests/auto/other/android/permissions/CMakeLists.txt b/tests/auto/other/android/permissions/CMakeLists.txt new file mode 100644 index 00000000000..0424c674e1b --- /dev/null +++ b/tests/auto/other/android/permissions/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_android_permissions LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_android_permissions + SOURCES + tst_android_permissions.cpp +) + +qt_add_android_permission(tst_android_permissions + NAME android.permission.ACCESS_COARSE_LOCATION +) + +qt_add_android_permission(tst_android_permissions + NAME android.permission.ACCESS_FINE_LOCATION + ATTRIBUTES + maxSdkVersion 31 +) diff --git a/tests/auto/other/android/permissions/tst_android_permissions.cpp b/tests/auto/other/android/permissions/tst_android_permissions.cpp new file mode 100644 index 00000000000..5d7a255d3ac --- /dev/null +++ b/tests/auto/other/android/permissions/tst_android_permissions.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QCoreApplication> +#include <QJniObject> +#include <QSet> +#include <QString> +#include <QTest> + +constexpr int GET_PERMISSIONS(0x00001000); + +using namespace QNativeInterface; +using namespace Qt::StringLiterals; + +Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager") +Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo") + +class tst_android_permissions : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void checkExpectedDefaults(); + void checkNonExisting(); + void checkNonDefaultPermissions(); + +private: + QJniArray<QString> m_requestedPermissions; +}; + +void tst_android_permissions::initTestCase() +{ + QJniObject appCtx = QAndroidApplication::context(); + QVERIFY(appCtx.isValid()); + + const auto packageName = appCtx.callMethod<QString>("getPackageName"); + const auto packageManager = appCtx.callMethod<QtJniTypes::PackageManager>("getPackageManager"); + QVERIFY(packageManager.isValid()); + + const auto packageInfo = QJniObject(packageManager.callMethod<QtJniTypes::PackageInfo>( + "getPackageInfo", packageName, jint(GET_PERMISSIONS))); + QVERIFY(packageInfo.isValid()); + + m_requestedPermissions = packageInfo.getField<QJniArray<QString>>("requestedPermissions"); + QVERIFY(m_requestedPermissions.isValid()); +} + +void tst_android_permissions::checkExpectedDefaults() +{ + QSet<QString> expectedDefaults{ { "android.permission.INTERNET"_L1 }, + { "android.permission.WRITE_EXTERNAL_STORAGE"_L1 }, + { "android.permission.READ_EXTERNAL_STORAGE"_L1 } }; + + for (const auto &permission : m_requestedPermissions) + expectedDefaults.remove(permission); + + QVERIFY(expectedDefaults.empty()); +} + +void tst_android_permissions::checkNonExisting() +{ + for (const auto &permission : m_requestedPermissions) + QCOMPARE_NE(permission, "android.permission.BLUETOOTH_SCAN"); +} + +void tst_android_permissions::checkNonDefaultPermissions() +{ + bool hasNonDefaultPermissions = false; + for (const auto &permission : m_requestedPermissions) { + if (permission == "android.permission.ACCESS_COARSE_LOCATION") + hasNonDefaultPermissions = true; + } + + QVERIFY(hasNonDefaultPermissions); +} + +QTEST_MAIN(tst_android_permissions); + +#include "tst_android_permissions.moc" diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index 03be7aa0fe9..b651668cabd 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -213,6 +213,7 @@ private slots: void storeRestoreLowMemoryMode(); void setSectionResizeModeWithSectionWillTakeMemory(); void setModelWithAutoSizeWillSwitchToMemoryMode(); + void tableViewResizeSectionsWillSwitchToMemoryMode(); void setDefaultSectionSizeRespectsColumnWidth(); @@ -3559,12 +3560,17 @@ struct TableViewWithBasicModel : public QTableView emptyState = verticalHeader()->saveState(); setModel(&m); header = verticalHeader(); + horizontal_header = horizontalHeader(); } bool hasLowMemoryUsage() const { return emptyState.size() == header->saveState().size(); } + bool horizontalLowMememorUsage() const { + return emptyState.size() == horizontal_header->saveState().size(); + } + bool hasHigherMemoryUsage() const { const int delta = 1000; return header->saveState().size() > delta + emptyState.size(); @@ -3572,6 +3578,7 @@ struct TableViewWithBasicModel : public QTableView BasicModel m; QHeaderView *header; + QHeaderView *horizontal_header; QByteArray emptyState; }; @@ -3770,6 +3777,15 @@ void tst_QHeaderView::setModelWithAutoSizeWillSwitchToMemoryMode() QCOMPARE_GT(nonEmptyState.size(), emptyState.size() + delta); } +void tst_QHeaderView::tableViewResizeSectionsWillSwitchToMemoryMode() +{ + TableViewWithBasicModel tv; + QVERIFY(tv.horizontalLowMememorUsage()); + tv.horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + tv.resizeColumnsToContents(); + QVERIFY(!tv.horizontalLowMememorUsage()); +} + void tst_QHeaderView::setDefaultSectionSizeRespectsColumnWidth() { QTreeWidget tree; |