Port Qt Quick GUI for Android to Qt 6
This commit is contained in:
parent
430e17416a
commit
4bf6a91d72
|
@ -42,3 +42,6 @@ Makefile*
|
||||||
|
|
||||||
# clang-format
|
# clang-format
|
||||||
/.clang-format
|
/.clang-format
|
||||||
|
|
||||||
|
# Android-specific
|
||||||
|
/android/AndroidManifest.xml
|
||||||
|
|
|
@ -130,11 +130,6 @@ use_qt_utilities()
|
||||||
find_package(passwordfile${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED)
|
find_package(passwordfile${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED)
|
||||||
use_password_file()
|
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
|
# 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)
|
# well)
|
||||||
if (QUICK_GUI AND NOT WIDGETS_GUI)
|
if (QUICK_GUI AND NOT WIDGETS_GUI)
|
||||||
|
@ -147,20 +142,33 @@ if (QUICK_GUI AND NOT WIDGETS_GUI)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# add further Qt/KF modules required by the Qt Quick GUI under Android
|
# deduce major Qt version from package prefix
|
||||||
if (ANDROID)
|
if (NOT QT_PACKAGE_PREFIX)
|
||||||
list(APPEND ADDITIONAL_QT_MODULES AndroidExtras)
|
set(MAJOR_QT_VERSION "5")
|
||||||
|
elseif (QT_PACKAGE_PREFIX MATCHES ".*Qt([0-9]+).*")
|
||||||
|
set(MAJOR_QT_VERSION "${CMAKE_MATCH_1}")
|
||||||
endif ()
|
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)
|
if (QUICK_GUI)
|
||||||
|
list(APPEND ADDITIONAL_QT_MODULES QuickControls2)
|
||||||
list(APPEND ADDITIONAL_KF_MODULES Kirigami2)
|
list(APPEND ADDITIONAL_KF_MODULES Kirigami2)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# add Qt-version-specific QML files
|
# add Qt-version-specific QML files
|
||||||
unset(QML_FILE)
|
unset(QML_FILE)
|
||||||
if (NOT QT_PACKAGE_PREFIX)
|
if (MAJOR_QT_VERSION)
|
||||||
set(QML_FILE "resources/qml5.qrc")
|
set(QML_FILE "resources/qml${MAJOR_QT_VERSION}.qrc")
|
||||||
elseif (QT_PACKAGE_PREFIX MATCHES ".*Qt([0-9]+).*")
|
|
||||||
set(QML_FILE "resources/qml${CMAKE_MATCH_1}.qrc")
|
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT QML_FILE)
|
if (NOT QML_FILE)
|
||||||
message(FATAL_ERROR "Unable to add Qt-version-specific resource file for QT_PACKAGE_PREFIX \"${QT_PACKAGE_PREFIX}\".")
|
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 ()
|
endif ()
|
||||||
include(WindowsResources)
|
include(WindowsResources)
|
||||||
include(AppTarget)
|
include(AppTarget)
|
||||||
include(AndroidApk)
|
|
||||||
include(ShellCompletion)
|
include(ShellCompletion)
|
||||||
include(ConfigHeader)
|
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
|
# create desktop file using previously defined meta data
|
||||||
add_desktop_file()
|
add_desktop_file()
|
||||||
|
|
131
README.md
131
README.md
|
@ -124,131 +124,44 @@ always requires the same major Qt version as your KDE modules use.
|
||||||
the desired location afterwards.
|
the desired location afterwards.
|
||||||
|
|
||||||
#### Concrete example of 3. for building an Android APK under Arch Linux
|
#### 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_dir=/path/to/keystore-dir
|
||||||
keystore_alias=$USER
|
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
|
||||||
keystore_url=$keystore_dir/$keystore_alias
|
|
||||||
|
|
||||||
# make up some password to protect the store; enter this on keytool invocation
|
|
||||||
keystore_password=<password>
|
|
||||||
|
|
||||||
# create keystore (do only once)
|
# create keystore (do only once)
|
||||||
|
mkdir -p "$keystore_dir"
|
||||||
pushd "$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
|
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
|
# use Java 17, the latest Java doesn't work at this point and avoid unwanted Java options
|
||||||
_pkg_arch=aarch64
|
export PATH=/usr/lib/jvm/java-17-openjdk/bin:$PATH
|
||||||
_android_arch=arm64-v8a
|
export _JAVA_OPTIONS=
|
||||||
_android_arch2=arm64
|
|
||||||
_android_api_level=22
|
|
||||||
|
|
||||||
# set project name
|
# configure and build using helpers from android-cmake package
|
||||||
_reponame=passwordmanager
|
android_arch=aarch64
|
||||||
_pkgname=passwordmanager
|
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
|
# install the app
|
||||||
android_sdk_root=${ANDROID_SDK_ROOT:-/opt/android-sdk}
|
adb install "$build_dir/passwordmanager/android-build//build/outputs/apk/release/android-build-release-signed.apk"
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Notes
|
##### 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.
|
my [PKGBUILDs](http://github.com/Martchus/PKGBUILDs) repo.
|
||||||
* The latest Java I was able to use was version 8 (`jdk8-openjdk` package).
|
* The latest Java I was able to use was version 17.
|
||||||
|
|
||||||
### Manual deployment of Android APK file
|
|
||||||
1. Find device ID: `adb devices`
|
|
||||||
2. Install App on phone: `adb -s <DEVICE_ID> install -r $BUILD_DIR/passwordmanager_build_apk/build/outputs/apk/passwordmanager_build_apk-debug.apk`
|
|
||||||
3. View log: `adb -s <DEVICE_ID> logcat`
|
|
||||||
|
|
||||||
### Building without Qt GUI
|
### 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.
|
It is possible to build without the GUI if only the CLI is needed. In this case no Qt dependencies (including qtutilities) are required.
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<manifest package="@META_ANDROID_PACKAGE_NAME@" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="@META_APP_VERSION@" android:versionCode="@META_VERSION_MAJOR@" android:installLocation="auto">
|
|
||||||
<application
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:resizeableActivity="true">
|
|
||||||
<activity
|
|
||||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
|
||||||
android:name="@META_ANDROID_PACKAGE_NAME@.Activity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:screenOrientation="unspecified"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<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="-- %%INSERT_APP_LIB_NAME%% --"/>
|
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
|
||||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
|
||||||
<!-- Deploy Qt libs as part of package -->
|
|
||||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
|
||||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
|
||||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
|
||||||
<!-- Run with local libs -->
|
|
||||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
|
||||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
|
||||||
<!--<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>-->
|
|
||||||
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
|
|
||||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
|
||||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
|
||||||
<!-- Messages maps -->
|
|
||||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
|
||||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
|
||||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
|
||||||
|
|
||||||
<!-- Splash screen -->
|
|
||||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splash"/>
|
|
||||||
<!-- Splash screen -->
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23"/>
|
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
|
||||||
|
|
||||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
|
||||||
Remove the comment if you do not require these default permissions. -->
|
|
||||||
<!-- %%INSERT_PERMISSIONS -->
|
|
||||||
|
|
||||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
|
||||||
Remove the comment if you do not require these default features. -->
|
|
||||||
<!-- %%INSERT_FEATURES -->
|
|
||||||
</manifest>
|
|
|
@ -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(",")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
android.useAndroidX=true
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name">@ANDROID_APK_APPLICATION_LABEL@</string>
|
|
||||||
</resources>
|
|
|
@ -7,9 +7,9 @@ import android.os.Bundle;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager.LayoutParams;
|
import android.view.WindowManager.LayoutParams;
|
||||||
import android.support.v4.provider.DocumentFile;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
import org.qtproject.qt.android.bindings.QtActivity;
|
||||||
|
|
||||||
public class Activity extends QtActivity {
|
public class Activity extends QtActivity {
|
||||||
private final int REQUEST_CODE_OPEN_EXISTING_FILE = 1;
|
private final int REQUEST_CODE_OPEN_EXISTING_FILE = 1;
|
||||||
|
|
|
@ -15,17 +15,17 @@ Kirigami.ScrollablePage {
|
||||||
var currentEntryName = entryModel.data(rootIndex)
|
var currentEntryName = entryModel.data(rootIndex)
|
||||||
return currentEntryName ? currentEntryName : ""
|
return currentEntryName ? currentEntryName : ""
|
||||||
}
|
}
|
||||||
actions {
|
actions:[
|
||||||
main: Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "list-add"
|
icon.name: "list-add"
|
||||||
text: qsTr("Add account")
|
text: qsTr("Add account")
|
||||||
visible: !nativeInterface.hasEntryFilter
|
visible: !nativeInterface.hasEntryFilter
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: insertEntry("Account")
|
onTriggered: insertEntry("Account")
|
||||||
shortcut: "Ctrl+A"
|
shortcut: "Ctrl+A"
|
||||||
}
|
},
|
||||||
left: Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-paste"
|
icon.name: "edit-paste"
|
||||||
text: qsTr("Paste account")
|
text: qsTr("Paste account")
|
||||||
visible: !nativeInterface.hasEntryFilter
|
visible: !nativeInterface.hasEntryFilter
|
||||||
enabled: nativeInterface.canPaste && !nativeInterface.hasEntryFilter
|
enabled: nativeInterface.canPaste && !nativeInterface.hasEntryFilter
|
||||||
|
@ -40,16 +40,16 @@ Kirigami.ScrollablePage {
|
||||||
pastedEntries.join(", ")))
|
pastedEntries.join(", ")))
|
||||||
}
|
}
|
||||||
shortcut: StandardKey.Paste
|
shortcut: StandardKey.Paste
|
||||||
}
|
},
|
||||||
right: Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "folder-add"
|
icon.name: "folder-add"
|
||||||
text: qsTr("Add category")
|
text: qsTr("Add category")
|
||||||
visible: !nativeInterface.hasEntryFilter
|
visible: !nativeInterface.hasEntryFilter
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: insertEntry("Node")
|
onTriggered: insertEntry("Node")
|
||||||
shortcut: "Ctrl+Shift+A"
|
shortcut: "Ctrl+Shift+A"
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: Kirigami.Theme.backgroundColor
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
actions: [
|
actions: [
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-cut"
|
icon.name: "edit-cut"
|
||||||
text: qsTr("Cut")
|
text: qsTr("Cut")
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -229,7 +229,7 @@ Kirigami.ScrollablePage {
|
||||||
shortcut: StandardKey.Cut
|
shortcut: StandardKey.Cut
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-delete"
|
icon.name: "edit-delete"
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: confirmDeletionDialog.confirmDeletion(
|
onTriggered: confirmDeletionDialog.confirmDeletion(
|
||||||
|
@ -237,7 +237,7 @@ Kirigami.ScrollablePage {
|
||||||
shortcut: StandardKey.Delete
|
shortcut: StandardKey.Delete
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-rename"
|
icon.name: "edit-rename"
|
||||||
text: qsTr("Rename")
|
text: qsTr("Rename")
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: renameDialog.renameEntry(model.name, index)
|
onTriggered: renameDialog.renameEntry(model.name, index)
|
||||||
|
@ -259,11 +259,7 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
model: DelegateModel {
|
model: DelegateModel {
|
||||||
id: delegateModel
|
id: delegateModel
|
||||||
|
delegate: listDelegateComponent
|
||||||
delegate: Kirigami.DelegateRecycler {
|
|
||||||
width: parent ? parent.width : implicitWidth
|
|
||||||
sourceComponent: listDelegateComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNode(rowNumber) {
|
function isNode(rowNumber) {
|
||||||
return entryModel.isNode(entryModel.index(rowNumber, 0,
|
return entryModel.isNode(entryModel.index(rowNumber, 0,
|
||||||
|
|
|
@ -218,7 +218,7 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
actions: [
|
actions: [
|
||||||
Kirigami.Action {
|
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(
|
text: model.isPassword ? qsTr(
|
||||||
"Mark as normal field") : qsTr(
|
"Mark as normal field") : qsTr(
|
||||||
"Mark as password field")
|
"Mark as password field")
|
||||||
|
@ -228,7 +228,7 @@ Kirigami.ScrollablePage {
|
||||||
visible: !fieldRow.isLast
|
visible: !fieldRow.isLast
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-copy"
|
icon.name: "edit-copy"
|
||||||
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
||||||
"Copy value")
|
"Copy value")
|
||||||
onTriggered: showPassiveNotification(
|
onTriggered: showPassiveNotification(
|
||||||
|
@ -239,14 +239,14 @@ Kirigami.ScrollablePage {
|
||||||
visible: !fieldRow.isLast
|
visible: !fieldRow.isLast
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-delete"
|
icon.name: "edit-delete"
|
||||||
text: qsTr("Delete field")
|
text: qsTr("Delete field")
|
||||||
onTriggered: fieldsListView.model.removeRows(index, 1)
|
onTriggered: fieldsListView.model.removeRows(index, 1)
|
||||||
shortcut: StandardKey.Delete
|
shortcut: StandardKey.Delete
|
||||||
visible: !fieldRow.isLast
|
visible: !fieldRow.isLast
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "list-add"
|
icon.name: "list-add"
|
||||||
text: qsTr("Insert empty field after this")
|
text: qsTr("Insert empty field after this")
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: fieldsListView.model.insertRows(index + 1, 1)
|
onTriggered: fieldsListView.model.insertRows(index + 1, 1)
|
||||||
|
@ -267,9 +267,6 @@ Kirigami.ScrollablePage {
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate: Kirigami.DelegateRecycler {
|
delegate: fieldsListDelegateComponent
|
||||||
width: parent ? parent.width : implicitWidth
|
|
||||||
sourceComponent: fieldsListDelegateComponent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
qml/main.qml
36
qml/main.qml
|
@ -16,6 +16,12 @@ Kirigami.ApplicationWindow {
|
||||||
|
|
||||||
title: app.applicationName
|
title: app.applicationName
|
||||||
titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager.svg"
|
titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager.svg"
|
||||||
|
// FIXME: not sure why this doesn't work anymore
|
||||||
|
//onBannerClicked: () => {
|
||||||
|
// leftMenu.resetMenu()
|
||||||
|
// aboutDialog.open()
|
||||||
|
//}
|
||||||
|
|
||||||
visible: true
|
visible: true
|
||||||
resetMenuOnTriggered: false
|
resetMenuOnTriggered: false
|
||||||
topContent: ColumnLayout {
|
topContent: ColumnLayout {
|
||||||
|
@ -101,20 +107,20 @@ Kirigami.ApplicationWindow {
|
||||||
actions: [
|
actions: [
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Create new file")
|
text: qsTr("Create new file")
|
||||||
iconName: "document-new"
|
icon.name: "document-new"
|
||||||
onTriggered: fileDialog.createNew()
|
onTriggered: fileDialog.createNew()
|
||||||
shortcut: StandardKey.New
|
shortcut: StandardKey.New
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Open existing file")
|
text: qsTr("Open existing file")
|
||||||
iconName: "document-open"
|
icon.name: "document-open"
|
||||||
onTriggered: fileDialog.openExisting()
|
onTriggered: fileDialog.openExisting()
|
||||||
shortcut: StandardKey.Open
|
shortcut: StandardKey.Open
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
id: recentlyOpenedAction
|
id: recentlyOpenedAction
|
||||||
text: qsTr("Recently opened ...")
|
text: qsTr("Recently opened ...")
|
||||||
iconName: "document-open-recent"
|
icon.name: "document-open-recent"
|
||||||
children: createRecentlyOpenedActions(
|
children: createRecentlyOpenedActions(
|
||||||
nativeInterface.recentFiles)
|
nativeInterface.recentFiles)
|
||||||
visible: nativeInterface.recentFiles.length > 0
|
visible: nativeInterface.recentFiles.length > 0
|
||||||
|
@ -123,14 +129,14 @@ Kirigami.ApplicationWindow {
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Save modifications")
|
text: qsTr("Save modifications")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
iconName: "document-save"
|
icon.name: "document-save"
|
||||||
onTriggered: nativeInterface.save()
|
onTriggered: nativeInterface.save()
|
||||||
shortcut: StandardKey.Save
|
shortcut: StandardKey.Save
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Save as")
|
text: qsTr("Save as")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
iconName: "document-save-as"
|
icon.name: "document-save-as"
|
||||||
onTriggered: fileDialog.saveAs()
|
onTriggered: fileDialog.saveAs()
|
||||||
shortcut: StandardKey.SaveAs
|
shortcut: StandardKey.SaveAs
|
||||||
},
|
},
|
||||||
|
@ -138,7 +144,7 @@ Kirigami.ApplicationWindow {
|
||||||
text: nativeInterface.passwordSet ? qsTr("Change password") : qsTr(
|
text: nativeInterface.passwordSet ? qsTr("Change password") : qsTr(
|
||||||
"Add password")
|
"Add password")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
iconName: "document-encrypt"
|
icon.name: "document-encrypt"
|
||||||
onTriggered: enterPasswordDialog.askForNewPassword(
|
onTriggered: enterPasswordDialog.askForNewPassword(
|
||||||
qsTr("Change password for %1").arg(
|
qsTr("Change password for %1").arg(
|
||||||
nativeInterface.filePath))
|
nativeInterface.filePath))
|
||||||
|
@ -147,7 +153,7 @@ Kirigami.ApplicationWindow {
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Details")
|
text: qsTr("Details")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
iconName: "document-properties"
|
icon.name: "document-properties"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
leftMenu.resetMenu()
|
leftMenu.resetMenu()
|
||||||
fileSummaryDialog.show()
|
fileSummaryDialog.show()
|
||||||
|
@ -159,7 +165,7 @@ Kirigami.ApplicationWindow {
|
||||||
"Adjust search")
|
"Adjust search")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
visible: nativeInterface.filterAsDialog
|
visible: nativeInterface.filterAsDialog
|
||||||
iconName: "search"
|
icon.name: "search"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
leftMenu.resetMenu()
|
leftMenu.resetMenu()
|
||||||
filterDialog.open()
|
filterDialog.open()
|
||||||
|
@ -171,7 +177,7 @@ Kirigami.ApplicationWindow {
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
visible: nativeInterface.filterAsDialog
|
visible: nativeInterface.filterAsDialog
|
||||||
&& nativeInterface.entryFilter.length > 0
|
&& nativeInterface.entryFilter.length > 0
|
||||||
iconName: "edit-clear"
|
icon.name: "edit-clear"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
leftMenu.resetMenu()
|
leftMenu.resetMenu()
|
||||||
nativeInterface.entryFilter = ""
|
nativeInterface.entryFilter = ""
|
||||||
|
@ -183,7 +189,7 @@ Kirigami.ApplicationWindow {
|
||||||
visible: nativeInterface.undoText.length !== 0
|
visible: nativeInterface.undoText.length !== 0
|
||||||
&& nativeInterface.entryFilter.length === 0
|
&& nativeInterface.entryFilter.length === 0
|
||||||
enabled: visible
|
enabled: visible
|
||||||
iconName: "edit-undo"
|
icon.name: "edit-undo"
|
||||||
shortcut: StandardKey.Undo
|
shortcut: StandardKey.Undo
|
||||||
onTriggered: nativeInterface.undo()
|
onTriggered: nativeInterface.undo()
|
||||||
},
|
},
|
||||||
|
@ -192,22 +198,18 @@ Kirigami.ApplicationWindow {
|
||||||
visible: nativeInterface.redoText.length !== 0
|
visible: nativeInterface.redoText.length !== 0
|
||||||
&& nativeInterface.entryFilter.length === 0
|
&& nativeInterface.entryFilter.length === 0
|
||||||
enabled: visible
|
enabled: visible
|
||||||
iconName: "edit-redo"
|
icon.name: "edit-redo"
|
||||||
shortcut: StandardKey.Redo
|
shortcut: StandardKey.Redo
|
||||||
onTriggered: nativeInterface.redo()
|
onTriggered: nativeInterface.redo()
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Close file")
|
text: qsTr("Close file")
|
||||||
enabled: nativeInterface.fileOpen
|
enabled: nativeInterface.fileOpen
|
||||||
iconName: "document-close"
|
icon.name: "document-close"
|
||||||
shortcut: StandardKey.Close
|
shortcut: StandardKey.Close
|
||||||
onTriggered: nativeInterface.close()
|
onTriggered: nativeInterface.close()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
onBannerClicked: {
|
|
||||||
leftMenu.resetMenu()
|
|
||||||
aboutDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
Controls.Switch {
|
Controls.Switch {
|
||||||
text: qsTr("Use native file dialog")
|
text: qsTr("Use native file dialog")
|
||||||
|
@ -395,7 +397,7 @@ Kirigami.ApplicationWindow {
|
||||||
id: clearRecentFilesActionComponent
|
id: clearRecentFilesActionComponent
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: qsTr("Clear recently opened files")
|
text: qsTr("Clear recently opened files")
|
||||||
iconName: "edit-clear"
|
icon.name: "edit-clear"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
nativeInterface.clearRecentFiles()
|
nativeInterface.clearRecentFiles()
|
||||||
leftMenu.resetMenu()
|
leftMenu.resetMenu()
|
||||||
|
|
|
@ -5,12 +5,11 @@
|
||||||
|
|
||||||
#include <c++utilities/conversion/stringbuilder.h>
|
#include <c++utilities/conversion/stringbuilder.h>
|
||||||
|
|
||||||
#include <QAndroidJniObject>
|
#include <QJniObject>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QMessageLogContext>
|
#include <QMessageLogContext>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
#include <QtAndroid>
|
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
|
@ -35,9 +34,9 @@ static Controller *controllerForAndroid = nullptr;
|
||||||
|
|
||||||
void applyThemingForAndroid()
|
void applyThemingForAndroid()
|
||||||
{
|
{
|
||||||
QtAndroid::runOnAndroidThread([=]() {
|
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
|
||||||
const auto color = QColor(QLatin1String("#2c714a")).rgba();
|
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<void>("addFlags", "(I)V", Android::WindowManager::LayoutParams::DrawsSystemBarBackgrounds);
|
window.callMethod<void>("addFlags", "(I)V", Android::WindowManager::LayoutParams::DrawsSystemBarBackgrounds);
|
||||||
window.callMethod<void>("clearFlags", "(I)V", Android::WindowManager::LayoutParams::TranslucentStatus);
|
window.callMethod<void>("clearFlags", "(I)V", Android::WindowManager::LayoutParams::TranslucentStatus);
|
||||||
window.callMethod<void>("setStatusBarColor", "(I)V", color);
|
window.callMethod<void>("setStatusBarColor", "(I)V", color);
|
||||||
|
@ -52,13 +51,13 @@ void registerControllerForAndroid(Controller *controller)
|
||||||
|
|
||||||
bool showAndroidFileDialog(bool existing, bool createNew)
|
bool showAndroidFileDialog(bool existing, bool createNew)
|
||||||
{
|
{
|
||||||
return QtAndroid::androidActivity().callMethod<jboolean>("showAndroidFileDialog", "(ZZ)Z", existing, createNew);
|
return QJniObject(QNativeInterface::QAndroidApplication::context()).callMethod<jboolean>("showAndroidFileDialog", "(ZZ)Z", existing, createNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
int openFileDescriptorFromAndroidContentUrl(const QString &url, const QString &mode)
|
int openFileDescriptorFromAndroidContentUrl(const QString &url, const QString &mode)
|
||||||
{
|
{
|
||||||
return QtAndroid::androidActivity().callMethod<jint>("openFileDescriptorFromAndroidContentUri", "(Ljava/lang/String;Ljava/lang/String;)I",
|
return QJniObject(QNativeInterface::QAndroidApplication::context()).callMethod<jint>("openFileDescriptorFromAndroidContentUri", "(Ljava/lang/String;Ljava/lang/String;)I",
|
||||||
QAndroidJniObject::fromString(url).object<jstring>(), QAndroidJniObject::fromString(mode).object<jstring>());
|
QJniObject::fromString(url).object<jstring>(), QJniObject::fromString(mode).object<jstring>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToAndroidLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
void writeToAndroidLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
@ -102,19 +101,19 @@ void setupAndroidSpecifics()
|
||||||
static void onAndroidError(JNIEnv *, jobject, jstring message)
|
static void onAndroidError(JNIEnv *, jobject, jstring message)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(
|
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)
|
static void onAndroidFileDialogAccepted(JNIEnv *, jobject, jstring fileName, jboolean existing, jboolean createNew)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(QtGui::controllerForAndroid, "handleFileSelectionAccepted", Qt::QueuedConnection,
|
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)
|
static void onAndroidFileDialogAcceptedDescriptor(JNIEnv *, jobject, jstring nativeUrl, jstring fileName, jint fileHandle, jboolean existing, jboolean createNew)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(QtGui::controllerForAndroid, "handleFileSelectionAcceptedDescriptor", Qt::QueuedConnection,
|
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));
|
Q_ARG(int, fileHandle), Q_ARG(bool, existing), Q_ARG(bool, createNew));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="@META_ANDROID_PACKAGE_NAME@"
|
||||||
|
android:label="@META_APP_NAME@"
|
||||||
|
android:installLocation="auto"
|
||||||
|
android:versionName="@META_APP_VERSION@"
|
||||||
|
android:versionCode="@META_VERSION_MAJOR@">
|
||||||
|
<!-- %%INSERT_PERMISSIONS -->
|
||||||
|
<!-- %%INSERT_FEATURES -->
|
||||||
|
<supports-screens
|
||||||
|
android:anyDensity="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:smallScreens="true" />
|
||||||
|
<application
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:label="@META_APP_NAME@"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:allowNativeHeapPointerTagging="false"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:fullBackupOnly="false">
|
||||||
|
<activity
|
||||||
|
android:name="org.martchus.passwordmanager.Activity"
|
||||||
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||||
|
android:label="@META_APP_NAME@"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<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="-- %%INSERT_APP_LIB_NAME%% --" />
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.arguments"
|
||||||
|
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.extract_android_style"
|
||||||
|
android:value="minimal" />
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.splash_screen_drawable"
|
||||||
|
android:resource="@drawable/splash" />
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
Loading…
Reference in New Issue