From 4bf6a91d722f929d0e8cfe76faec1c1cbbb0f4d1 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 4 Mar 2023 17:27:38 +0100 Subject: [PATCH] Port Qt Quick GUI for Android to Qt 6 --- .gitignore | 3 + CMakeLists.txt | 53 +++++-- README.md | 131 +++--------------- android/AndroidManifest.xml.in | 54 -------- android/build.gradle | 83 +++++++++++ android/build.gradle.in | 63 --------- android/gradle.properties | 1 + android/res/values/strings.xml.in | 4 - .../martchus/passwordmanager/Activity.java | 4 +- qml/EntriesPage.qml | 32 ++--- qml/FieldsPage.qml | 13 +- qml/main.qml | 36 ++--- quickgui/android.cpp | 19 ++- resources/AndroidManifest.xml.in | 50 +++++++ 14 files changed, 248 insertions(+), 298 deletions(-) delete mode 100644 android/AndroidManifest.xml.in create mode 100644 android/build.gradle delete mode 100644 android/build.gradle.in create mode 100644 android/gradle.properties delete mode 100644 android/res/values/strings.xml.in create mode 100644 resources/AndroidManifest.xml.in diff --git a/.gitignore b/.gitignore index 3991c1d..efe5228 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ Makefile* # clang-format /.clang-format + +# Android-specific +/android/AndroidManifest.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index b24730c..9bc7332 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,11 +130,6 @@ use_qt_utilities() find_package(passwordfile${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED) use_password_file() -# require at least Qt 5.8 for the Qt Quick GUI -if (QUICK_GUI) - set(META_QT5_VERSION 5.8) -endif () - # allow to enable undo support from the widgets GUI in the quick GUI as well (so the quick GUI will depend on Qt Widgets as # well) if (QUICK_GUI AND NOT WIDGETS_GUI) @@ -147,20 +142,33 @@ if (QUICK_GUI AND NOT WIDGETS_GUI) endif () endif () -# add further Qt/KF modules required by the Qt Quick GUI under Android -if (ANDROID) - list(APPEND ADDITIONAL_QT_MODULES AndroidExtras) +# deduce major Qt version from package prefix +if (NOT QT_PACKAGE_PREFIX) + set(MAJOR_QT_VERSION "5") +elseif (QT_PACKAGE_PREFIX MATCHES ".*Qt([0-9]+).*") + set(MAJOR_QT_VERSION "${CMAKE_MATCH_1}") endif () + +# require Qt 6 for the Qt Quick GUI +if (QUICK_GUI AND MAJOR_QT_VERSION VERSION_LESS 6 OR MAJOR_QT_VERSION VERSION_GREATER_EQUAL 7) + message(FATAL_ERROR "The Qt Quick GUI is only compatible with Qt 6 (but Qt ${MAJOR_QT_VERSION} was found).") +endif () + +# workaround "ld: error: undefined symbol: qt_resourceFeatureZstd" when Qt 6 is not configured with zstd support +if (MAJOR_QT_VERSION GREATER_EQUAL 6 AND NOT QT_FEATURE_zstd) + set(CMAKE_AUTORCC_OPTIONS "--no-zstd") +endif () + +# add further Qt/KF modules required by Qt Quick GUI if (QUICK_GUI) + list(APPEND ADDITIONAL_QT_MODULES QuickControls2) list(APPEND ADDITIONAL_KF_MODULES Kirigami2) endif () # add Qt-version-specific QML files unset(QML_FILE) -if (NOT QT_PACKAGE_PREFIX) - set(QML_FILE "resources/qml5.qrc") -elseif (QT_PACKAGE_PREFIX MATCHES ".*Qt([0-9]+).*") - set(QML_FILE "resources/qml${CMAKE_MATCH_1}.qrc") +if (MAJOR_QT_VERSION) + set(QML_FILE "resources/qml${MAJOR_QT_VERSION}.qrc") endif () if (NOT QML_FILE) message(FATAL_ERROR "Unable to add Qt-version-specific resource file for QT_PACKAGE_PREFIX \"${QT_PACKAGE_PREFIX}\".") @@ -180,9 +188,28 @@ if (WIDGETS_GUI OR QUICK_GUI) endif () include(WindowsResources) include(AppTarget) -include(AndroidApk) include(ShellCompletion) include(ConfigHeader) +# configure creating an Android package using androiddeployqt +if (ANDROID) + set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + set_target_properties(${META_TARGET_NAME} PROPERTIES QT_ANDROID_PACKAGE_SOURCE_DIR "${ANDROID_PACKAGE_SOURCE_DIR}") + + set(ANDROID_MANIFEST_PATH "${ANDROID_PACKAGE_SOURCE_DIR}/AndroidManifest.xml") + configure_file("resources/AndroidManifest.xml.in" "${ANDROID_MANIFEST_PATH}") + + # bundle OpenMP (used by Kirigami) explicitly as it is otherwise not bundled + find_package(OpenMP) + if (OpenMP_CXX_FOUND) + message(STATUS "Bundling OpenMP library for Kirigami: ${OpenMP_omp_LIBRARY}") + set_target_properties(${META_TARGET_NAME} PROPERTIES QT_ANDROID_EXTRA_LIBS "${OpenMP_omp_LIBRARY}") + endif () + + set(QT_ANDROID_SIGN_APK ON) + qt_android_generate_deployment_settings(${META_TARGET_NAME}) + qt_android_add_apk_target(${META_TARGET_NAME}) +endif () + # create desktop file using previously defined meta data add_desktop_file() diff --git a/README.md b/README.md index b9a2445..17514f9 100644 --- a/README.md +++ b/README.md @@ -124,131 +124,44 @@ always requires the same major Qt version as your KDE modules use. the desired location afterwards. #### Concrete example of 3. for building an Android APK under Arch Linux -Create stuff for signing the package (remove `-DANDROID_APK_FORCE_DEBUG=ON` line in the CMake invocation to actually use this): +Create stuff for signing the package: ``` -# locate keystore +# set variables for creating keystore and androiddeployqt to find it keystore_dir=/path/to/keystore-dir -keystore_alias=$USER -keystore_url=$keystore_dir/$keystore_alias - -# make up some password to protect the store; enter this on keytool invocation -keystore_password= +export QT_ANDROID_KEYSTORE_PATH=$keystore_dir QT_ANDROID_KEYSTORE_ALIAS=$USER QT_ANDROID_KEYSTORE_STORE_PASS=$USER-devel QT_ANDROID_KEYSTORE_KEY_PASS=$USER-devel # create keystore (do only once) +mkdir -p "$keystore_dir" pushd "$keystore_dir" -keytool -genkey -v -keystore "$keystore_alias" -alias "$keystore_alias" -keyalg RSA -keysize 2048 -validity 10000 +keytool -genkey -v -keystore "$QT_ANDROID_KEYSTORE_ALIAS" -alias "$QT_ANDROID_KEYSTORE_ALIAS" -keyalg RSA -keysize 2048 -validity 10000 popd ``` -Build c++utilities, passwordfile, qtutilities and passwordmanager in one step to create an Android APK for arm64-v8a: +Build c++utilities, passwordfile, qtutilities and passwordmanager in one step to create an Android APK for aarch64: ``` -# specify Android platform -_pkg_arch=aarch64 -_android_arch=arm64-v8a -_android_arch2=arm64 -_android_api_level=22 +# use Java 17, the latest Java doesn't work at this point and avoid unwanted Java options +export PATH=/usr/lib/jvm/java-17-openjdk/bin:$PATH +export _JAVA_OPTIONS= -# set project name -_reponame=passwordmanager -_pkgname=passwordmanager +# configure and build using helpers from android-cmake package +android_arch=aarch64 +build_dir=$BUILD_DIR/../manual/passwordmanager-android-$android_arch-release +source /usr/bin/android-env $android_arch +android-$android_arch-cmake -G Ninja -S . -B "$build_dir" \ + -DCMAKE_FIND_ROOT_PATH="${ANDROID_PREFIX}" -DANDROID_SDK_ROOT="${ANDROID_HOME}" \ + -DPKG_CONFIG_EXECUTABLE:FILEPATH=/usr/bin/android-$android_arch-pkg-config \ + -DQT_PACKAGE_PREFIX:STRING=Qt6 -DKF_PACKAGE_PREFIX:STRING=KF6 +cmake --build "$build_dir" -# locate SDK, NDK and further libraries -android_sdk_root=${ANDROID_SDK_ROOT:-/opt/android-sdk} -android_ndk_root=${ANDROID_NDK_ROOT:-/opt/android-ndk} -build_tools_version=$(pacman -Q android-sdk-build-tools | sed 's/.* r\(.*\)-.*/\1/') -other_libs_root=/opt/android-libs/$_pkg_arch -other_libs_include=$other_libs_root/include -root="$android_ndk_root/sysroot;$other_libs_root" - -# use Java 8 which seems to be the latest version which works -export PATH=/usr/lib/jvm/java-8-openjdk/jre/bin/:$PATH - -# configure with the toolchain file provided by the Android NDK (still WIP) -# note: This configuration is likely required in the future to resolve https://gitlab.kitware.com/cmake/cmake/issues/18739. But for now -# better keep using CMake's internal Android support because this config has its own pitfalls (see CMAKE_CXX_FLAGS). -cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DANDROID_ABI=$_android_arch \ - -DANDROID_PLATFORM=$_android_api_level \ - -DCMAKE_TOOLCHAIN_FILE=$android_ndk_root/build/cmake/android.toolchain.cmake \ - -DCMAKE_SYSTEM_NAME=Android \ - -DCMAKE_SYSTEM_VERSION=$_android_api_level \ - -DCMAKE_ANDROID_ARCH_ABI=$_android_arch \ - -DCMAKE_ANDROID_NDK="$android_ndk_root" \ - -DCMAKE_ANDROID_SDK="$android_sdk_root" \ - -DCMAKE_ANDROID_STL_TYPE=c++_shared \ - -DCMAKE_INSTALL_PREFIX=$other_libs_root \ - -DCMAKE_PREFIX_PATH="$root" \ - -DCMAKE_FIND_ROOT_PATH="$root;$root/libs" \ - -DCMAKE_CXX_FLAGS="-include $android_ndk_root/sysroot/usr/include/math.h -include $android_ndk_root/sources/cxx-stl/llvm-libc++/include/math.h -I$other_libs_include" \ - -DBUILD_SHARED_LIBS=ON \ - -DZLIB_LIBRARY="$android_ndk_root/platforms/android-$_android_api_level/arch-$_android_arch2/usr/lib/libz.so" \ - -DCLANG_FORMAT_ENABLED=ON \ - -DUSE_NATIVE_FILE_BUFFER=ON \ - -DUSE_STANDARD_FILESYSTEM=OFF \ - -DNO_DOXYGEN=ON \ - -DWIDGETS_GUI=OFF \ - -DQUICK_GUI=ON \ - -DBUILTIN_ICON_THEMES=breeze \ - -DBUILTIN_TRANSLATIONS=ON \ - -DANDROID_APK_TOOLCHAIN_VERSION=4.9 \ - -DANDROID_APK_CXX_STANDARD_LIBRARY="$android_ndk_root/platforms/android-$_android_api_level/arch-$_android_arch2/usr/lib/libstdc++.so" \ - -DANDROID_APK_FORCE_DEBUG=ON \ - -DANDROID_APK_KEYSTORE_URL="$keystore_url" \ - -DANDROID_APK_KEYSTORE_ALIAS="$keystore_alias" \ - -DANDROID_APK_KEYSTORE_PASSWORD="$keystore_password" \ - -DANDROID_APK_APPLICATION_ID_SUFFIX=".unstable" \ - -DANDROID_APK_APPLICATION_LABEL="Password Manager (unstable)" \ - $SOURCES/subdirs/$_reponame - -# configure with CMake's internal Android support -# note: Requires workaround with Android NDK r19: https://gitlab.kitware.com/cmake/cmake/issues/18739#note_498676 -cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_SYSTEM_NAME=Android \ - -DCMAKE_SYSTEM_VERSION=$_android_api_level \ - -DCMAKE_ANDROID_ARCH_ABI=$_android_arch \ - -DCMAKE_ANDROID_NDK="$android_ndk_root" \ - -DCMAKE_ANDROID_SDK="$android_sdk_root" \ - -DCMAKE_ANDROID_STL_TYPE=c++_shared \ - -DCMAKE_INSTALL_PREFIX=$other_libs_root \ - -DCMAKE_PREFIX_PATH="$root" \ - -DCMAKE_FIND_ROOT_PATH="$root;$root/libs" \ - -DCMAKE_CXX_FLAGS="-D__ANDROID_API__=$_android_api_level" \ - -DCLANG_FORMAT_ENABLED=ON \ - -DBUILD_SHARED_LIBS=ON \ - -DUSE_NATIVE_FILE_BUFFER=ON \ - -DUSE_STANDARD_FILESYSTEM=OFF \ - -DNO_DOXYGEN=ON \ - -DWIDGETS_GUI=OFF \ - -DQUICK_GUI=ON \ - -DBUILTIN_ICON_THEMES=breeze \ - -DBUILTIN_TRANSLATIONS=ON \ - -DANDROID_APK_FORCE_DEBUG=ON \ - -DANDROID_APK_KEYSTORE_URL="$keystore_url" \ - -DANDROID_APK_KEYSTORE_ALIAS="$keystore_alias" \ - -DANDROID_APK_KEYSTORE_PASSWORD="$keystore_password" \ - -DANDROID_APK_APPLICATION_ID_SUFFIX=".unstable" \ - -DANDROID_APK_APPLICATION_LABEL="Password Manager (unstable)" \ - $SOURCES/subdirs/$_reponame - -# build all binaries and make APK file using all CPU cores -make passwordmanager_apk -j$(nproc) - -# install app on USB-connected phone -make passwordmanager_deploy_apk +# install the app +adb install "$build_dir/passwordmanager/android-build//build/outputs/apk/release/android-build-release-signed.apk" ``` ##### Notes -* The Android packages for the dependencies Qt, iconv, OpenSSL and Kirigami 2 are provided in +* The Android packages for the dependencies Boost, Qt, iconv, OpenSSL and Kirigami are provided in my [PKGBUILDs](http://github.com/Martchus/PKGBUILDs) repo. -* The latest Java I was able to use was version 8 (`jdk8-openjdk` package). - -### Manual deployment of Android APK file -1. Find device ID: `adb devices` -2. Install App on phone: `adb -s install -r $BUILD_DIR/passwordmanager_build_apk/build/outputs/apk/passwordmanager_build_apk-debug.apk` -3. View log: `adb -s logcat` +* The latest Java I was able to use was version 17. ### Building without Qt GUI It is possible to build without the GUI if only the CLI is needed. In this case no Qt dependencies (including qtutilities) are required. diff --git a/android/AndroidManifest.xml.in b/android/AndroidManifest.xml.in deleted file mode 100644 index 38197f9..0000000 --- a/android/AndroidManifest.xml.in +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..2dad8f9 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,83 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.4.1' + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) + implementation "androidx.documentfile:documentfile:1.0.1" + implementation 'androidx.core:core:1.10.1' +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qtAndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion + buildToolsVersion androidBuildToolsVersion + ndkVersion androidNdkVersion + + // Extract native libraries from the APK + packagingOptions.jniLibs.useLegacyPackaging true + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qtAndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qtAndroidDir + '/res', 'res'] + resources.srcDirs = ['resources'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + // Do not compress Qt binary resources file + aaptOptions { + noCompress 'rcc' + } + + defaultConfig { + resConfig "en" + minSdkVersion qtMinSdkVersion + targetSdkVersion qtTargetSdkVersion + ndk.abiFilters = qtTargetAbiList.split(",") + } +} diff --git a/android/build.gradle.in b/android/build.gradle.in deleted file mode 100644 index 7cbc260..0000000 --- a/android/build.gradle.in +++ /dev/null @@ -1,63 +0,0 @@ -buildscript { - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' - } -} - -repositories { - google() - jcenter() -} - -apply plugin: 'com.android.application' - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:27.1.0' -} - -android { - /******************************************************* - * The following variables: - * - androidBuildToolsVersion, - * - androidCompileSdkVersion - * - qt5AndroidDir - holds the path to qt android files - * needed to build any Qt application - * on Android. - * - * are defined in gradle.properties file. This file is - * updated by QtCreator and androiddeployqt tools. - * Changing them manually might break the compilation! - *******************************************************/ - - compileSdkVersion androidCompileSdkVersion.toInteger() - - buildToolsVersion androidBuildToolsVersion - - defaultConfig { - applicationId "@META_ANDROID_PACKAGE_NAME@" - applicationIdSuffix "@ANDROID_APK_APPLICATION_ID_SUFFIX@" - } - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] - aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] - res.srcDirs = [qt5AndroidDir + '/res', 'res'] - resources.srcDirs = ['src'] - renderscript.srcDirs = ['src'] - assets.srcDirs = ['assets'] - jniLibs.srcDirs = ['libs'] - } - } - - lintOptions { - abortOnError false - } -} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..5bac8ac --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX=true diff --git a/android/res/values/strings.xml.in b/android/res/values/strings.xml.in deleted file mode 100644 index 4e43e13..0000000 --- a/android/res/values/strings.xml.in +++ /dev/null @@ -1,4 +0,0 @@ - - - @ANDROID_APK_APPLICATION_LABEL@ - diff --git a/android/src/org/martchus/passwordmanager/Activity.java b/android/src/org/martchus/passwordmanager/Activity.java index 6311af4..c3e60a6 100644 --- a/android/src/org/martchus/passwordmanager/Activity.java +++ b/android/src/org/martchus/passwordmanager/Activity.java @@ -7,9 +7,9 @@ import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.view.Window; import android.view.WindowManager.LayoutParams; -import android.support.v4.provider.DocumentFile; +import androidx.documentfile.provider.DocumentFile; import java.io.FileNotFoundException; -import org.qtproject.qt5.android.bindings.QtActivity; +import org.qtproject.qt.android.bindings.QtActivity; public class Activity extends QtActivity { private final int REQUEST_CODE_OPEN_EXISTING_FILE = 1; diff --git a/qml/EntriesPage.qml b/qml/EntriesPage.qml index 847c57b..3a2e947 100644 --- a/qml/EntriesPage.qml +++ b/qml/EntriesPage.qml @@ -15,17 +15,17 @@ Kirigami.ScrollablePage { var currentEntryName = entryModel.data(rootIndex) return currentEntryName ? currentEntryName : "" } - actions { - main: Kirigami.Action { - iconName: "list-add" + actions:[ + Kirigami.Action { + icon.name: "list-add" text: qsTr("Add account") visible: !nativeInterface.hasEntryFilter enabled: !nativeInterface.hasEntryFilter onTriggered: insertEntry("Account") shortcut: "Ctrl+A" - } - left: Kirigami.Action { - iconName: "edit-paste" + }, + Kirigami.Action { + icon.name: "edit-paste" text: qsTr("Paste account") visible: !nativeInterface.hasEntryFilter enabled: nativeInterface.canPaste && !nativeInterface.hasEntryFilter @@ -40,16 +40,16 @@ Kirigami.ScrollablePage { pastedEntries.join(", "))) } shortcut: StandardKey.Paste - } - right: Kirigami.Action { - iconName: "folder-add" + }, + Kirigami.Action { + icon.name: "folder-add" text: qsTr("Add category") visible: !nativeInterface.hasEntryFilter enabled: !nativeInterface.hasEntryFilter onTriggered: insertEntry("Node") shortcut: "Ctrl+Shift+A" } - } + ] background: Rectangle { color: Kirigami.Theme.backgroundColor } @@ -218,7 +218,7 @@ Kirigami.ScrollablePage { } actions: [ Kirigami.Action { - iconName: "edit-cut" + icon.name: "edit-cut" text: qsTr("Cut") enabled: !nativeInterface.hasEntryFilter onTriggered: { @@ -229,7 +229,7 @@ Kirigami.ScrollablePage { shortcut: StandardKey.Cut }, Kirigami.Action { - iconName: "edit-delete" + icon.name: "edit-delete" text: qsTr("Delete") enabled: !nativeInterface.hasEntryFilter onTriggered: confirmDeletionDialog.confirmDeletion( @@ -237,7 +237,7 @@ Kirigami.ScrollablePage { shortcut: StandardKey.Delete }, Kirigami.Action { - iconName: "edit-rename" + icon.name: "edit-rename" text: qsTr("Rename") enabled: !nativeInterface.hasEntryFilter onTriggered: renameDialog.renameEntry(model.name, index) @@ -259,11 +259,7 @@ Kirigami.ScrollablePage { } model: DelegateModel { id: delegateModel - - delegate: Kirigami.DelegateRecycler { - width: parent ? parent.width : implicitWidth - sourceComponent: listDelegateComponent - } + delegate: listDelegateComponent function isNode(rowNumber) { return entryModel.isNode(entryModel.index(rowNumber, 0, diff --git a/qml/FieldsPage.qml b/qml/FieldsPage.qml index 6ad8f15..1b8842e 100644 --- a/qml/FieldsPage.qml +++ b/qml/FieldsPage.qml @@ -218,7 +218,7 @@ Kirigami.ScrollablePage { } actions: [ Kirigami.Action { - iconName: !model.isPassword ? "password-show-off" : "password-show-on" + icon.name: !model.isPassword ? "password-show-off" : "password-show-on" text: model.isPassword ? qsTr( "Mark as normal field") : qsTr( "Mark as password field") @@ -228,7 +228,7 @@ Kirigami.ScrollablePage { visible: !fieldRow.isLast }, Kirigami.Action { - iconName: "edit-copy" + icon.name: "edit-copy" text: model.isPassword ? qsTr("Copy password") : qsTr( "Copy value") onTriggered: showPassiveNotification( @@ -239,14 +239,14 @@ Kirigami.ScrollablePage { visible: !fieldRow.isLast }, Kirigami.Action { - iconName: "edit-delete" + icon.name: "edit-delete" text: qsTr("Delete field") onTriggered: fieldsListView.model.removeRows(index, 1) shortcut: StandardKey.Delete visible: !fieldRow.isLast }, Kirigami.Action { - iconName: "list-add" + icon.name: "list-add" text: qsTr("Insert empty field after this") enabled: !nativeInterface.hasEntryFilter onTriggered: fieldsListView.model.insertRows(index + 1, 1) @@ -267,9 +267,6 @@ Kirigami.ScrollablePage { easing.type: Easing.InOutQuad } } - delegate: Kirigami.DelegateRecycler { - width: parent ? parent.width : implicitWidth - sourceComponent: fieldsListDelegateComponent - } + delegate: fieldsListDelegateComponent } } diff --git a/qml/main.qml b/qml/main.qml index 2c23cc1..bedf80c 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -16,6 +16,12 @@ Kirigami.ApplicationWindow { title: app.applicationName titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager.svg" + // FIXME: not sure why this doesn't work anymore + //onBannerClicked: () => { + // leftMenu.resetMenu() + // aboutDialog.open() + //} + visible: true resetMenuOnTriggered: false topContent: ColumnLayout { @@ -101,20 +107,20 @@ Kirigami.ApplicationWindow { actions: [ Kirigami.Action { text: qsTr("Create new file") - iconName: "document-new" + icon.name: "document-new" onTriggered: fileDialog.createNew() shortcut: StandardKey.New }, Kirigami.Action { text: qsTr("Open existing file") - iconName: "document-open" + icon.name: "document-open" onTriggered: fileDialog.openExisting() shortcut: StandardKey.Open }, Kirigami.Action { id: recentlyOpenedAction text: qsTr("Recently opened ...") - iconName: "document-open-recent" + icon.name: "document-open-recent" children: createRecentlyOpenedActions( nativeInterface.recentFiles) visible: nativeInterface.recentFiles.length > 0 @@ -123,14 +129,14 @@ Kirigami.ApplicationWindow { Kirigami.Action { text: qsTr("Save modifications") enabled: nativeInterface.fileOpen - iconName: "document-save" + icon.name: "document-save" onTriggered: nativeInterface.save() shortcut: StandardKey.Save }, Kirigami.Action { text: qsTr("Save as") enabled: nativeInterface.fileOpen - iconName: "document-save-as" + icon.name: "document-save-as" onTriggered: fileDialog.saveAs() shortcut: StandardKey.SaveAs }, @@ -138,7 +144,7 @@ Kirigami.ApplicationWindow { text: nativeInterface.passwordSet ? qsTr("Change password") : qsTr( "Add password") enabled: nativeInterface.fileOpen - iconName: "document-encrypt" + icon.name: "document-encrypt" onTriggered: enterPasswordDialog.askForNewPassword( qsTr("Change password for %1").arg( nativeInterface.filePath)) @@ -147,7 +153,7 @@ Kirigami.ApplicationWindow { Kirigami.Action { text: qsTr("Details") enabled: nativeInterface.fileOpen - iconName: "document-properties" + icon.name: "document-properties" onTriggered: { leftMenu.resetMenu() fileSummaryDialog.show() @@ -159,7 +165,7 @@ Kirigami.ApplicationWindow { "Adjust search") enabled: nativeInterface.fileOpen visible: nativeInterface.filterAsDialog - iconName: "search" + icon.name: "search" onTriggered: { leftMenu.resetMenu() filterDialog.open() @@ -171,7 +177,7 @@ Kirigami.ApplicationWindow { enabled: nativeInterface.fileOpen visible: nativeInterface.filterAsDialog && nativeInterface.entryFilter.length > 0 - iconName: "edit-clear" + icon.name: "edit-clear" onTriggered: { leftMenu.resetMenu() nativeInterface.entryFilter = "" @@ -183,7 +189,7 @@ Kirigami.ApplicationWindow { visible: nativeInterface.undoText.length !== 0 && nativeInterface.entryFilter.length === 0 enabled: visible - iconName: "edit-undo" + icon.name: "edit-undo" shortcut: StandardKey.Undo onTriggered: nativeInterface.undo() }, @@ -192,22 +198,18 @@ Kirigami.ApplicationWindow { visible: nativeInterface.redoText.length !== 0 && nativeInterface.entryFilter.length === 0 enabled: visible - iconName: "edit-redo" + icon.name: "edit-redo" shortcut: StandardKey.Redo onTriggered: nativeInterface.redo() }, Kirigami.Action { text: qsTr("Close file") enabled: nativeInterface.fileOpen - iconName: "document-close" + icon.name: "document-close" shortcut: StandardKey.Close onTriggered: nativeInterface.close() } ] - onBannerClicked: { - leftMenu.resetMenu() - aboutDialog.open() - } Controls.Switch { text: qsTr("Use native file dialog") @@ -395,7 +397,7 @@ Kirigami.ApplicationWindow { id: clearRecentFilesActionComponent Kirigami.Action { text: qsTr("Clear recently opened files") - iconName: "edit-clear" + icon.name: "edit-clear" onTriggered: { nativeInterface.clearRecentFiles() leftMenu.resetMenu() diff --git a/quickgui/android.cpp b/quickgui/android.cpp index 726fc99..fe4ae48 100644 --- a/quickgui/android.cpp +++ b/quickgui/android.cpp @@ -5,12 +5,11 @@ #include -#include +#include #include #include #include #include -#include #include @@ -35,9 +34,9 @@ static Controller *controllerForAndroid = nullptr; void applyThemingForAndroid() { - QtAndroid::runOnAndroidThread([=]() { + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() { const auto color = QColor(QLatin1String("#2c714a")).rgba(); - QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); + QJniObject window = QJniObject(QNativeInterface::QAndroidApplication::context()).callObjectMethod("getWindow", "()Landroid/view/Window;"); window.callMethod("addFlags", "(I)V", Android::WindowManager::LayoutParams::DrawsSystemBarBackgrounds); window.callMethod("clearFlags", "(I)V", Android::WindowManager::LayoutParams::TranslucentStatus); window.callMethod("setStatusBarColor", "(I)V", color); @@ -52,13 +51,13 @@ void registerControllerForAndroid(Controller *controller) bool showAndroidFileDialog(bool existing, bool createNew) { - return QtAndroid::androidActivity().callMethod("showAndroidFileDialog", "(ZZ)Z", existing, createNew); + return QJniObject(QNativeInterface::QAndroidApplication::context()).callMethod("showAndroidFileDialog", "(ZZ)Z", existing, createNew); } int openFileDescriptorFromAndroidContentUrl(const QString &url, const QString &mode) { - return QtAndroid::androidActivity().callMethod("openFileDescriptorFromAndroidContentUri", "(Ljava/lang/String;Ljava/lang/String;)I", - QAndroidJniObject::fromString(url).object(), QAndroidJniObject::fromString(mode).object()); + return QJniObject(QNativeInterface::QAndroidApplication::context()).callMethod("openFileDescriptorFromAndroidContentUri", "(Ljava/lang/String;Ljava/lang/String;)I", + QJniObject::fromString(url).object(), QJniObject::fromString(mode).object()); } void writeToAndroidLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) @@ -102,19 +101,19 @@ void setupAndroidSpecifics() static void onAndroidError(JNIEnv *, jobject, jstring message) { QMetaObject::invokeMethod( - QtGui::controllerForAndroid, "newNotification", Qt::QueuedConnection, Q_ARG(QString, QAndroidJniObject::fromLocalRef(message).toString())); + QtGui::controllerForAndroid, "newNotification", Qt::QueuedConnection, Q_ARG(QString, QJniObject::fromLocalRef(message).toString())); } static void onAndroidFileDialogAccepted(JNIEnv *, jobject, jstring fileName, jboolean existing, jboolean createNew) { QMetaObject::invokeMethod(QtGui::controllerForAndroid, "handleFileSelectionAccepted", Qt::QueuedConnection, - Q_ARG(QString, QAndroidJniObject::fromLocalRef(fileName).toString()), Q_ARG(bool, existing), Q_ARG(bool, createNew)); + Q_ARG(QString, QJniObject::fromLocalRef(fileName).toString()), Q_ARG(bool, existing), Q_ARG(bool, createNew)); } static void onAndroidFileDialogAcceptedDescriptor(JNIEnv *, jobject, jstring nativeUrl, jstring fileName, jint fileHandle, jboolean existing, jboolean createNew) { QMetaObject::invokeMethod(QtGui::controllerForAndroid, "handleFileSelectionAcceptedDescriptor", Qt::QueuedConnection, - Q_ARG(QString, QAndroidJniObject::fromLocalRef(nativeUrl).toString()), Q_ARG(QString, QAndroidJniObject::fromLocalRef(fileName).toString()), + Q_ARG(QString, QJniObject::fromLocalRef(nativeUrl).toString()), Q_ARG(QString, QJniObject::fromLocalRef(fileName).toString()), Q_ARG(int, fileHandle), Q_ARG(bool, existing), Q_ARG(bool, createNew)); } diff --git a/resources/AndroidManifest.xml.in b/resources/AndroidManifest.xml.in new file mode 100644 index 0000000..a848418 --- /dev/null +++ b/resources/AndroidManifest.xml.in @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + +