summaryrefslogtreecommitdiffstats
path: root/src/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/android')
-rw-r--r--src/android/CMakeLists.txt6
-rw-r--r--src/android/android.pro3
-rw-r--r--src/android/jar/.gitignore9
-rw-r--r--src/android/jar/.prev_CMakeLists.txt30
-rw-r--r--src/android/jar/AndroidManifest.xml3
-rw-r--r--src/android/jar/CMakeLists.txt65
-rw-r--r--src/android/jar/build.gradle52
-rw-r--r--src/android/jar/jar.pro29
-rw-r--r--src/android/jar/settings.gradle1
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/CursorHandle.java199
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/EditContextView.java119
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/EditPopupMenu.java (renamed from src/android/jar/src/org/qtproject/qt5/android/EditPopupMenu.java)111
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java1858
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java (renamed from src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java)304
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java325
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java444
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java259
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java151
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java15
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtClipboardManager.java232
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java287
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEditText.java221
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java174
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java37
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java52
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java (renamed from src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java)195
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java654
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtLayout.java (renamed from src/android/jar/src/org/qtproject/qt5/android/QtLayout.java)125
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtLoader.java558
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtMessageDialogHelper.java336
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtNative.java456
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java22
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java98
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java51
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java28
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtSurface.java53
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java13
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtTextureView.java52
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtThread.java81
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtView.java187
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtWindow.java215
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/UsedFromNativeCode.java10
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidBinder.java36
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java45
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/extras/QtNative.java17
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/CursorHandle.java221
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/EditContextView.java123
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java2036
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java1305
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtEditText.java110
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java388
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtNative.java1276
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java60
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java200
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtSurface.java121
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtThread.java112
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java58
-rw-r--r--src/android/java/CMakeLists.txt70
-rw-r--r--src/android/java/java.pro23
-rw-r--r--src/android/java/res/values-de/strings.xml2
-rw-r--r--src/android/java/res/values-el/strings.xml2
-rw-r--r--src/android/java/res/values-es/strings.xml2
-rw-r--r--src/android/java/res/values-et/strings.xml2
-rw-r--r--src/android/java/res/values-fa/strings.xml2
-rw-r--r--src/android/java/res/values-fr/strings.xml2
-rw-r--r--src/android/java/res/values-in/strings.xml2
-rw-r--r--src/android/java/res/values-it/strings.xml2
-rw-r--r--src/android/java/res/values-ja/strings.xml2
-rw-r--r--src/android/java/res/values-ms/strings.xml2
-rw-r--r--src/android/java/res/values-nb/strings.xml2
-rw-r--r--src/android/java/res/values-nl/strings.xml2
-rw-r--r--src/android/java/res/values-pl/strings.xml2
-rw-r--r--src/android/java/res/values-pt-rBR/strings.xml2
-rw-r--r--src/android/java/res/values-ro/strings.xml2
-rw-r--r--src/android/java/res/values-ru/strings.xml2
-rw-r--r--src/android/java/res/values-se/strings.xml2
-rw-r--r--src/android/java/res/values-zh-rCN/strings.xml2
-rw-r--r--src/android/java/res/values-zh-rTW/strings.xml2
-rw-r--r--src/android/java/res/values/strings.xml2
-rw-r--r--src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl60
-rw-r--r--src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl65
-rw-r--r--src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java18
-rw-r--r--src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java15
-rw-r--r--src/android/java/src/org/qtproject/qt/android/bindings/QtService.java16
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java990
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java169
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java163
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java529
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java156
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java80
-rw-r--r--src/android/templates/.gitignore4
-rw-r--r--src/android/templates/AndroidManifest.xml119
-rw-r--r--src/android/templates/CMakeLists.txt39
-rw-r--r--src/android/templates/build.gradle36
-rw-r--r--src/android/templates/doc/src/android-manifest-file-configuration.qdoc287
-rw-r--r--src/android/templates/res/values/libs.xml11
-rw-r--r--src/android/templates/res/xml/qtprovider_paths.xml4
-rw-r--r--src/android/templates/templates.pro25
98 files changed, 8293 insertions, 8854 deletions
diff --git a/src/android/CMakeLists.txt b/src/android/CMakeLists.txt
index a821cee44b..045076c8d3 100644
--- a/src/android/CMakeLists.txt
+++ b/src/android/CMakeLists.txt
@@ -1,7 +1,6 @@
-# Generated from android.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-# special case skip regeneration
-# special case begin
# Only build jars during Android build and install the templates for the
# androiddeployqt tool.
# android
@@ -10,5 +9,4 @@ if (ANDROID)
add_subdirectory(java)
add_subdirectory(templates)
endif()
-# special case end
diff --git a/src/android/android.pro b/src/android/android.pro
deleted file mode 100644
index 1174084591..0000000000
--- a/src/android/android.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-CONFIG += ordered
-SUBDIRS = jar java templates
diff --git a/src/android/jar/.gitignore b/src/android/jar/.gitignore
new file mode 100644
index 0000000000..e2339f3385
--- /dev/null
+++ b/src/android/jar/.gitignore
@@ -0,0 +1,9 @@
+.gradle/
+.settings/
+.project
+build/
+gradle/
+gradlew
+gradlew.bat
+local.properties
+
diff --git a/src/android/jar/.prev_CMakeLists.txt b/src/android/jar/.prev_CMakeLists.txt
deleted file mode 100644
index f4c807c33a..0000000000
--- a/src/android/jar/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated from jar.pro.
-
-set(java_sources
- src/org/qtproject/qt5/android//accessibility/QtAccessibilityDelegate.java
- src/org/qtproject/qt5/android//accessibility/QtNativeAccessibility.java
- src/org/qtproject/qt5/android//CursorHandle.java
- src/org/qtproject/qt5/android//EditContextView.java
- src/org/qtproject/qt5/android//EditPopupMenu.java
- src/org/qtproject/qt5/android//ExtractStyle.java
- src/org/qtproject/qt5/android//QtActivityDelegate.java
- src/org/qtproject/qt5/android//QtEditText.java
- src/org/qtproject/qt5/android//QtInputConnection.java
- src/org/qtproject/qt5/android//QtLayout.java
- src/org/qtproject/qt5/android//QtMessageDialogHelper.java
- src/org/qtproject/qt5/android//QtNative.java
- src/org/qtproject/qt5/android//QtNativeLibrariesDir.java
- src/org/qtproject/qt5/android//QtSurface.java
- src/org/qtproject/qt5/android//QtThread.java
-)
-
-add_jar(QtAndroid
- INCLUDE_JARS ${QT_ANDROID_JAR}
- SOURCES ${java_sources}
-)
-
-install_jar(QtAndroid
- DESTINATION jar
- COMPONENT Devel
-)
-
diff --git a/src/android/jar/AndroidManifest.xml b/src/android/jar/AndroidManifest.xml
index cef88f7f19..c53b90c912 100644
--- a/src/android/jar/AndroidManifest.xml
+++ b/src/android/jar/AndroidManifest.xml
@@ -1,5 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="org.qtproject.qt5.android">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="org.qtproject.qt.android">
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
- <uses-sdk android:minSdkVersion="9" />
</manifest>
diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt
index 61af168a67..698853588c 100644
--- a/src/android/jar/CMakeLists.txt
+++ b/src/android/jar/CMakeLists.txt
@@ -1,31 +1,54 @@
-# Generated from jar.pro.
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
set(java_sources
- src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java
- src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java
- src/org/qtproject/qt5/android/CursorHandle.java
- src/org/qtproject/qt5/android/EditContextView.java
- src/org/qtproject/qt5/android/EditPopupMenu.java
- src/org/qtproject/qt5/android/ExtractStyle.java
- src/org/qtproject/qt5/android/QtActivityDelegate.java
- src/org/qtproject/qt5/android/QtEditText.java
- src/org/qtproject/qt5/android/QtInputConnection.java
- src/org/qtproject/qt5/android/QtLayout.java
- src/org/qtproject/qt5/android/QtMessageDialogHelper.java
- src/org/qtproject/qt5/android/QtNative.java
- src/org/qtproject/qt5/android/QtNativeLibrariesDir.java
- src/org/qtproject/qt5/android/QtSurface.java
- src/org/qtproject/qt5/android/QtThread.java
- src/org/qtproject/qt5/android/QtServiceDelegate.java # special case
+ src/org/qtproject/qt/android/QtAccessibilityDelegate.java
+ src/org/qtproject/qt/android/QtNativeAccessibility.java
+ src/org/qtproject/qt/android/CursorHandle.java
+ src/org/qtproject/qt/android/EditContextView.java
+ src/org/qtproject/qt/android/EditPopupMenu.java
+ src/org/qtproject/qt/android/ExtractStyle.java
+ src/org/qtproject/qt/android/QtApplicationBase.java
+ src/org/qtproject/qt/android/QtActivityBase.java
+ src/org/qtproject/qt/android/QtServiceBase.java
+ src/org/qtproject/qt/android/QtActivityDelegate.java
+ src/org/qtproject/qt/android/QtInputDelegate.java
+ src/org/qtproject/qt/android/QtLoader.java
+ src/org/qtproject/qt/android/QtActivityLoader.java
+ src/org/qtproject/qt/android/QtServiceLoader.java
+ src/org/qtproject/qt/android/QtEditText.java
+ src/org/qtproject/qt/android/QtInputConnection.java
+ src/org/qtproject/qt/android/QtLayout.java
+ src/org/qtproject/qt/android/QtMessageDialogHelper.java
+ src/org/qtproject/qt/android/QtNative.java
+ src/org/qtproject/qt/android/QtSurfaceInterface.java
+ src/org/qtproject/qt/android/QtSurface.java
+ src/org/qtproject/qt/android/QtTextureView.java
+ src/org/qtproject/qt/android/QtThread.java
+ src/org/qtproject/qt/android/extras/QtAndroidBinder.java
+ src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
+ src/org/qtproject/qt/android/extras/QtNative.java
+ src/org/qtproject/qt/android/QtClipboardManager.java
+ src/org/qtproject/qt/android/QtDisplayManager.java
+ src/org/qtproject/qt/android/UsedFromNativeCode.java
+ src/org/qtproject/qt/android/QtRootLayout.java
+ src/org/qtproject/qt/android/QtWindow.java
+ src/org/qtproject/qt/android/QtActivityDelegateBase.java
+ src/org/qtproject/qt/android/QtEmbeddedDelegate.java
+ src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java
+ src/org/qtproject/qt/android/QtEmbeddedLoader.java
+ src/org/qtproject/qt/android/QtView.java
)
-add_jar(QtAndroid
+qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android
INCLUDE_JARS ${QT_ANDROID_JAR}
SOURCES ${java_sources}
+ OUTPUT_DIR "${QT_BUILD_DIR}/jar"
)
-install_jar(QtAndroid
- DESTINATION jar
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
+install_jar(Qt${QtBase_VERSION_MAJOR}Android
+ DESTINATION ${destination}
COMPONENT Devel
)
-
diff --git a/src/android/jar/build.gradle b/src/android/jar/build.gradle
new file mode 100644
index 0000000000..452d4ad780
--- /dev/null
+++ b/src/android/jar/build.gradle
@@ -0,0 +1,52 @@
+// This is mainly used to allow Android Studio to easily read this folder as an android project.
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.0.2'
+ }
+}
+
+apply plugin: 'com.android.library'
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+}
+
+repositories {
+ google()
+ mavenCentral()
+}
+
+android {
+ compileSdk 34
+ namespace "org.qtproject.qt.android"
+
+ defaultConfig {
+ minSdkVersion 23
+ }
+
+ sourceSets {
+ main {
+ java.srcDir 'src/'
+ resources.srcDir 'libs/'
+ manifest.srcFile 'AndroidManifest.xml'
+ res.srcDirs = ['res/']
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ android {
+ lintOptions {
+ abortOnError true
+ }
+ }
+}
diff --git a/src/android/jar/jar.pro b/src/android/jar/jar.pro
deleted file mode 100644
index 24a83d56a1..0000000000
--- a/src/android/jar/jar.pro
+++ /dev/null
@@ -1,29 +0,0 @@
-TARGET = QtAndroid
-
-CONFIG += java
-
-DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar
-
-PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/
-
-JAVACLASSPATH += $$PWD/src/
-JAVASOURCES += \
- $$PATHPREFIX/accessibility/QtAccessibilityDelegate.java \
- $$PATHPREFIX/accessibility/QtNativeAccessibility.java \
- $$PATHPREFIX/QtActivityDelegate.java \
- $$PATHPREFIX/QtEditText.java \
- $$PATHPREFIX/QtInputConnection.java \
- $$PATHPREFIX/QtLayout.java \
- $$PATHPREFIX/QtMessageDialogHelper.java \
- $$PATHPREFIX/QtNative.java \
- $$PATHPREFIX/QtNativeLibrariesDir.java \
- $$PATHPREFIX/QtSurface.java \
- $$PATHPREFIX/ExtractStyle.java \
- $$PATHPREFIX/EditContextView.java \
- $$PATHPREFIX/EditPopupMenu.java \
- $$PATHPREFIX/CursorHandle.java \
- $$PATHPREFIX/QtThread.java
-
-# install
-target.path = $$[QT_INSTALL_PREFIX]/jar
-INSTALLS += target
diff --git a/src/android/jar/settings.gradle b/src/android/jar/settings.gradle
new file mode 100644
index 0000000000..3f935dd3e2
--- /dev/null
+++ b/src/android/jar/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "QtAndroid"
diff --git a/src/android/jar/src/org/qtproject/qt/android/CursorHandle.java b/src/android/jar/src/org/qtproject/qt/android/CursorHandle.java
new file mode 100644
index 0000000000..7e601c0551
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/CursorHandle.java
@@ -0,0 +1,199 @@
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+
+/* This view represents one of the handle (selection or cursor handle) */
+@SuppressLint("ViewConstructor")
+class CursorView extends ImageView
+{
+ private final CursorHandle mHandle;
+ // The coordinate which where clicked
+ private float m_offsetX;
+ private float m_offsetY;
+ private boolean m_pressed = false;
+
+ CursorView (Context context, CursorHandle handle) {
+ super(context);
+ mHandle = handle;
+ }
+
+ // Called when the handle was moved programmatically , with the delta amount in pixels
+ public void adjusted(int dx, int dy) {
+ m_offsetX += dx;
+ m_offsetY += dy;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ m_offsetX = ev.getRawX();
+ m_offsetY = ev.getRawY() + (float) getHeight() / 2;
+ m_pressed = true;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ if (!m_pressed)
+ return false;
+ mHandle.updatePosition(Math.round(ev.getRawX() - m_offsetX),
+ Math.round(ev.getRawY() - m_offsetY));
+ break;
+ }
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ m_pressed = false;
+ break;
+ }
+ return true;
+ }
+}
+
+// Helper class that manages a cursor or selection handle
+class CursorHandle implements ViewTreeObserver.OnPreDrawListener
+{
+ private static final String QtTag = "QtCursorHandle";
+ private final View m_layout;
+ private CursorView m_cursorView = null;
+ private PopupWindow m_popup = null;
+ private final int m_id;
+ private final int m_attr;
+ private final Activity m_activity;
+ private int m_posX = 0;
+ private int m_posY = 0;
+ private int m_lastX;
+ private int m_lastY;
+ int tolerance;
+ private final boolean m_rtl;
+ int m_yShift;
+
+ public CursorHandle(Activity activity, View layout, int id, int attr, boolean rtl) {
+ m_activity = activity;
+ m_id = id;
+ m_attr = attr;
+ m_layout = layout;
+ DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
+ m_yShift = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, metrics);
+ tolerance = Math.min(1, (int)(m_yShift / 2f));
+ m_lastX = m_lastY = -1 - tolerance;
+ m_rtl = rtl;
+ }
+
+ private void initOverlay(){
+ if (m_popup != null)
+ return;
+
+ Context context = m_layout.getContext();
+ int[] attrs = {m_attr};
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs);
+ Drawable drawable = a.getDrawable(0);
+
+ m_cursorView = new CursorView(context, this);
+ m_cursorView.setImageDrawable(drawable);
+
+ m_popup = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle);
+ m_popup.setSplitTouchEnabled(true);
+ m_popup.setClippingEnabled(false);
+ m_popup.setContentView(m_cursorView);
+ if (drawable != null) {
+ m_popup.setWidth(drawable.getIntrinsicWidth());
+ m_popup.setHeight(drawable.getIntrinsicHeight());
+ } else {
+ Log.w(QtTag, "initOverlay(): cannot get width/height for popup " +
+ "from null drawable for attribute " + m_attr);
+ }
+
+ m_layout.getViewTreeObserver().addOnPreDrawListener(this);
+ }
+
+ // Show the handle at a given position (or move it if it is already shown)
+ public void setPosition(final int x, final int y){
+ initOverlay();
+
+ final int[] layoutLocation = new int[2];
+ m_layout.getLocationOnScreen(layoutLocation);
+
+ // These values are used for handling split screen case
+ final int[] activityLocation = new int[2];
+ final int[] activityLocationInWindow = new int[2];
+ m_activity.getWindow().getDecorView().getLocationOnScreen(activityLocation);
+ m_activity.getWindow().getDecorView().getLocationInWindow(activityLocationInWindow);
+
+ int x2 = x + layoutLocation[0] - activityLocation[0];
+ int y2 = y + layoutLocation[1] + m_yShift + (activityLocationInWindow[1] - activityLocation[1]);
+
+ if (m_id == QtInputDelegate.IdCursorHandle) {
+ x2 -= m_popup.getWidth() / 2 ;
+ } else if ((m_id == QtInputDelegate.IdLeftHandle && !m_rtl) || (m_id == QtInputDelegate.IdRightHandle && m_rtl)) {
+ x2 -= m_popup.getWidth() * 3 / 4;
+ } else {
+ x2 -= m_popup.getWidth() / 4;
+ }
+
+ if (m_popup.isShowing()) {
+ m_popup.update(x2, y2, -1, -1);
+ m_cursorView.adjusted(x - m_posX, y - m_posY);
+ } else {
+ m_popup.showAtLocation(m_layout, 0, x2, y2);
+ }
+
+ m_posX = x;
+ m_posY = y;
+ }
+
+ public int bottom()
+ {
+ initOverlay();
+ final int[] location = new int[2];
+ m_cursorView.getLocationOnScreen(location);
+ return location[1] + m_cursorView.getHeight();
+ }
+
+ public void hide() {
+ if (m_popup != null) {
+ m_popup.dismiss();
+ }
+ }
+
+ public int width()
+ {
+ return m_cursorView.getDrawable().getIntrinsicWidth();
+ }
+
+ // The handle was dragged by a given relative position
+ public void updatePosition(int x, int y) {
+ y -= m_yShift;
+ if (Math.abs(m_lastX - x) > tolerance || Math.abs(m_lastY - y) > tolerance) {
+ QtInputDelegate.handleLocationChanged(m_id, x + m_posX, y + m_posY);
+ m_lastX = x;
+ m_lastY = y;
+ }
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ // This hook is called when the view location is changed
+ // For example if the keyboard appears.
+ // Adjust the position of the handle accordingly
+ if (m_popup != null && m_popup.isShowing())
+ setPosition(m_posX, m_posY);
+
+ return true;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/EditContextView.java b/src/android/jar/src/org/qtproject/qt/android/EditContextView.java
new file mode 100644
index 0000000000..fbd32ed98b
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/EditContextView.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2018 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Point;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+@SuppressLint("ViewConstructor")
+class EditContextView extends LinearLayout implements View.OnClickListener
+{
+ public static final int CUT_BUTTON = 1;
+ public static final int COPY_BUTTON = 1 << 1;
+ public static final int PASTE_BUTTON = 1 << 2;
+ public static final int SELECT_ALL_BUTTON = 1 << 3;
+
+ HashMap<Integer, ContextButton> m_buttons = new HashMap<>(4);
+ OnClickListener m_onClickListener;
+
+ public interface OnClickListener
+ {
+ void contextButtonClicked(int buttonId);
+ }
+
+ private class ContextButton extends TextView
+ {
+ public int m_buttonId;
+ public ContextButton(Context context, int stringId) {
+ super(context);
+ m_buttonId = stringId;
+ setText(stringId);
+ setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 1));
+ setGravity(Gravity.CENTER);
+ setTextColor(getResources().getColor(
+ android.R.color.widget_edittext_dark, context.getTheme()));
+ EditContextView.this.setBackground(getResources().getDrawable(
+ android.R.drawable.editbox_background_normal, context.getTheme()));
+ float scale = getResources().getDisplayMetrics().density;
+ int hPadding = (int)(16 * scale + 0.5f);
+ int vPadding = (int)(8 * scale + 0.5f);
+ setPadding(hPadding, vPadding, hPadding, vPadding);
+ setSingleLine();
+ setEllipsize(TextUtils.TruncateAt.END);
+ setOnClickListener(EditContextView.this);
+ }
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ ContextButton button = (ContextButton)v;
+ m_onClickListener.contextButtonClicked(button.m_buttonId);
+ }
+
+ void addButton(int id)
+ {
+ ContextButton button = new ContextButton(getContext(), id);
+ m_buttons.put(id, button);
+ addView(button);
+ }
+
+ public void updateButtons(int buttonsLayout)
+ {
+ ContextButton button = m_buttons.get(android.R.string.cut);
+ if (button != null)
+ button.setVisibility((buttonsLayout & CUT_BUTTON) != 0 ? View.VISIBLE : View.GONE);
+
+ button = m_buttons.get(android.R.string.copy);
+ if (button != null)
+ button.setVisibility((buttonsLayout & COPY_BUTTON) != 0 ? View.VISIBLE : View.GONE);
+
+ button = m_buttons.get(android.R.string.paste);
+ if (button != null)
+ button.setVisibility((buttonsLayout & PASTE_BUTTON) != 0 ? View.VISIBLE : View.GONE);
+
+ button = m_buttons.get(android.R.string.selectAll);
+ if (button != null)
+ button.setVisibility((buttonsLayout & SELECT_ALL_BUTTON) != 0 ? View.VISIBLE : View.GONE);
+ }
+
+ public Point getCalculatedSize()
+ {
+ Point size = new Point(0, 0);
+ for (ContextButton b : m_buttons.values()) {
+ if (b.getVisibility() == View.VISIBLE) {
+ b.measure(0, 0);
+ size.x += b.getMeasuredWidth();
+ size.y = Math.max(size.y, b.getMeasuredHeight());
+ }
+ }
+
+ size.x += getPaddingLeft() + getPaddingRight();
+ size.y += getPaddingTop() + getPaddingBottom();
+
+ return size;
+ }
+
+ public EditContextView(Context context, OnClickListener onClickListener) {
+ super(context);
+ m_onClickListener = onClickListener;
+ setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ addButton(android.R.string.cut);
+ addButton(android.R.string.copy);
+ addButton(android.R.string.paste);
+ addButton(android.R.string.selectAll);
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/EditPopupMenu.java b/src/android/jar/src/org/qtproject/qt/android/EditPopupMenu.java
index 18a8b36273..25be522c48 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/EditPopupMenu.java
+++ b/src/android/jar/src/org/qtproject/qt/android/EditPopupMenu.java
@@ -1,70 +1,25 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
+// Copyright (C) 2018 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+package org.qtproject.qt.android;
+
+import android.app.Activity;
import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
+import android.graphics.Point;
import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.ImageView;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.view.MotionEvent;
-import android.widget.PopupWindow;
-import android.app.Activity;
-import android.view.ViewTreeObserver;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
-import android.R;
+import android.view.ViewTreeObserver;
+import android.widget.PopupWindow;
// Helper class that manages a cursor or selection handle
-public class EditPopupMenu implements ViewTreeObserver.OnPreDrawListener, View.OnLayoutChangeListener,
+class EditPopupMenu implements ViewTreeObserver.OnPreDrawListener, View.OnLayoutChangeListener,
EditContextView.OnClickListener
{
- private View m_layout = null;
- private EditContextView m_view = null;
+ private final View m_layout;
+ private final EditContextView m_view;
private PopupWindow m_popup = null;
+ private final Activity m_activity;
private int m_posX;
private int m_posY;
private int m_buttons;
@@ -74,6 +29,7 @@ public class EditPopupMenu implements ViewTreeObserver.OnPreDrawListener, View.O
public EditPopupMenu(Activity activity, View layout)
{
+ m_activity = activity;
m_view = new EditContextView(activity, this);
m_view.addOnLayoutChangeListener(this);
@@ -103,24 +59,35 @@ public class EditPopupMenu implements ViewTreeObserver.OnPreDrawListener, View.O
initOverlay();
m_view.updateButtons(buttons);
- final int[] location = new int[2];
- m_layout.getLocationOnScreen(location);
+ Point viewSize = m_view.getCalculatedSize();
+
+ final int[] layoutLocation = new int[2];
+ m_layout.getLocationOnScreen(layoutLocation);
+
+ // These values are used for handling split screen case
+ final int[] activityLocation = new int[2];
+ final int[] activityLocationInWindow = new int[2];
+ m_activity.getWindow().getDecorView().getLocationOnScreen(activityLocation);
+ m_activity.getWindow().getDecorView().getLocationInWindow(activityLocationInWindow);
- int x2 = x + location[0];
- int y2 = y + location[1];
+ int x2 = x + layoutLocation[0] - activityLocation[0];
+ int y2 = y + layoutLocation[1] + (activityLocationInWindow[1] - activityLocation[1]);
- x2 -= m_view.getWidth() / 2 ;
+ x2 -= viewSize.x / 2 ;
- y2 -= m_view.getHeight();
+ y2 -= viewSize.y;
if (y2 < 0) {
- if (cursorHandle != null)
+ if (cursorHandle != null) {
y2 = cursorHandle.bottom();
- else if (leftSelectionHandle != null && rightSelectionHandle != null)
+ } else if (leftSelectionHandle != null && rightSelectionHandle != null) {
y2 = Math.max(leftSelectionHandle.bottom(), rightSelectionHandle.bottom());
+ if (y2 <= 0)
+ m_layout.requestLayout();
+ }
}
- if (m_layout.getWidth() < x + m_view.getWidth() / 2)
- x2 = m_layout.getWidth() - m_view.getWidth();
+ if (m_layout.getWidth() < x + viewSize.x / 2)
+ x2 = m_layout.getWidth() - viewSize.x;
if (x2 < 0)
x2 = 0;
@@ -168,16 +135,16 @@ public class EditPopupMenu implements ViewTreeObserver.OnPreDrawListener, View.O
@Override
public void contextButtonClicked(int buttonId) {
switch (buttonId) {
- case R.string.cut:
+ case android.R.string.cut:
QtNativeInputConnection.cut();
break;
- case R.string.copy:
+ case android.R.string.copy:
QtNativeInputConnection.copy();
break;
- case R.string.paste:
+ case android.R.string.paste:
QtNativeInputConnection.paste();
break;
- case R.string.selectAll:
+ case android.R.string.selectAll:
QtNativeInputConnection.selectAll();
break;
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java b/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java
new file mode 100644
index 0000000000..6780634317
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java
@@ -0,0 +1,1858 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedStateListDrawable;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ClipDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.RotateDrawable;
+import android.graphics.drawable.ScaleDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.ContextThemeWrapper;
+import android.view.inputmethod.EditorInfo;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+
+class ExtractStyle {
+
+ // This used to be retrieved from android.R.styleable.ViewDrawableStates field via reflection,
+ // but since the access to that is restricted, we need to have hard-coded here.
+ final int[] viewDrawableStatesState = new int[]{
+ android.R.attr.state_focused,
+ android.R.attr.state_window_focused,
+ android.R.attr.state_enabled,
+ android.R.attr.state_selected,
+ android.R.attr.state_pressed,
+ android.R.attr.state_activated,
+ android.R.attr.state_accelerated,
+ android.R.attr.state_hovered,
+ android.R.attr.state_drag_can_accept,
+ android.R.attr.state_drag_hovered
+ };
+ final int[] EMPTY_STATE_SET = {};
+ final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled};
+ final int[] FOCUSED_STATE_SET = {android.R.attr.state_focused};
+ final int[] SELECTED_STATE_SET = {android.R.attr.state_selected};
+ final int[] PRESSED_STATE_SET = {android.R.attr.state_pressed};
+ final int[] WINDOW_FOCUSED_STATE_SET = {android.R.attr.state_window_focused};
+ final int[] ENABLED_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET);
+ final int[] ENABLED_SELECTED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET);
+ final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] FOCUSED_SELECTED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, SELECTED_STATE_SET);
+ final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+ final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, SELECTED_STATE_SET);
+ final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, FOCUSED_STATE_SET);
+ final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+ final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_ENABLED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, ENABLED_STATE_SET);
+ final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_ENABLED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, SELECTED_STATE_SET);
+ final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, FOCUSED_STATE_SET);
+ final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+ final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+ final Resources.Theme m_theme;
+ final String m_extractPath;
+ final int defaultBackgroundColor;
+ final int defaultTextColor;
+ final boolean m_minimal;
+ final int[] DrawableStates = { android.R.attr.state_active, android.R.attr.state_checked,
+ android.R.attr.state_enabled, android.R.attr.state_focused,
+ android.R.attr.state_pressed, android.R.attr.state_selected,
+ android.R.attr.state_window_focused, 16908288, android.R.attr.state_multiline,
+ android.R.attr.state_activated, android.R.attr.state_accelerated};
+ final String[] DrawableStatesLabels = {"active", "checked", "enabled", "focused", "pressed",
+ "selected", "window_focused", "background", "multiline", "activated", "accelerated"};
+ final String[] DisableDrawableStatesLabels = {"inactive", "unchecked", "disabled",
+ "not_focused", "no_pressed", "unselected", "window_not_focused", "background",
+ "multiline", "activated", "accelerated"};
+ final String[] sScaleTypeArray = {
+ "MATRIX",
+ "FIT_XY",
+ "FIT_START",
+ "FIT_CENTER",
+ "FIT_END",
+ "CENTER",
+ "CENTER_CROP",
+ "CENTER_INSIDE"
+ };
+ Context m_context;
+ private final HashMap<String, DrawableCache> m_drawableCache = new HashMap<>();
+
+ private static boolean m_missingNormalStyle = false;
+ private static boolean m_missingDarkStyle = false;
+ private static String m_stylePath = null;
+ private static boolean m_extractMinimal = false;
+
+ private static final String QtTAG = "QtExtractStyle";
+
+ private static boolean isUiModeDark(Configuration config)
+ {
+ return (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ public static String setup(Context context, String extractOption, int dpi) {
+
+ String dataDir = context.getApplicationInfo().dataDir;
+ m_stylePath = dataDir + "/qt-reserved-files/android-style/" + dpi + "/";
+
+ if (extractOption.equals("none"))
+ return m_stylePath;
+
+ if (extractOption.isEmpty())
+ extractOption = "minimal";
+
+ if (!extractOption.equals("default") && !extractOption.equals("full")
+ && !extractOption.equals("minimal") && !extractOption.equals("none")) {
+ Log.e(QtTAG, "Invalid extract_android_style option \"" + extractOption
+ + "\", defaulting to \"minimal\"");
+ extractOption = "minimal";
+ }
+
+ // QTBUG-69810: The extraction code will trigger compatibility warnings on Android
+ // SDK version >= 28 when the target SDK version is set to something lower then 28,
+ // so default to "none" and issue a warning if that is the case.
+ if (extractOption.equals("default")) {
+ int targetSdk = context.getApplicationInfo().targetSdkVersion;
+ if (targetSdk < 28 && Build.VERSION.SDK_INT >= 28) {
+ Log.e(QtTAG, "extract_android_style option set to \"none\" when " +
+ "targetSdkVersion is less then 28");
+ extractOption = "none";
+ }
+ }
+
+ boolean darkModeFileMissing = !(new File(m_stylePath + "darkUiMode/style.json").exists());
+ m_missingDarkStyle = Build.VERSION.SDK_INT > 28 && darkModeFileMissing;
+ m_missingNormalStyle = !(new File(m_stylePath + "style.json").exists());
+ m_extractMinimal = extractOption.equals("minimal");
+
+ ExtractStyle.runIfNeeded(context, isUiModeDark(context.getResources().getConfiguration()));
+
+ return m_stylePath;
+ }
+
+ public static void runIfNeeded(Context context, boolean extractDarkMode) {
+ if (m_stylePath == null)
+ return;
+ if (extractDarkMode) {
+ if (m_missingDarkStyle) {
+ new ExtractStyle(context, m_stylePath + "darkUiMode/", m_extractMinimal);
+ m_missingDarkStyle = false;
+ }
+ } else if (m_missingNormalStyle) {
+ new ExtractStyle(context, m_stylePath, m_extractMinimal);
+ m_missingNormalStyle = false;
+ }
+ }
+
+ public ExtractStyle(Context context, String extractPath, boolean minimal) {
+ m_minimal = minimal;
+ m_extractPath = extractPath + "/";
+ boolean dirCreated = new File(m_extractPath).mkdirs();
+ if (!dirCreated)
+ Log.w(QtNative.QtTAG, "Cannot create Android style directory.");
+ m_context = context;
+ m_theme = context.getTheme();
+ TypedArray array = m_theme.obtainStyledAttributes(new int[]{
+ android.R.attr.colorBackground,
+ android.R.attr.textColorPrimary,
+ android.R.attr.textColor
+ });
+ defaultBackgroundColor = array.getColor(0, 0);
+ int textColor = array.getColor(1, 0xFFFFFF);
+ if (textColor == 0xFFFFFF)
+ textColor = array.getColor(2, 0xFFFFFF);
+ defaultTextColor = textColor;
+ array.recycle();
+
+ try {
+ SimpleJsonWriter jsonWriter = new SimpleJsonWriter(m_extractPath + "style.json");
+ jsonWriter.beginObject();
+ try {
+ jsonWriter.name("defaultStyle").value(extractDefaultPalette());
+ extractWindow(jsonWriter);
+ jsonWriter.name("buttonStyle").value(extractTextAppearanceInformation(android.R.attr.buttonStyle, "QPushButton"));
+ jsonWriter.name("spinnerStyle").value(extractTextAppearanceInformation(android.R.attr.spinnerStyle, "QComboBox"));
+ extractProgressBar(jsonWriter, android.R.attr.progressBarStyleHorizontal, "progressBarStyleHorizontal", "QProgressBar");
+ extractProgressBar(jsonWriter, android.R.attr.progressBarStyleLarge, "progressBarStyleLarge", null);
+ extractProgressBar(jsonWriter, android.R.attr.progressBarStyleSmall, "progressBarStyleSmall", null);
+ extractProgressBar(jsonWriter, android.R.attr.progressBarStyle, "progressBarStyle", null);
+ extractAbsSeekBar(jsonWriter);
+ extractSwitch(jsonWriter);
+ extractCompoundButton(jsonWriter, android.R.attr.checkboxStyle, "checkboxStyle", "QCheckBox");
+ jsonWriter.name("editTextStyle").value(extractTextAppearanceInformation(android.R.attr.editTextStyle, "QLineEdit"));
+ extractCompoundButton(jsonWriter, android.R.attr.radioButtonStyle, "radioButtonStyle", "QRadioButton");
+ jsonWriter.name("textViewStyle").value(extractTextAppearanceInformation(android.R.attr.textViewStyle, "QWidget"));
+ jsonWriter.name("scrollViewStyle").value(extractTextAppearanceInformation(android.R.attr.scrollViewStyle, "QAbstractScrollArea"));
+ extractListView(jsonWriter);
+ jsonWriter.name("listSeparatorTextViewStyle").value(extractTextAppearanceInformation(android.R.attr.listSeparatorTextViewStyle, null));
+ extractItemsStyle(jsonWriter);
+ extractCompoundButton(jsonWriter, android.R.attr.buttonStyleToggle, "buttonStyleToggle", null);
+ extractCalendar(jsonWriter);
+ extractToolBar(jsonWriter);
+ jsonWriter.name("actionButtonStyle").value(extractTextAppearanceInformation(android.R.attr.actionButtonStyle, "QToolButton"));
+ jsonWriter.name("actionBarTabTextStyle").value(extractTextAppearanceInformation(android.R.attr.actionBarTabTextStyle, null));
+ jsonWriter.name("actionBarTabStyle").value(extractTextAppearanceInformation(android.R.attr.actionBarTabStyle, null));
+ jsonWriter.name("actionOverflowButtonStyle").value(extractImageViewInformation(android.R.attr.actionOverflowButtonStyle, null));
+ extractTabBar(jsonWriter);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ jsonWriter.endObject();
+ jsonWriter.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ native static int[] extractNativeChunkInfo20(long nativeChunk);
+
+ private int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) {
+ try {
+ final int stateSet1Length = stateSet1.length;
+ final int stateSet2Length = stateSet2.length;
+ final int[] newSet = new int[stateSet1Length + stateSet2Length];
+ int k = 0;
+ int i = 0;
+ int j = 0;
+ // This is a merge of the two input state sets and assumes that the
+ // input sets are sorted by the order imposed by ViewDrawableStates.
+ for (int viewState : viewDrawableStatesState) {
+ if (i < stateSet1Length && stateSet1[i] == viewState) {
+ newSet[k++] = viewState;
+ i++;
+ } else if (j < stateSet2Length && stateSet2[j] == viewState) {
+ newSet[k++] = viewState;
+ j++;
+ }
+ assert k <= 1 || (newSet[k - 1] > newSet[k - 2]);
+ }
+ return newSet;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ Field getAccessibleField(Class<?> clazz, String fieldName) {
+ try {
+ Field f = clazz.getDeclaredField(fieldName);
+ f.setAccessible(true);
+ return f;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ Field tryGetAccessibleField(Class<?> clazz, String fieldName) {
+ if (clazz == null)
+ return null;
+
+ try {
+ Field f = clazz.getDeclaredField(fieldName);
+ f.setAccessible(true);
+ return f;
+ } catch (Exception e) {
+ for (Class<?> c : clazz.getInterfaces()) {
+ Field f = tryGetAccessibleField(c, fieldName);
+ if (f != null)
+ return f;
+ }
+ }
+ return tryGetAccessibleField(clazz.getSuperclass(), fieldName);
+ }
+
+ JSONObject getColorStateList(ColorStateList colorList) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("EMPTY_STATE_SET", colorList.getColorForState(EMPTY_STATE_SET, 0));
+ json.put("WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("SELECTED_STATE_SET", colorList.getColorForState(SELECTED_STATE_SET, 0));
+ json.put("SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_STATE_SET, 0));
+ json.put("FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_STATE_SET, 0));
+ json.put("FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("ENABLED_STATE_SET", colorList.getColorForState(ENABLED_STATE_SET, 0));
+ json.put("ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("ENABLED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_STATE_SET, 0));
+ json.put("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_STATE_SET, 0));
+ json.put("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_STATE_SET, 0));
+ json.put("ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_STATE_SET", colorList.getColorForState(PRESSED_STATE_SET, 0));
+ json.put("PRESSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_STATE_SET, 0));
+ json.put("PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_STATE_SET, 0));
+ json.put("PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, 0));
+ json.put("PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ return json;
+ }
+
+ JSONObject getStatesList(int[] states) throws JSONException {
+ JSONObject json = new JSONObject();
+ for (int s : states) {
+ boolean found = false;
+ for (int d = 0; d < DrawableStates.length; d++) {
+ if (s == DrawableStates[d]) {
+ json.put(DrawableStatesLabels[d], true);
+ found = true;
+ break;
+ } else if (s == -DrawableStates[d]) {
+ json.put(DrawableStatesLabels[d], false);
+
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ json.put("unhandled_state_" + s, s > 0);
+ }
+ }
+ return json;
+ }
+
+ String getStatesName(int[] states) {
+ StringBuilder statesName = new StringBuilder();
+ for (int s : states) {
+ boolean found = false;
+ for (int d = 0; d < DrawableStates.length; d++) {
+ if (s == DrawableStates[d]) {
+ if (statesName.length() > 0)
+ statesName.append("__");
+ statesName.append(DrawableStatesLabels[d]);
+ found = true;
+ break;
+ } else if (s == -DrawableStates[d]) {
+ if (statesName.length() > 0)
+ statesName.append("__");
+ statesName.append(DisableDrawableStatesLabels[d]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (statesName.length() > 0)
+ statesName.append(";");
+ statesName.append(s);
+ }
+ }
+ if (statesName.length() > 0)
+ return statesName.toString();
+ return "empty";
+ }
+
+ private JSONObject getLayerDrawable(Object drawable, String filename) {
+ JSONObject json = new JSONObject();
+ LayerDrawable layers = (LayerDrawable) drawable;
+ final int nr = layers.getNumberOfLayers();
+ try {
+ JSONArray array = new JSONArray();
+ for (int i = 0; i < nr; i++) {
+ int id = layers.getId(i);
+ if (id == -1)
+ id = i;
+ JSONObject layerJsonObject = getDrawable(layers.getDrawable(i), filename + "__" + id, null);
+ layerJsonObject.put("id", id);
+ array.put(layerJsonObject);
+ }
+ json.put("type", "layer");
+ Rect padding = new Rect();
+ if (layers.getPadding(padding))
+ json.put("padding", getJsonRect(padding));
+ json.put("layers", array);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getStateListDrawable(Object drawable, String filename) {
+ JSONObject json = new JSONObject();
+ try {
+ StateListDrawable stateList = (StateListDrawable) drawable;
+ JSONArray array = new JSONArray();
+ final int numStates;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
+ numStates = (Integer) StateListDrawable.class.getMethod("getStateCount").invoke(stateList);
+ else
+ numStates = stateList.getStateCount();
+ for (int i = 0; i < numStates; i++) {
+ JSONObject stateJson = new JSONObject();
+ final Drawable d = (Drawable) StateListDrawable.class.getMethod("getStateDrawable", Integer.TYPE).invoke(stateList, i);
+ final int[] states = (int[]) StateListDrawable.class.getMethod("getStateSet", Integer.TYPE).invoke(stateList, i);
+ if (states != null)
+ stateJson.put("states", getStatesList(states));
+ stateJson.put("drawable", getDrawable(d, filename + "__" + (states != null ? getStatesName(states) : ("state_pos_" + i)), null));
+ array.put(stateJson);
+ }
+ json.put("type", "stateslist");
+ Rect padding = new Rect();
+ if (stateList.getPadding(padding))
+ json.put("padding", getJsonRect(padding));
+ json.put("stateslist", array);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getGradientDrawable(GradientDrawable drawable) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("type", "gradient");
+ Object obj = drawable.getConstantState();
+ Class<?> gradientStateClass = obj.getClass();
+ json.put("shape", gradientStateClass.getField("mShape").getInt(obj));
+ json.put("gradient", gradientStateClass.getField("mGradient").getInt(obj));
+ GradientDrawable.Orientation orientation = (Orientation) gradientStateClass.getField("mOrientation").get(obj);
+ if (orientation != null)
+ json.put("orientation", orientation.name());
+ int[] intArray = (int[]) gradientStateClass.getField("mGradientColors").get(obj);
+ if (intArray != null)
+ json.put("colors", getJsonArray(intArray, 0, intArray.length));
+ json.put("positions", getJsonArray((float[]) gradientStateClass.getField("mPositions").get(obj)));
+ json.put("strokeWidth", gradientStateClass.getField("mStrokeWidth").getInt(obj));
+ json.put("strokeDashWidth", gradientStateClass.getField("mStrokeDashWidth").getFloat(obj));
+ json.put("strokeDashGap", gradientStateClass.getField("mStrokeDashGap").getFloat(obj));
+ json.put("radius", gradientStateClass.getField("mRadius").getFloat(obj));
+ float[] floatArray = (float[]) gradientStateClass.getField("mRadiusArray").get(obj);
+ if (floatArray != null)
+ json.put("radiusArray", getJsonArray(floatArray));
+ Rect rc = (Rect) gradientStateClass.getField("mPadding").get(obj);
+ if (rc != null)
+ json.put("padding", getJsonRect(rc));
+ json.put("width", gradientStateClass.getField("mWidth").getInt(obj));
+ json.put("height", gradientStateClass.getField("mHeight").getInt(obj));
+ json.put("innerRadiusRatio", gradientStateClass.getField("mInnerRadiusRatio").getFloat(obj));
+ json.put("thicknessRatio", gradientStateClass.getField("mThicknessRatio").getFloat(obj));
+ json.put("innerRadius", gradientStateClass.getField("mInnerRadius").getInt(obj));
+ json.put("thickness", gradientStateClass.getField("mThickness").getInt(obj));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getRotateDrawable(RotateDrawable drawable, String filename) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("type", "rotate");
+ Object obj = drawable.getConstantState();
+ Class<?> rotateStateClass = obj.getClass();
+ json.put("drawable", getDrawable(drawable.getClass().getMethod("getDrawable").invoke(drawable), filename, null));
+ json.put("pivotX", getAccessibleField(rotateStateClass, "mPivotX").getFloat(obj));
+ json.put("pivotXRel", getAccessibleField(rotateStateClass, "mPivotXRel").getBoolean(obj));
+ json.put("pivotY", getAccessibleField(rotateStateClass, "mPivotY").getFloat(obj));
+ json.put("pivotYRel", getAccessibleField(rotateStateClass, "mPivotYRel").getBoolean(obj));
+ json.put("fromDegrees", getAccessibleField(rotateStateClass, "mFromDegrees").getFloat(obj));
+ json.put("toDegrees", getAccessibleField(rotateStateClass, "mToDegrees").getFloat(obj));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getAnimationDrawable(AnimationDrawable drawable, String filename) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("type", "animation");
+ json.put("oneshot", drawable.isOneShot());
+ final int count = drawable.getNumberOfFrames();
+ JSONArray frames = new JSONArray();
+ for (int i = 0; i < count; ++i) {
+ JSONObject frame = new JSONObject();
+ frame.put("duration", drawable.getDuration(i));
+ frame.put("drawable", getDrawable(drawable.getFrame(i), filename + "__" + i, null));
+ frames.put(frame);
+ }
+ json.put("frames", frames);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getJsonRect(Rect rect) throws JSONException {
+ JSONObject jsonRect = new JSONObject();
+ jsonRect.put("left", rect.left);
+ jsonRect.put("top", rect.top);
+ jsonRect.put("right", rect.right);
+ jsonRect.put("bottom", rect.bottom);
+ return jsonRect;
+
+ }
+
+ private JSONArray getJsonArray(int[] array, int pos, int len) {
+ JSONArray a = new JSONArray();
+ final int l = pos + len;
+ for (int i = pos; i < l; i++)
+ a.put(array[i]);
+ return a;
+ }
+
+ private JSONArray getJsonArray(float[] array) throws JSONException {
+ JSONArray a = new JSONArray();
+ if (array != null)
+ for (float val : array)
+ a.put(val);
+ return a;
+ }
+
+ private JSONObject getJsonChunkInfo(int[] chunkData) throws JSONException {
+ JSONObject jsonRect = new JSONObject();
+ if (chunkData == null)
+ return jsonRect;
+
+ jsonRect.put("xdivs", getJsonArray(chunkData, 3, chunkData[0]));
+ jsonRect.put("ydivs", getJsonArray(chunkData, 3 + chunkData[0], chunkData[1]));
+ jsonRect.put("colors", getJsonArray(chunkData, 3 + chunkData[0] + chunkData[1], chunkData[2]));
+ return jsonRect;
+ }
+
+ private JSONObject findPatchesMarings(Drawable d) throws JSONException, IllegalAccessException {
+ NinePatch np;
+ Field f = tryGetAccessibleField(NinePatchDrawable.class, "mNinePatch");
+ if (f != null) {
+ np = (NinePatch) f.get(d);
+ } else {
+ Object state = getAccessibleField(NinePatchDrawable.class, "mNinePatchState").get(d);
+ np = (NinePatch) getAccessibleField(Objects.requireNonNull(state).getClass(), "mNinePatch").get(state);
+ }
+ return getJsonChunkInfo(extractNativeChunkInfo20(getAccessibleField(Objects.requireNonNull(np).getClass(), "mNativeChunk").getLong(np)));
+ }
+
+ private JSONObject getRippleDrawable(Object drawable, String filename, Rect padding) {
+ JSONObject json = getLayerDrawable(drawable, filename);
+ JSONObject ripple = new JSONObject();
+ try {
+ Class<?> rippleDrawableClass = Class.forName("android.graphics.drawable.RippleDrawable");
+ final Object mState = getAccessibleField(rippleDrawableClass, "mState").get(drawable);
+ ripple.put("mask", getDrawable((Drawable) getAccessibleField(rippleDrawableClass, "mMask").get(drawable), filename, padding));
+ if (mState != null) {
+ ripple.put("maxRadius", getAccessibleField(mState.getClass(), "mMaxRadius").getInt(mState));
+ ColorStateList color = (ColorStateList) getAccessibleField(mState.getClass(), "mColor").get(mState);
+ if (color != null)
+ ripple.put("color", getColorStateList(color));
+ }
+ json.put("ripple", ripple);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private HashMap<Long, Long> getStateTransitions(Object sa) throws Exception {
+ HashMap<Long, Long> transitions = new HashMap<>();
+ final int sz = getAccessibleField(sa.getClass(), "mSize").getInt(sa);
+ long[] keys = (long[]) getAccessibleField(sa.getClass(), "mKeys").get(sa);
+ long[] values = (long[]) getAccessibleField(sa.getClass(), "mValues").get(sa);
+ for (int i = 0; i < sz; i++) {
+ if (keys != null && values != null)
+ transitions.put(keys[i], values[i]);
+ }
+ return transitions;
+ }
+
+ private HashMap<Integer, Integer> getStateIds(Object sa) throws Exception {
+ HashMap<Integer, Integer> states = new HashMap<>();
+ final int sz = getAccessibleField(sa.getClass(), "mSize").getInt(sa);
+ int[] keys = (int[]) getAccessibleField(sa.getClass(), "mKeys").get(sa);
+ int[] values = (int[]) getAccessibleField(sa.getClass(), "mValues").get(sa);
+ for (int i = 0; i < sz; i++) {
+ if (keys != null && values != null)
+ states.put(keys[i], values[i]);
+ }
+ return states;
+ }
+
+ private int findStateIndex(int id, HashMap<Integer, Integer> stateIds) {
+ for (Map.Entry<Integer, Integer> s : stateIds.entrySet()) {
+ if (id == s.getValue())
+ return s.getKey();
+ }
+ return -1;
+ }
+
+ private JSONObject getAnimatedStateListDrawable(Object drawable, String filename) {
+ JSONObject json = getStateListDrawable(drawable, filename);
+ try {
+ Class<?> animatedStateListDrawableClass = Class.forName("android.graphics.drawable.AnimatedStateListDrawable");
+ Object state = getAccessibleField(animatedStateListDrawableClass, "mState").get(drawable);
+
+ if (state != null) {
+ Class<?> stateClass = state.getClass();
+ HashMap<Integer, Integer> stateIds = getStateIds(Objects.requireNonNull(getAccessibleField(stateClass, "mStateIds").get(state)));
+ HashMap<Long, Long> transitions = getStateTransitions(Objects.requireNonNull(getAccessibleField(stateClass, "mTransitions").get(state)));
+
+ for (Map.Entry<Long, Long> t : transitions.entrySet()) {
+ final int toState = findStateIndex(t.getKey().intValue(), stateIds);
+ final int fromState = findStateIndex((int) (t.getKey() >> 32), stateIds);
+
+ JSONObject transition = new JSONObject();
+ transition.put("from", fromState);
+ transition.put("to", toState);
+ transition.put("reverse", (t.getValue() >> 32) != 0);
+
+ JSONArray stateslist = json.getJSONArray("stateslist");
+ JSONObject stateobj = stateslist.getJSONObject(t.getValue().intValue());
+ stateobj.put("transition", transition);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject getVPath(Object path) throws Exception {
+ JSONObject json = new JSONObject();
+ final Class<?> pathClass = path.getClass();
+ json.put("type", "path");
+ json.put("name", tryGetAccessibleField(pathClass, "mPathName").get(path));
+ Object[] mNodes = (Object[]) tryGetAccessibleField(pathClass, "mNodes").get(path);
+ JSONArray nodes = new JSONArray();
+ if (mNodes != null) {
+ for (Object n : mNodes) {
+ JSONObject node = new JSONObject();
+ node.put("type", String.valueOf(getAccessibleField(n.getClass(), "mType").getChar(n)));
+ node.put("params", getJsonArray((float[]) getAccessibleField(n.getClass(), "mParams").get(n)));
+ nodes.put(node);
+ }
+ json.put("nodes", nodes);
+ }
+ json.put("isClip", (Boolean) pathClass.getMethod("isClipPath").invoke(path));
+
+ if (tryGetAccessibleField(pathClass, "mStrokeColor") == null)
+ return json; // not VFullPath
+
+ json.put("strokeColor", getAccessibleField(pathClass, "mStrokeColor").getInt(path));
+ json.put("strokeWidth", getAccessibleField(pathClass, "mStrokeWidth").getFloat(path));
+ json.put("fillColor", getAccessibleField(pathClass, "mFillColor").getInt(path));
+ json.put("strokeAlpha", getAccessibleField(pathClass, "mStrokeAlpha").getFloat(path));
+ json.put("fillRule", getAccessibleField(pathClass, "mFillRule").getInt(path));
+ json.put("fillAlpha", getAccessibleField(pathClass, "mFillAlpha").getFloat(path));
+ json.put("trimPathStart", getAccessibleField(pathClass, "mTrimPathStart").getFloat(path));
+ json.put("trimPathEnd", getAccessibleField(pathClass, "mTrimPathEnd").getFloat(path));
+ json.put("trimPathOffset", getAccessibleField(pathClass, "mTrimPathOffset").getFloat(path));
+ json.put("strokeLineCap", (Paint.Cap) getAccessibleField(pathClass, "mStrokeLineCap").get(path));
+ json.put("strokeLineJoin", (Paint.Join) getAccessibleField(pathClass, "mStrokeLineJoin").get(path));
+ json.put("strokeMiterlimit", getAccessibleField(pathClass, "mStrokeMiterlimit").getFloat(path));
+ return json;
+ }
+
+ @SuppressWarnings("unchecked")
+ private JSONObject getVGroup(Object group) throws Exception {
+ JSONObject json = new JSONObject();
+ json.put("type", "group");
+ final Class<?> groupClass = group.getClass();
+ json.put("name", getAccessibleField(groupClass, "mGroupName").get(group));
+ json.put("rotate", getAccessibleField(groupClass, "mRotate").getFloat(group));
+ json.put("pivotX", getAccessibleField(groupClass, "mPivotX").getFloat(group));
+ json.put("pivotY", getAccessibleField(groupClass, "mPivotY").getFloat(group));
+ json.put("scaleX", getAccessibleField(groupClass, "mScaleX").getFloat(group));
+ json.put("scaleY", getAccessibleField(groupClass, "mScaleY").getFloat(group));
+ json.put("translateX", getAccessibleField(groupClass, "mTranslateX").getFloat(group));
+ json.put("translateY", getAccessibleField(groupClass, "mTranslateY").getFloat(group));
+
+ ArrayList<Object> mChildren = (ArrayList<Object>) getAccessibleField(groupClass, "mChildren").get(group);
+ JSONArray children = new JSONArray();
+ if (mChildren != null) {
+ for (Object c : mChildren) {
+ if (groupClass.isInstance(c))
+ children.put(getVGroup(c));
+ else
+ children.put(getVPath(c));
+ }
+ json.put("children", children);
+ }
+ return json;
+ }
+
+ private JSONObject getVectorDrawable(Object drawable) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("type", "vector");
+ Class<?> vectorDrawableClass = Class.forName("android.graphics.drawable.VectorDrawable");
+ final Object state = getAccessibleField(vectorDrawableClass, "mVectorState").get(drawable);
+ final Class<?> stateClass = Objects.requireNonNull(state).getClass();
+ final ColorStateList mTint = (ColorStateList) getAccessibleField(stateClass, "mTint").get(state);
+ if (mTint != null) {
+ json.put("tintList", getColorStateList(mTint));
+ json.put("tintMode", (PorterDuff.Mode) getAccessibleField(stateClass, "mTintMode").get(state));
+ }
+ final Object mVPathRenderer = getAccessibleField(stateClass, "mVPathRenderer").get(state);
+ final Class<?> VPathRendererClass = Objects.requireNonNull(mVPathRenderer).getClass();
+ json.put("baseWidth", getAccessibleField(VPathRendererClass, "mBaseWidth").getFloat(mVPathRenderer));
+ json.put("baseHeight", getAccessibleField(VPathRendererClass, "mBaseHeight").getFloat(mVPathRenderer));
+ json.put("viewportWidth", getAccessibleField(VPathRendererClass, "mViewportWidth").getFloat(mVPathRenderer));
+ json.put("viewportHeight", getAccessibleField(VPathRendererClass, "mViewportHeight").getFloat(mVPathRenderer));
+ json.put("rootAlpha", getAccessibleField(VPathRendererClass, "mRootAlpha").getInt(mVPathRenderer));
+ json.put("rootName", getAccessibleField(VPathRendererClass, "mRootName").get(mVPathRenderer));
+ json.put("rootGroup", getVGroup(Objects.requireNonNull(getAccessibleField(VPathRendererClass, "mRootGroup").get(mVPathRenderer))));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ public JSONObject getDrawable(Object drawable, String filename, Rect padding) {
+ if (drawable == null || m_minimal)
+ return null;
+
+ DrawableCache dc = m_drawableCache.get(filename);
+ if (dc != null) {
+ if (dc.drawable.equals(drawable))
+ return dc.object;
+ else
+ Log.e(QtNative.QtTAG, "Different drawable objects points to the same file name \"" + filename + "\"");
+ }
+ JSONObject json = new JSONObject();
+ Bitmap bmp = null;
+ if (drawable instanceof Bitmap)
+ bmp = (Bitmap) drawable;
+ else {
+ if (drawable instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ bmp = bitmapDrawable.getBitmap();
+ try {
+ json.put("gravity", bitmapDrawable.getGravity());
+ json.put("tileModeX", bitmapDrawable.getTileModeX());
+ json.put("tileModeY", bitmapDrawable.getTileModeY());
+ json.put("antialias", (Boolean) BitmapDrawable.class.getMethod("hasAntiAlias").invoke(bitmapDrawable));
+ json.put("mipMap", (Boolean) BitmapDrawable.class.getMethod("hasMipMap").invoke(bitmapDrawable));
+ json.put("tintMode", (PorterDuff.Mode) BitmapDrawable.class.getMethod("getTintMode").invoke(bitmapDrawable));
+ ColorStateList tintList = (ColorStateList) BitmapDrawable.class.getMethod("getTint").invoke(bitmapDrawable);
+ if (tintList != null)
+ json.put("tintList", getColorStateList(tintList));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+
+ if (drawable instanceof RippleDrawable)
+ return getRippleDrawable(drawable, filename, padding);
+
+ if (drawable instanceof AnimatedStateListDrawable)
+ return getAnimatedStateListDrawable(drawable, filename);
+
+ if (drawable instanceof VectorDrawable)
+ return getVectorDrawable(drawable);
+
+ if (drawable instanceof ScaleDrawable) {
+ return getDrawable(((ScaleDrawable) drawable).getDrawable(), filename, null);
+ }
+ if (drawable instanceof LayerDrawable) {
+ return getLayerDrawable(drawable, filename);
+ }
+ if (drawable instanceof StateListDrawable) {
+ return getStateListDrawable(drawable, filename);
+ }
+ if (drawable instanceof GradientDrawable) {
+ return getGradientDrawable((GradientDrawable) drawable);
+ }
+ if (drawable instanceof RotateDrawable) {
+ return getRotateDrawable((RotateDrawable) drawable, filename);
+ }
+ if (drawable instanceof AnimationDrawable) {
+ return getAnimationDrawable((AnimationDrawable) drawable, filename);
+ }
+ if (drawable instanceof ClipDrawable) {
+ try {
+ json.put("type", "clipDrawable");
+ Drawable.ConstantState dcs = ((ClipDrawable) drawable).getConstantState();
+ json.put("drawable", getDrawable(getAccessibleField(dcs.getClass(), "mDrawable").get(dcs), filename, null));
+ if (null != padding)
+ json.put("padding", getJsonRect(padding));
+ else {
+ Rect _padding = new Rect();
+ if (((Drawable) drawable).getPadding(_padding))
+ json.put("padding", getJsonRect(_padding));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+ if (drawable instanceof ColorDrawable) {
+ bmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+ Drawable d = (Drawable) drawable;
+ d.setBounds(0, 0, 1, 1);
+ d.draw(new Canvas(bmp));
+ try {
+ json.put("type", "color");
+ json.put("color", bmp.getPixel(0, 0));
+ if (null != padding)
+ json.put("padding", getJsonRect(padding));
+ else {
+ Rect _padding = new Rect();
+ if (d.getPadding(_padding))
+ json.put("padding", getJsonRect(_padding));
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+ if (drawable instanceof InsetDrawable) {
+ try {
+ InsetDrawable d = (InsetDrawable) drawable;
+ Object mInsetStateObject = getAccessibleField(InsetDrawable.class, "mState").get(d);
+ Rect _padding = new Rect();
+ boolean hasPadding = d.getPadding(_padding);
+ return getDrawable(getAccessibleField(Objects.requireNonNull(mInsetStateObject).getClass(), "mDrawable").get(mInsetStateObject), filename, hasPadding ? _padding : null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ Drawable d = (Drawable) drawable;
+ int w = d.getIntrinsicWidth();
+ int h = d.getIntrinsicHeight();
+ d.setLevel(10000);
+ if (w < 1 || h < 1) {
+ w = 100;
+ h = 100;
+ }
+ bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);
+ d.setBounds(0, 0, w, h);
+ d.draw(new Canvas(bmp));
+ if (drawable instanceof NinePatchDrawable) {
+ NinePatchDrawable npd = (NinePatchDrawable) drawable;
+ try {
+ json.put("type", "9patch");
+ json.put("drawable", getDrawable(bmp, filename, null));
+ if (padding != null)
+ json.put("padding", getJsonRect(padding));
+ else {
+ Rect _padding = new Rect();
+ if (npd.getPadding(_padding))
+ json.put("padding", getJsonRect(_padding));
+ }
+
+ json.put("chunkInfo", findPatchesMarings(d));
+ return json;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ FileOutputStream out;
+ try {
+ filename = m_extractPath + filename + ".png";
+ out = new FileOutputStream(filename);
+ if (bmp != null)
+ bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ json.put("type", "image");
+ json.put("path", filename);
+ if (bmp != null) {
+ json.put("width", bmp.getWidth());
+ json.put("height", bmp.getHeight());
+ }
+ m_drawableCache.put(filename, new DrawableCache(json, drawable));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private TypedArray obtainStyledAttributes(int styleName, int[] attributes)
+ {
+ TypedValue typedValue = new TypedValue();
+ Context ctx = new ContextThemeWrapper(m_context, m_theme);
+ ctx.getTheme().resolveAttribute(styleName, typedValue, true);
+ return ctx.obtainStyledAttributes(typedValue.data, attributes);
+ }
+
+ private ArrayList<Integer> getArrayListFromIntArray(int[] attributes) {
+ ArrayList<Integer> sortedAttrs = new ArrayList<>();
+ for (int attr : attributes)
+ sortedAttrs.add(attr);
+ return sortedAttrs;
+ }
+
+ public void extractViewInformation(int styleName, JSONObject json, String qtClassName) {
+ extractViewInformation(styleName, json, qtClassName, null);
+ }
+
+ public void extractViewInformation(int styleName, JSONObject json, String qtClassName, AttributeSet attributeSet) {
+ try {
+ TypedValue typedValue = new TypedValue();
+ Context ctx = new ContextThemeWrapper(m_context, m_theme);
+ ctx.getTheme().resolveAttribute(styleName, typedValue, true);
+
+ int[] attributes = new int[]{
+ android.R.attr.digits,
+ android.R.attr.background,
+ android.R.attr.padding,
+ android.R.attr.paddingLeft,
+ android.R.attr.paddingTop,
+ android.R.attr.paddingRight,
+ android.R.attr.paddingBottom,
+ android.R.attr.scrollX,
+ android.R.attr.scrollY,
+ android.R.attr.id,
+ android.R.attr.tag,
+ android.R.attr.fitsSystemWindows,
+ android.R.attr.focusable,
+ android.R.attr.focusableInTouchMode,
+ android.R.attr.clickable,
+ android.R.attr.longClickable,
+ android.R.attr.saveEnabled,
+ android.R.attr.duplicateParentState,
+ android.R.attr.visibility,
+ android.R.attr.drawingCacheQuality,
+ android.R.attr.contentDescription,
+ android.R.attr.soundEffectsEnabled,
+ android.R.attr.hapticFeedbackEnabled,
+ android.R.attr.scrollbars,
+ android.R.attr.fadingEdge,
+ android.R.attr.scrollbarStyle,
+ android.R.attr.scrollbarFadeDuration,
+ android.R.attr.scrollbarDefaultDelayBeforeFade,
+ android.R.attr.scrollbarSize,
+ android.R.attr.scrollbarThumbHorizontal,
+ android.R.attr.scrollbarThumbVertical,
+ android.R.attr.scrollbarTrackHorizontal,
+ android.R.attr.scrollbarTrackVertical,
+ android.R.attr.isScrollContainer,
+ android.R.attr.keepScreenOn,
+ android.R.attr.filterTouchesWhenObscured,
+ android.R.attr.nextFocusLeft,
+ android.R.attr.nextFocusRight,
+ android.R.attr.nextFocusUp,
+ android.R.attr.nextFocusDown,
+ android.R.attr.minWidth,
+ android.R.attr.minHeight,
+ android.R.attr.onClick,
+ android.R.attr.overScrollMode,
+ android.R.attr.paddingStart,
+ android.R.attr.paddingEnd,
+ };
+
+ // The array must be sorted in ascending order, otherwise obtainStyledAttributes()
+ // might fail to find some attributes
+ Arrays.sort(attributes);
+ TypedArray array;
+ if (attributeSet != null)
+ array = m_theme.obtainStyledAttributes(attributeSet, attributes, styleName, 0);
+ else
+ array = obtainStyledAttributes(styleName, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ if (null != qtClassName)
+ json.put("qtClass", qtClassName);
+
+ json.put("defaultBackgroundColor", defaultBackgroundColor);
+ json.put("defaultTextColorPrimary", defaultTextColor);
+ json.put("TextView_digits", array.getText(sortedAttrs.indexOf(android.R.attr.digits)));
+ json.put("View_background", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.background)), styleName + "_View_background", null));
+ json.put("View_padding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.padding), -1));
+ json.put("View_paddingLeft", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingLeft), -1));
+ json.put("View_paddingTop", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingTop), -1));
+ json.put("View_paddingRight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingRight), -1));
+ json.put("View_paddingBottom", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingBottom), -1));
+ json.put("View_paddingBottom", array.getDimensionPixelOffset(sortedAttrs.indexOf(android.R.attr.scrollX), 0));
+ json.put("View_scrollY", array.getDimensionPixelOffset(sortedAttrs.indexOf(android.R.attr.scrollY), 0));
+ json.put("View_id", array.getResourceId(sortedAttrs.indexOf(android.R.attr.id), -1));
+ json.put("View_tag", array.getText(sortedAttrs.indexOf(android.R.attr.tag)));
+ json.put("View_fitsSystemWindows", array.getBoolean(sortedAttrs.indexOf(android.R.attr.fitsSystemWindows), false));
+ json.put("View_focusable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.focusable), false));
+ json.put("View_focusableInTouchMode", array.getBoolean(sortedAttrs.indexOf(android.R.attr.focusableInTouchMode), false));
+ json.put("View_clickable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.clickable), false));
+ json.put("View_longClickable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.longClickable), false));
+ json.put("View_saveEnabled", array.getBoolean(sortedAttrs.indexOf(android.R.attr.saveEnabled), true));
+ json.put("View_duplicateParentState", array.getBoolean(sortedAttrs.indexOf(android.R.attr.duplicateParentState), false));
+ json.put("View_visibility", array.getInt(sortedAttrs.indexOf(android.R.attr.visibility), 0));
+ json.put("View_drawingCacheQuality", array.getInt(sortedAttrs.indexOf(android.R.attr.drawingCacheQuality), 0));
+ json.put("View_contentDescription", array.getString(sortedAttrs.indexOf(android.R.attr.contentDescription)));
+ json.put("View_soundEffectsEnabled", array.getBoolean(sortedAttrs.indexOf(android.R.attr.soundEffectsEnabled), true));
+ json.put("View_hapticFeedbackEnabled", array.getBoolean(sortedAttrs.indexOf(android.R.attr.hapticFeedbackEnabled), true));
+ json.put("View_scrollbars", array.getInt(sortedAttrs.indexOf(android.R.attr.scrollbars), 0));
+ json.put("View_fadingEdge", array.getInt(sortedAttrs.indexOf(android.R.attr.fadingEdge), 0));
+ json.put("View_scrollbarStyle", array.getInt(sortedAttrs.indexOf(android.R.attr.scrollbarStyle), 0));
+ json.put("View_scrollbarFadeDuration", array.getInt(sortedAttrs.indexOf(android.R.attr.scrollbarFadeDuration), 0));
+ json.put("View_scrollbarDefaultDelayBeforeFade", array.getInt(sortedAttrs.indexOf(android.R.attr.scrollbarDefaultDelayBeforeFade), 0));
+ json.put("View_scrollbarSize", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.scrollbarSize), -1));
+ json.put("View_scrollbarThumbHorizontal", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.scrollbarThumbHorizontal)), styleName + "_View_scrollbarThumbHorizontal", null));
+ json.put("View_scrollbarThumbVertical", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.scrollbarThumbVertical)), styleName + "_View_scrollbarThumbVertical", null));
+ json.put("View_scrollbarTrackHorizontal", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.scrollbarTrackHorizontal)), styleName + "_View_scrollbarTrackHorizontal", null));
+ json.put("View_scrollbarTrackVertical", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.scrollbarTrackVertical)), styleName + "_View_scrollbarTrackVertical", null));
+ json.put("View_isScrollContainer", array.getBoolean(sortedAttrs.indexOf(android.R.attr.isScrollContainer), false));
+ json.put("View_keepScreenOn", array.getBoolean(sortedAttrs.indexOf(android.R.attr.keepScreenOn), false));
+ json.put("View_filterTouchesWhenObscured", array.getBoolean(sortedAttrs.indexOf(android.R.attr.filterTouchesWhenObscured), false));
+ json.put("View_nextFocusLeft", array.getResourceId(sortedAttrs.indexOf(android.R.attr.nextFocusLeft), -1));
+ json.put("View_nextFocusRight", array.getResourceId(sortedAttrs.indexOf(android.R.attr.nextFocusRight), -1));
+ json.put("View_nextFocusUp", array.getResourceId(sortedAttrs.indexOf(android.R.attr.nextFocusUp), -1));
+ json.put("View_nextFocusDown", array.getResourceId(sortedAttrs.indexOf(android.R.attr.nextFocusDown), -1));
+ json.put("View_minWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minWidth), 0));
+ json.put("View_minHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minHeight), 0));
+ json.put("View_onClick", array.getString(sortedAttrs.indexOf(android.R.attr.onClick)));
+ json.put("View_overScrollMode", array.getInt(sortedAttrs.indexOf(android.R.attr.overScrollMode), 1));
+ json.put("View_paddingStart", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingStart), 0));
+ json.put("View_paddingEnd", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.paddingEnd), 0));
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public JSONObject extractTextAppearance(int styleName)
+ {
+ return extractTextAppearance(styleName, false);
+ }
+
+ @SuppressLint("ResourceType")
+ public JSONObject extractTextAppearance(int styleName, boolean subStyle)
+ {
+ final int[] attributes = new int[]{
+ android.R.attr.textSize,
+ android.R.attr.textStyle,
+ android.R.attr.textColor,
+ android.R.attr.typeface,
+ android.R.attr.textAllCaps,
+ android.R.attr.textColorHint,
+ android.R.attr.textColorLink,
+ android.R.attr.textColorHighlight
+ };
+ Arrays.sort(attributes);
+ TypedArray array;
+ if (subStyle)
+ array = m_theme.obtainStyledAttributes(styleName, attributes);
+ else
+ array = obtainStyledAttributes(styleName, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+ JSONObject json = new JSONObject();
+ try {
+ int attr = sortedAttrs.indexOf(android.R.attr.textSize);
+ if (array.hasValue(attr))
+ json.put("TextAppearance_textSize", array.getDimensionPixelSize(attr, 15));
+ attr = sortedAttrs.indexOf(android.R.attr.textStyle);
+ if (array.hasValue(attr))
+ json.put("TextAppearance_textStyle", array.getInt(attr, -1));
+ ColorStateList color = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColor));
+ if (color != null)
+ json.put("TextAppearance_textColor", getColorStateList(color));
+ attr = sortedAttrs.indexOf(android.R.attr.typeface);
+ if (array.hasValue(attr))
+ json.put("TextAppearance_typeface", array.getInt(attr, -1));
+ attr = sortedAttrs.indexOf(android.R.attr.textAllCaps);
+ if (array.hasValue(attr))
+ json.put("TextAppearance_textAllCaps", array.getBoolean(attr, false));
+ color = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColorHint));
+ if (color != null)
+ json.put("TextAppearance_textColorHint", getColorStateList(color));
+ color = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColorLink));
+ if (color != null)
+ json.put("TextAppearance_textColorLink", getColorStateList(color));
+ attr = sortedAttrs.indexOf(android.R.attr.textColorHighlight);
+ if (array.hasValue(attr))
+ json.put("TextAppearance_textColorHighlight", array.getColor(attr, 0));
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ public JSONObject extractTextAppearanceInformation(int styleName, String qtClass) {
+ return extractTextAppearanceInformation(styleName, qtClass, android.R.attr.textAppearance, null);
+ }
+
+ public JSONObject extractTextAppearanceInformation(int styleName, String qtClass, int textAppearance, AttributeSet attributeSet) {
+ JSONObject json = new JSONObject();
+ extractViewInformation(styleName, json, qtClass, attributeSet);
+
+ if (textAppearance == -1)
+ textAppearance = android.R.attr.textAppearance;
+
+ try {
+ TypedValue typedValue = new TypedValue();
+ Context ctx = new ContextThemeWrapper(m_context, m_theme);
+ ctx.getTheme().resolveAttribute(styleName, typedValue, true);
+
+ // Get textAppearance values
+ int[] textAppearanceAttr = new int[]{textAppearance};
+ TypedArray textAppearanceArray = ctx.obtainStyledAttributes(typedValue.data, textAppearanceAttr);
+ int textAppearanceId = textAppearanceArray.getResourceId(0, -1);
+ textAppearanceArray.recycle();
+
+ int textSize = 15;
+ int styleIndex = -1;
+ int typefaceIndex = -1;
+ int textColorHighlight = 0;
+ boolean allCaps = false;
+
+ if (textAppearanceId != -1) {
+ int[] attributes = new int[]{
+ android.R.attr.textSize,
+ android.R.attr.textStyle,
+ android.R.attr.typeface,
+ android.R.attr.textAllCaps,
+ android.R.attr.textColorHighlight
+ };
+ Arrays.sort(attributes);
+ TypedArray array = m_theme.obtainStyledAttributes(textAppearanceId, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ textSize = array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.textSize), 15);
+ styleIndex = array.getInt(sortedAttrs.indexOf(android.R.attr.textStyle), -1);
+ typefaceIndex = array.getInt(sortedAttrs.indexOf(android.R.attr.typeface), -1);
+ textColorHighlight = array.getColor(sortedAttrs.indexOf(android.R.attr.textColorHighlight), 0);
+ allCaps = array.getBoolean(sortedAttrs.indexOf(android.R.attr.textAllCaps), false);
+ array.recycle();
+ }
+ // Get TextView values
+ int[] attributes = new int[]{
+ android.R.attr.editable,
+ android.R.attr.inputMethod,
+ android.R.attr.numeric,
+ android.R.attr.digits,
+ android.R.attr.phoneNumber,
+ android.R.attr.autoText,
+ android.R.attr.capitalize,
+ android.R.attr.bufferType,
+ android.R.attr.selectAllOnFocus,
+ android.R.attr.autoLink,
+ android.R.attr.linksClickable,
+ android.R.attr.drawableLeft,
+ android.R.attr.drawableTop,
+ android.R.attr.drawableRight,
+ android.R.attr.drawableBottom,
+ android.R.attr.drawableStart,
+ android.R.attr.drawableEnd,
+ android.R.attr.maxLines,
+ android.R.attr.drawablePadding,
+ android.R.attr.textCursorDrawable,
+ android.R.attr.maxHeight,
+ android.R.attr.lines,
+ android.R.attr.height,
+ android.R.attr.minLines,
+ android.R.attr.minHeight,
+ android.R.attr.maxEms,
+ android.R.attr.maxWidth,
+ android.R.attr.ems,
+ android.R.attr.width,
+ android.R.attr.minEms,
+ android.R.attr.minWidth,
+ android.R.attr.gravity,
+ android.R.attr.hint,
+ android.R.attr.text,
+ android.R.attr.scrollHorizontally,
+ android.R.attr.singleLine,
+ android.R.attr.ellipsize,
+ android.R.attr.marqueeRepeatLimit,
+ android.R.attr.includeFontPadding,
+ android.R.attr.cursorVisible,
+ android.R.attr.maxLength,
+ android.R.attr.textScaleX,
+ android.R.attr.freezesText,
+ android.R.attr.shadowColor,
+ android.R.attr.shadowDx,
+ android.R.attr.shadowDy,
+ android.R.attr.shadowRadius,
+ android.R.attr.enabled,
+ android.R.attr.textColorHighlight,
+ android.R.attr.textColor,
+ android.R.attr.textColorHint,
+ android.R.attr.textColorLink,
+ android.R.attr.textSize,
+ android.R.attr.typeface,
+ android.R.attr.textStyle,
+ android.R.attr.password,
+ android.R.attr.lineSpacingExtra,
+ android.R.attr.lineSpacingMultiplier,
+ android.R.attr.inputType,
+ android.R.attr.imeOptions,
+ android.R.attr.imeActionLabel,
+ android.R.attr.imeActionId,
+ android.R.attr.privateImeOptions,
+ android.R.attr.textSelectHandleLeft,
+ android.R.attr.textSelectHandleRight,
+ android.R.attr.textSelectHandle,
+ android.R.attr.textIsSelectable,
+ android.R.attr.textAllCaps
+ };
+
+ // The array must be sorted in ascending order, otherwise obtainStyledAttributes()
+ // might fail to find some attributes
+ Arrays.sort(attributes);
+ TypedArray array = ctx.obtainStyledAttributes(typedValue.data, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ textSize = array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.textSize), textSize);
+ styleIndex = array.getInt(sortedAttrs.indexOf(android.R.attr.textStyle), styleIndex);
+ typefaceIndex = array.getInt(sortedAttrs.indexOf(android.R.attr.typeface), typefaceIndex);
+ textColorHighlight = array.getColor(sortedAttrs.indexOf(android.R.attr.textColorHighlight), textColorHighlight);
+ allCaps = array.getBoolean(sortedAttrs.indexOf(android.R.attr.textAllCaps), allCaps);
+
+ ColorStateList textColor = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColor));
+ ColorStateList textColorHint = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColorHint));
+ ColorStateList textColorLink = array.getColorStateList(sortedAttrs.indexOf(android.R.attr.textColorLink));
+
+ json.put("TextAppearance_textSize", textSize);
+ json.put("TextAppearance_textStyle", styleIndex);
+ json.put("TextAppearance_typeface", typefaceIndex);
+ json.put("TextAppearance_textColorHighlight", textColorHighlight);
+ json.put("TextAppearance_textAllCaps", allCaps);
+ if (textColor != null)
+ json.put("TextAppearance_textColor", getColorStateList(textColor));
+ if (textColorHint != null)
+ json.put("TextAppearance_textColorHint", getColorStateList(textColorHint));
+ if (textColorLink != null)
+ json.put("TextAppearance_textColorLink", getColorStateList(textColorLink));
+
+ json.put("TextView_editable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.editable), false));
+ json.put("TextView_inputMethod", array.getText(sortedAttrs.indexOf(android.R.attr.inputMethod)));
+ json.put("TextView_numeric", array.getInt(sortedAttrs.indexOf(android.R.attr.numeric), 0));
+ json.put("TextView_digits", array.getText(sortedAttrs.indexOf(android.R.attr.digits)));
+ json.put("TextView_phoneNumber", array.getBoolean(sortedAttrs.indexOf(android.R.attr.phoneNumber), false));
+ json.put("TextView_autoText", array.getBoolean(sortedAttrs.indexOf(android.R.attr.autoText), false));
+ json.put("TextView_capitalize", array.getInt(sortedAttrs.indexOf(android.R.attr.capitalize), -1));
+ json.put("TextView_bufferType", array.getInt(sortedAttrs.indexOf(android.R.attr.bufferType), 0));
+ json.put("TextView_selectAllOnFocus", array.getBoolean(sortedAttrs.indexOf(android.R.attr.selectAllOnFocus), false));
+ json.put("TextView_autoLink", array.getInt(sortedAttrs.indexOf(android.R.attr.autoLink), 0));
+ json.put("TextView_linksClickable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.linksClickable), true));
+ json.put("TextView_drawableLeft", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableLeft)), styleName + "_TextView_drawableLeft", null));
+ json.put("TextView_drawableTop", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableTop)), styleName + "_TextView_drawableTop", null));
+ json.put("TextView_drawableRight", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableRight)), styleName + "_TextView_drawableRight", null));
+ json.put("TextView_drawableBottom", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableBottom)), styleName + "_TextView_drawableBottom", null));
+ json.put("TextView_drawableStart", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableStart)), styleName + "_TextView_drawableStart", null));
+ json.put("TextView_drawableEnd", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.drawableEnd)), styleName + "_TextView_drawableEnd", null));
+ json.put("TextView_maxLines", array.getInt(sortedAttrs.indexOf(android.R.attr.maxLines), -1));
+ json.put("TextView_drawablePadding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.drawablePadding), 0));
+
+ try {
+ json.put("TextView_textCursorDrawable", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.textCursorDrawable)), styleName + "_TextView_textCursorDrawable", null));
+ } catch (Exception e_) {
+ json.put("TextView_textCursorDrawable", getDrawable(m_context.getResources().getDrawable(array.getResourceId(sortedAttrs.indexOf(android.R.attr.textCursorDrawable), 0), m_theme), styleName + "_TextView_textCursorDrawable", null));
+ }
+
+ json.put("TextView_maxLines", array.getInt(sortedAttrs.indexOf(android.R.attr.maxLines), -1));
+ json.put("TextView_maxHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxHeight), -1));
+ json.put("TextView_lines", array.getInt(sortedAttrs.indexOf(android.R.attr.lines), -1));
+ json.put("TextView_height", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.height), -1));
+ json.put("TextView_minLines", array.getInt(sortedAttrs.indexOf(android.R.attr.minLines), -1));
+ json.put("TextView_minHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minHeight), -1));
+ json.put("TextView_maxEms", array.getInt(sortedAttrs.indexOf(android.R.attr.maxEms), -1));
+ json.put("TextView_maxWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxWidth), -1));
+ json.put("TextView_ems", array.getInt(sortedAttrs.indexOf(android.R.attr.ems), -1));
+ json.put("TextView_width", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.width), -1));
+ json.put("TextView_minEms", array.getInt(sortedAttrs.indexOf(android.R.attr.minEms), -1));
+ json.put("TextView_minWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minWidth), -1));
+ json.put("TextView_gravity", array.getInt(sortedAttrs.indexOf(android.R.attr.gravity), -1));
+ json.put("TextView_hint", array.getText(sortedAttrs.indexOf(android.R.attr.hint)));
+ json.put("TextView_text", array.getText(sortedAttrs.indexOf(android.R.attr.text)));
+ json.put("TextView_scrollHorizontally", array.getBoolean(sortedAttrs.indexOf(android.R.attr.scrollHorizontally), false));
+ json.put("TextView_singleLine", array.getBoolean(sortedAttrs.indexOf(android.R.attr.singleLine), false));
+ json.put("TextView_ellipsize", array.getInt(sortedAttrs.indexOf(android.R.attr.ellipsize), -1));
+ json.put("TextView_marqueeRepeatLimit", array.getInt(sortedAttrs.indexOf(android.R.attr.marqueeRepeatLimit), 3));
+ json.put("TextView_includeFontPadding", array.getBoolean(sortedAttrs.indexOf(android.R.attr.includeFontPadding), true));
+ json.put("TextView_cursorVisible", array.getBoolean(sortedAttrs.indexOf(android.R.attr.maxLength), true));
+ json.put("TextView_maxLength", array.getInt(sortedAttrs.indexOf(android.R.attr.maxLength), -1));
+ json.put("TextView_textScaleX", array.getFloat(sortedAttrs.indexOf(android.R.attr.textScaleX), 1.0f));
+ json.put("TextView_freezesText", array.getBoolean(sortedAttrs.indexOf(android.R.attr.freezesText), false));
+ json.put("TextView_shadowColor", array.getInt(sortedAttrs.indexOf(android.R.attr.shadowColor), 0));
+ json.put("TextView_shadowDx", array.getFloat(sortedAttrs.indexOf(android.R.attr.shadowDx), 0));
+ json.put("TextView_shadowDy", array.getFloat(sortedAttrs.indexOf(android.R.attr.shadowDy), 0));
+ json.put("TextView_shadowRadius", array.getFloat(sortedAttrs.indexOf(android.R.attr.shadowRadius), 0));
+ json.put("TextView_enabled", array.getBoolean(sortedAttrs.indexOf(android.R.attr.enabled), true));
+ json.put("TextView_password", array.getBoolean(sortedAttrs.indexOf(android.R.attr.password), false));
+ json.put("TextView_lineSpacingExtra", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.lineSpacingExtra), 0));
+ json.put("TextView_lineSpacingMultiplier", array.getFloat(sortedAttrs.indexOf(android.R.attr.lineSpacingMultiplier), 1.0f));
+ json.put("TextView_inputType", array.getInt(sortedAttrs.indexOf(android.R.attr.inputType), EditorInfo.TYPE_NULL));
+ json.put("TextView_imeOptions", array.getInt(sortedAttrs.indexOf(android.R.attr.imeOptions), EditorInfo.IME_NULL));
+ json.put("TextView_imeActionLabel", array.getText(sortedAttrs.indexOf(android.R.attr.imeActionLabel)));
+ json.put("TextView_imeActionId", array.getInt(sortedAttrs.indexOf(android.R.attr.imeActionId), 0));
+ json.put("TextView_privateImeOptions", array.getString(sortedAttrs.indexOf(android.R.attr.privateImeOptions)));
+
+ try {
+ json.put("TextView_textSelectHandleLeft", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.textSelectHandleLeft)), styleName + "_TextView_textSelectHandleLeft", null));
+ } catch (Exception _e) {
+ json.put("TextView_textSelectHandleLeft", getDrawable(m_context.getResources().getDrawable(array.getResourceId(sortedAttrs.indexOf(android.R.attr.textSelectHandleLeft), 0), m_theme), styleName + "_TextView_textSelectHandleLeft", null));
+ }
+
+ try {
+ json.put("TextView_textSelectHandleRight", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.textSelectHandleRight)), styleName + "_TextView_textSelectHandleRight", null));
+ } catch (Exception _e) {
+ json.put("TextView_textSelectHandleRight", getDrawable(m_context.getResources().getDrawable(array.getResourceId(sortedAttrs.indexOf(android.R.attr.textSelectHandleRight), 0), m_theme), styleName + "_TextView_textSelectHandleRight", null));
+ }
+
+ try {
+ json.put("TextView_textSelectHandle", getDrawable(array.getDrawable(sortedAttrs.indexOf(android.R.attr.textSelectHandle)), styleName + "_TextView_textSelectHandle", null));
+ } catch (Exception _e) {
+ json.put("TextView_textSelectHandle", getDrawable(m_context.getResources().getDrawable(array.getResourceId(sortedAttrs.indexOf(android.R.attr.textSelectHandle), 0), m_theme), styleName + "_TextView_textSelectHandle", null));
+ }
+ json.put("TextView_textIsSelectable", array.getBoolean(sortedAttrs.indexOf(android.R.attr.textIsSelectable), false));
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ public JSONObject extractImageViewInformation(int styleName, String qtClassName) {
+ JSONObject json = new JSONObject();
+ try {
+ extractViewInformation(styleName, json, qtClassName);
+
+ int[] attributes = new int[]{
+ android.R.attr.src,
+ android.R.attr.baselineAlignBottom,
+ android.R.attr.adjustViewBounds,
+ android.R.attr.maxWidth,
+ android.R.attr.maxHeight,
+ android.R.attr.scaleType,
+ android.R.attr.cropToPadding,
+ android.R.attr.tint
+
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(styleName, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable drawable = array.getDrawable(sortedAttrs.indexOf(android.R.attr.src));
+ if (drawable != null)
+ json.put("ImageView_src", getDrawable(drawable, styleName + "_ImageView_src", null));
+
+ json.put("ImageView_baselineAlignBottom", array.getBoolean(sortedAttrs.indexOf(android.R.attr.baselineAlignBottom), false));
+ json.put("ImageView_adjustViewBounds", array.getBoolean(sortedAttrs.indexOf(android.R.attr.baselineAlignBottom), false));
+ json.put("ImageView_maxWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxWidth), Integer.MAX_VALUE));
+ json.put("ImageView_maxHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxHeight), Integer.MAX_VALUE));
+ int index = array.getInt(sortedAttrs.indexOf(android.R.attr.scaleType), -1);
+ if (index >= 0)
+ json.put("ImageView_scaleType", sScaleTypeArray[index]);
+
+ int tint = array.getInt(sortedAttrs.indexOf(android.R.attr.tint), 0);
+ if (tint != 0)
+ json.put("ImageView_tint", tint);
+
+ json.put("ImageView_cropToPadding", array.getBoolean(sortedAttrs.indexOf(android.R.attr.cropToPadding), false));
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ void extractCompoundButton(SimpleJsonWriter jsonWriter, int styleName, String className, String qtClass) {
+ JSONObject json = extractTextAppearanceInformation(styleName, qtClass);
+
+ TypedValue typedValue = new TypedValue();
+ Context ctx = new ContextThemeWrapper(m_context, m_theme);
+ ctx.getTheme().resolveAttribute(styleName, typedValue, true);
+ final int[] attributes = new int[]{android.R.attr.button};
+ TypedArray array = ctx.obtainStyledAttributes(typedValue.data, attributes);
+ Drawable drawable = array.getDrawable(0);
+ array.recycle();
+
+ try {
+ if (drawable != null)
+ json.put("CompoundButton_button", getDrawable(drawable, styleName + "_CompoundButton_button", null));
+ jsonWriter.name(className).value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractProgressBarInfo(JSONObject json, int styleName) {
+ try {
+ final int[] attributes = new int[]{
+ android.R.attr.minWidth,
+ android.R.attr.maxWidth,
+ android.R.attr.minHeight,
+ android.R.attr.maxHeight,
+ android.R.attr.indeterminateDuration,
+ android.R.attr.progressDrawable,
+ android.R.attr.indeterminateDrawable
+ };
+
+ // The array must be sorted in ascending order, otherwise obtainStyledAttributes()
+ // might fail to find some attributes
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(styleName, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ json.put("ProgressBar_indeterminateDuration", array.getInt(sortedAttrs.indexOf(android.R.attr.indeterminateDuration), 4000));
+ json.put("ProgressBar_minWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minWidth), 24));
+ json.put("ProgressBar_maxWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxWidth), 48));
+ json.put("ProgressBar_minHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.minHeight), 24));
+ json.put("ProgressBar_maxHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.maxHeight), 28));
+ json.put("ProgressBar_progress_id", android.R.id.progress);
+ json.put("ProgressBar_secondaryProgress_id", android.R.id.secondaryProgress);
+
+ Drawable drawable = array.getDrawable(sortedAttrs.indexOf(android.R.attr.progressDrawable));
+ if (drawable != null)
+ json.put("ProgressBar_progressDrawable", getDrawable(drawable,
+ styleName + "_ProgressBar_progressDrawable", null));
+
+ drawable = array.getDrawable(sortedAttrs.indexOf(android.R.attr.indeterminateDrawable));
+ if (drawable != null)
+ json.put("ProgressBar_indeterminateDrawable", getDrawable(drawable,
+ styleName + "_ProgressBar_indeterminateDrawable", null));
+
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractProgressBar(SimpleJsonWriter writer, int styleName, String className, String qtClass) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.progressBarStyle, qtClass);
+ try {
+ extractProgressBarInfo(json, styleName);
+ writer.name(className).value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractAbsSeekBar(SimpleJsonWriter jsonWriter) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.seekBarStyle, "QSlider");
+ extractProgressBarInfo(json, android.R.attr.seekBarStyle);
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.thumb,
+ android.R.attr.thumbOffset
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.seekBarStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.thumb));
+ if (d != null)
+ json.put("SeekBar_thumb", getDrawable(d, android.R.attr.seekBarStyle + "_SeekBar_thumb", null));
+ json.put("SeekBar_thumbOffset", array.getDimensionPixelOffset(sortedAttrs.indexOf(android.R.attr.thumbOffset), -1));
+ array.recycle();
+ jsonWriter.name("seekBarStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractSwitch(SimpleJsonWriter jsonWriter) {
+ JSONObject json = new JSONObject();
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.thumb,
+ android.R.attr.track,
+ android.R.attr.switchTextAppearance,
+ android.R.attr.textOn,
+ android.R.attr.textOff,
+ android.R.attr.switchMinWidth,
+ android.R.attr.switchPadding,
+ android.R.attr.thumbTextPadding,
+ android.R.attr.showText,
+ android.R.attr.splitTrack
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.switchStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable thumb = array.getDrawable(sortedAttrs.indexOf(android.R.attr.thumb));
+ if (thumb != null)
+ json.put("Switch_thumb", getDrawable(thumb, android.R.attr.switchStyle + "_Switch_thumb", null));
+
+ Drawable track = array.getDrawable(sortedAttrs.indexOf(android.R.attr.track));
+ if (track != null)
+ json.put("Switch_track", getDrawable(track, android.R.attr.switchStyle + "_Switch_track", null));
+
+ json.put("Switch_textOn", array.getText(sortedAttrs.indexOf(android.R.attr.textOn)));
+ json.put("Switch_textOff", array.getText(sortedAttrs.indexOf(android.R.attr.textOff)));
+ json.put("Switch_switchMinWidth", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.switchMinWidth), 0));
+ json.put("Switch_switchPadding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.switchPadding), 0));
+ json.put("Switch_thumbTextPadding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.thumbTextPadding), 0));
+ json.put("Switch_showText", array.getBoolean(sortedAttrs.indexOf(android.R.attr.showText), true));
+ json.put("Switch_splitTrack", array.getBoolean(sortedAttrs.indexOf(android.R.attr.splitTrack), false));
+
+ // Get textAppearance values
+ final int textAppearanceId = array.getResourceId(sortedAttrs.indexOf(android.R.attr.switchTextAppearance), -1);
+ json.put("Switch_switchTextAppearance", extractTextAppearance(textAppearanceId, true));
+
+ array.recycle();
+ jsonWriter.name("switchStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ JSONObject extractCheckedTextView(String itemName) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.checkedTextViewStyle, itemName);
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.checkMark,
+ };
+
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.switchStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable drawable = array.getDrawable(sortedAttrs.indexOf(android.R.attr.checkMark));
+ if (drawable != null)
+ json.put("CheckedTextView_checkMark", getDrawable(drawable, itemName + "_CheckedTextView_checkMark", null));
+ array.recycle();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ private JSONObject extractItemStyle(int resourceId, String itemName)
+ {
+ try {
+ XmlResourceParser parser = m_context.getResources().getLayout(resourceId);
+ int type = parser.next();
+ while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT)
+ type = parser.next();
+
+ if (type != XmlPullParser.START_TAG)
+ return null;
+
+ AttributeSet attributes = Xml.asAttributeSet(parser);
+ String name = parser.getName();
+ if (name.equals("TextView"))
+ return extractTextAppearanceInformation(android.R.attr.textViewStyle, itemName, android.R.attr.textAppearanceListItem, attributes);
+ else if (name.equals("CheckedTextView"))
+ return extractCheckedTextView(itemName);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private void extractItemsStyle(SimpleJsonWriter jsonWriter) {
+ try {
+ JSONObject itemStyle = extractItemStyle(android.R.layout.simple_list_item_1, "simple_list_item");
+ if (itemStyle != null)
+ jsonWriter.name("simple_list_item").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_list_item_checked, "simple_list_item_checked");
+ if (itemStyle != null)
+ jsonWriter.name("simple_list_item_checked").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_list_item_multiple_choice, "simple_list_item_multiple_choice");
+ if (itemStyle != null)
+ jsonWriter.name("simple_list_item_multiple_choice").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_list_item_single_choice, "simple_list_item_single_choice");
+ if (itemStyle != null)
+ jsonWriter.name("simple_list_item_single_choice").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_spinner_item, "simple_spinner_item");
+ if (itemStyle != null)
+ jsonWriter.name("simple_spinner_item").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_spinner_dropdown_item, "simple_spinner_dropdown_item");
+ if (itemStyle != null)
+ jsonWriter.name("simple_spinner_dropdown_item").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_dropdown_item_1line, "simple_dropdown_item_1line");
+ if (itemStyle != null)
+ jsonWriter.name("simple_dropdown_item_1line").value(itemStyle);
+ itemStyle = extractItemStyle(android.R.layout.simple_selectable_list_item, "simple_selectable_list_item");
+ if (itemStyle != null)
+ jsonWriter.name("simple_selectable_list_item").value(itemStyle);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractListView(SimpleJsonWriter writer) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.listViewStyle, "QListView");
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.divider,
+ android.R.attr.dividerHeight
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.listViewStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable divider = array.getDrawable(sortedAttrs.indexOf(android.R.attr.divider));
+ if (divider != null)
+ json.put("ListView_divider", getDrawable(divider, android.R.attr.listViewStyle + "_ListView_divider", null));
+
+ json.put("ListView_dividerHeight", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.dividerHeight), 0));
+
+ array.recycle();
+ writer.name("listViewStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractCalendar(SimpleJsonWriter writer) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.calendarViewStyle, "QCalendarWidget");
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.firstDayOfWeek,
+ android.R.attr.focusedMonthDateColor,
+ android.R.attr.selectedWeekBackgroundColor,
+ android.R.attr.showWeekNumber,
+ android.R.attr.shownWeekCount,
+ android.R.attr.unfocusedMonthDateColor,
+ android.R.attr.weekNumberColor,
+ android.R.attr.weekSeparatorLineColor,
+ android.R.attr.selectedDateVerticalBar,
+ android.R.attr.dateTextAppearance,
+ android.R.attr.weekDayTextAppearance
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.calendarViewStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.selectedDateVerticalBar));
+ if (d != null)
+ json.put("CalendarView_selectedDateVerticalBar", getDrawable(d, android.R.attr.calendarViewStyle + "_CalendarView_selectedDateVerticalBar", null));
+
+ int textAppearanceId = array.getResourceId(sortedAttrs.indexOf(android.R.attr.dateTextAppearance), -1);
+ json.put("CalendarView_dateTextAppearance", extractTextAppearance(textAppearanceId, true));
+ textAppearanceId = array.getResourceId(sortedAttrs.indexOf(android.R.attr.weekDayTextAppearance), -1);
+ json.put("CalendarView_weekDayTextAppearance", extractTextAppearance(textAppearanceId, true));
+
+
+ json.put("CalendarView_firstDayOfWeek", array.getInt(sortedAttrs.indexOf(android.R.attr.firstDayOfWeek), 0));
+ json.put("CalendarView_focusedMonthDateColor", array.getColor(sortedAttrs.indexOf(android.R.attr.focusedMonthDateColor), 0));
+ json.put("CalendarView_selectedWeekBackgroundColor", array.getColor(sortedAttrs.indexOf(android.R.attr.selectedWeekBackgroundColor), 0));
+ json.put("CalendarView_showWeekNumber", array.getBoolean(sortedAttrs.indexOf(android.R.attr.showWeekNumber), true));
+ json.put("CalendarView_shownWeekCount", array.getInt(sortedAttrs.indexOf(android.R.attr.shownWeekCount), 6));
+ json.put("CalendarView_unfocusedMonthDateColor", array.getColor(sortedAttrs.indexOf(android.R.attr.unfocusedMonthDateColor), 0));
+ json.put("CalendarView_weekNumberColor", array.getColor(sortedAttrs.indexOf(android.R.attr.weekNumberColor), 0));
+ json.put("CalendarView_weekSeparatorLineColor", array.getColor(sortedAttrs.indexOf(android.R.attr.weekSeparatorLineColor), 0));
+ array.recycle();
+ writer.name("calendarViewStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractToolBar(SimpleJsonWriter writer) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.toolbarStyle, "QToolBar");
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.background,
+ android.R.attr.backgroundStacked,
+ android.R.attr.backgroundSplit,
+ android.R.attr.divider,
+ android.R.attr.itemPadding
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.toolbarStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.background));
+ if (d != null)
+ json.put("ActionBar_background", getDrawable(d, android.R.attr.toolbarStyle + "_ActionBar_background", null));
+
+ d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.backgroundStacked));
+ if (d != null)
+ json.put("ActionBar_backgroundStacked", getDrawable(d, android.R.attr.toolbarStyle + "_ActionBar_backgroundStacked", null));
+
+ d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.backgroundSplit));
+ if (d != null)
+ json.put("ActionBar_backgroundSplit", getDrawable(d, android.R.attr.toolbarStyle + "_ActionBar_backgroundSplit", null));
+
+ d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.divider));
+ if (d != null)
+ json.put("ActionBar_divider", getDrawable(d, android.R.attr.toolbarStyle + "_ActionBar_divider", null));
+
+ json.put("ActionBar_itemPadding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.itemPadding), 0));
+
+ array.recycle();
+ writer.name("actionBarStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ void extractTabBar(SimpleJsonWriter writer) {
+ JSONObject json = extractTextAppearanceInformation(android.R.attr.actionBarTabBarStyle, "QTabBar");
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.showDividers,
+ android.R.attr.dividerPadding,
+ android.R.attr.divider
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.actionBarTabStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable d = array.getDrawable(sortedAttrs.indexOf(android.R.attr.divider));
+ if (d != null)
+ json.put("LinearLayout_divider", getDrawable(d, android.R.attr.actionBarTabStyle + "_LinearLayout_divider", null));
+ json.put("LinearLayout_showDividers", array.getInt(sortedAttrs.indexOf(android.R.attr.showDividers), 0));
+ json.put("LinearLayout_dividerPadding", array.getDimensionPixelSize(sortedAttrs.indexOf(android.R.attr.dividerPadding), 0));
+
+ array.recycle();
+ writer.name("actionBarTabBarStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void extractWindow(SimpleJsonWriter writer) {
+ JSONObject json = new JSONObject();
+ try {
+ int[] attributes = new int[]{
+ android.R.attr.windowBackground,
+ android.R.attr.windowFrame
+ };
+ Arrays.sort(attributes);
+ TypedArray array = obtainStyledAttributes(android.R.attr.popupWindowStyle, attributes);
+ ArrayList<Integer> sortedAttrs = getArrayListFromIntArray(attributes);
+
+ Drawable background = array.getDrawable(sortedAttrs.indexOf(android.R.attr.windowBackground));
+ if (background != null)
+ json.put("Window_windowBackground", getDrawable(background, android.R.attr.popupWindowStyle + "_Window_windowBackground", null));
+
+ Drawable frame = array.getDrawable(sortedAttrs.indexOf(android.R.attr.windowFrame));
+ if (frame != null)
+ json.put("Window_windowFrame", getDrawable(frame, android.R.attr.popupWindowStyle + "_Window_windowFrame", null));
+ array.recycle();
+ writer.name("windowStyle").value(json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private JSONObject extractDefaultPalette() {
+ JSONObject json = extractTextAppearance(android.R.attr.textAppearance);
+ try {
+ json.put("defaultBackgroundColor", defaultBackgroundColor);
+ json.put("defaultTextColorPrimary", defaultTextColor);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ static class SimpleJsonWriter {
+ private final OutputStreamWriter m_writer;
+ private boolean m_addComma = false;
+ private int m_indentLevel = 0;
+
+ public SimpleJsonWriter(String filePath) throws FileNotFoundException {
+ m_writer = new OutputStreamWriter(new FileOutputStream(filePath));
+ }
+
+ public void close() throws IOException {
+ m_writer.close();
+ }
+
+ private void writeIndent() throws IOException {
+ m_writer.write(" ", 0, m_indentLevel);
+ }
+
+ void beginObject() throws IOException {
+ writeIndent();
+ m_writer.write("{\n");
+ ++m_indentLevel;
+ m_addComma = false;
+ }
+
+ void endObject() throws IOException {
+ m_writer.write("\n");
+ writeIndent();
+ m_writer.write("}\n");
+ --m_indentLevel;
+ m_addComma = false;
+ }
+
+ SimpleJsonWriter name(String name) throws IOException {
+ if (m_addComma) {
+ m_writer.write(",\n");
+ }
+ writeIndent();
+ m_writer.write(JSONObject.quote(name) + ": ");
+ m_addComma = true;
+ return this;
+ }
+
+ void value(JSONObject value) throws IOException {
+ m_writer.write(value.toString());
+ }
+ }
+
+ static class DrawableCache {
+ JSONObject object;
+ Object drawable;
+ public DrawableCache(JSONObject json, Object drawable) {
+ object = json;
+ this.drawable = drawable;
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java
index 79caaf318e..ea77739339 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java
@@ -1,71 +1,32 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-package org.qtproject.qt5.android.accessibility;
-
-import android.accessibilityservice.AccessibilityService;
+// 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
+
+
+package org.qtproject.qt.android;
+
import android.app.Activity;
+import android.content.Context;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
+import android.system.Os;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.text.TextUtils;
-
-import android.view.accessibility.*;
-import android.view.MotionEvent;
-import android.view.View.OnHoverListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
-import android.content.Context;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import org.qtproject.qt5.android.QtActivityDelegate;
-
-public class QtAccessibilityDelegate extends View.AccessibilityDelegate
+class QtAccessibilityDelegate extends View.AccessibilityDelegate
{
private static final String TAG = "Qt A11Y";
- // Qt uses the upper half of the unsiged integers
+ // Qt uses the upper half of the unsigned integers
// all low positive ints should be fine.
public static final int INVALID_ID = 333; // half evil
@@ -74,10 +35,8 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
private static final String DEFAULT_CLASS_NAME = "$VirtualChild";
private View m_view = null;
- private AccessibilityManager m_manager;
- private QtActivityDelegate m_activityDelegate;
- private Activity m_activity;
- private ViewGroup m_layout;
+ private final AccessibilityManager m_manager;
+ private final QtLayout m_layout;
// The accessible object that currently has the "accessibility focus"
// usually indicated by a yellow rectangle on screen.
@@ -89,6 +48,8 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
// this is because the Android platform window does not take
// the offset of the view on screen into account (eg status bar on top)
private final int[] m_globalOffset = new int[2];
+ private int m_oldOffsetX = 0;
+ private int m_oldOffsetY = 0;
private class HoverEventListener implements View.OnHoverListener
{
@@ -98,14 +59,13 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
return dispatchHoverEvent(event);
}
}
-
- public QtAccessibilityDelegate(Activity activity, ViewGroup layout, QtActivityDelegate activityDelegate)
+ // TODO do we want to have one QtAccessibilityDelegate for the whole app (QtRootLayout) or
+ // e.g. one per window?
+ public QtAccessibilityDelegate(QtLayout layout)
{
- m_activity = activity;
m_layout = layout;
- m_activityDelegate = activityDelegate;
- m_manager = (AccessibilityManager) m_activity.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ m_manager = (AccessibilityManager) m_layout.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (m_manager != null) {
AccessibilityManagerListener accServiceListener = new AccessibilityManagerListener();
if (!m_manager.addAccessibilityStateChangeListener(accServiceListener))
@@ -120,24 +80,29 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
@Override
public void onAccessibilityStateChanged(boolean enabled)
{
+ if (Os.getenv("QT_ANDROID_DISABLE_ACCESSIBILITY") != null)
+ return;
if (enabled) {
- try {
+ try {
View view = m_view;
if (view == null) {
- view = new View(m_activity);
+ view = new View(m_layout.getContext());
view.setId(View.NO_ID);
}
// ### Keep this for debugging for a while. It allows us to visually see that our View
// ### is on top of the surface(s)
- // ColorDrawable color = new ColorDrawable(0x80ff8080); //0xAARRGGBB
- // view.setBackground(color);
+ //noinspection CommentedOutCode
+ {
+ // ColorDrawable color = new ColorDrawable(0x80ff8080); //0xAARRGGBB
+ // view.setBackground(color);
+ }
view.setAccessibilityDelegate(QtAccessibilityDelegate.this);
// if all is fine, add it to the layout
if (m_view == null) {
//m_layout.addAccessibilityView(view);
- m_layout.addView(view, m_activityDelegate.getSurfaceCount(),
+ m_layout.addView(view, m_layout.getChildCount(),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
m_view = view;
@@ -145,7 +110,7 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
m_view.setOnHoverListener(new HoverEventListener());
} catch (Exception e) {
// Unknown exception means something went wrong.
- Log.w("Qt A11y", "Unknown exception: " + e.toString());
+ Log.w("Qt A11y", "Unknown exception: " + e);
}
} else {
if (m_view != null) {
@@ -181,8 +146,6 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
- setHoveredVirtualViewId(virtualViewId);
- break;
case MotionEvent.ACTION_HOVER_EXIT:
setHoveredVirtualViewId(virtualViewId);
break;
@@ -191,27 +154,117 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
return true;
}
- public boolean sendEventForVirtualViewId(int virtualViewId, int eventType)
+ public void notifyScrolledEvent(int viewId)
{
- if ((virtualViewId == INVALID_ID) || !m_manager.isEnabled()) {
- Log.w(TAG, "sendEventForVirtualViewId for invalid view");
- return false;
- }
+ QtNative.runAction(() -> sendEventForVirtualViewId(viewId,
+ AccessibilityEvent.TYPE_VIEW_SCROLLED));
+ }
+
+ public void notifyLocationChange(int viewId)
+ {
+ QtNative.runAction(() -> {
+ if (m_focusedVirtualViewId == viewId)
+ invalidateVirtualViewId(m_focusedVirtualViewId);
+ });
+ }
+
+ public void notifyObjectHide(int viewId, int parentId)
+ {
+ QtNative.runAction(() -> {
+ // If the object had accessibility focus, we need to clear it.
+ // Note: This code is mostly copied from
+ // AccessibilityNodeProvider::performAction, but we remove the
+ // focus only if the focused view id matches the one that was hidden.
+ if (m_focusedVirtualViewId == viewId) {
+ m_focusedVirtualViewId = INVALID_ID;
+ m_view.invalidate();
+ sendEventForVirtualViewId(viewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ }
+ // When the object is hidden, we need to notify its parent about
+ // content change, not the hidden object itself
+ invalidateVirtualViewId(parentId);
+ });
+ }
+
+ public void notifyObjectFocus(int viewId)
+ {
+ QtNative.runAction(() -> {
+ if (m_view == null)
+ return;
+ m_focusedVirtualViewId = viewId;
+ m_view.invalidate();
+ sendEventForVirtualViewId(viewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ });
+ }
+
+ public void notifyValueChanged(int viewId, String value)
+ {
+ QtNative.runAction(() -> {
+ // Send a TYPE_ANNOUNCEMENT event with the new value
+
+ if ((viewId == INVALID_ID) || !m_manager.isEnabled()) {
+ Log.w(TAG, "notifyValueChanged() for invalid view");
+ return;
+ }
+
+ final ViewGroup group = (ViewGroup) m_view.getParent();
+ if (group == null) {
+ Log.w(TAG, "Could not announce value because ViewGroup was null.");
+ return;
+ }
+
+ final AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+
+ event.setEnabled(true);
+ event.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME);
+
+ event.setContentDescription(value);
+
+ if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) {
+ Log.w(TAG, "No value to announce for " + event.getClassName());
+ return;
+ }
+
+ event.setPackageName(m_view.getContext().getPackageName());
+ event.setSource(m_view, viewId);
+
+ if (!group.requestSendAccessibilityEvent(m_view, event))
+ Log.w(TAG, "Failed to send value change announcement for " + event.getClassName());
+ });
+ }
+
+ public void sendEventForVirtualViewId(int virtualViewId, int eventType)
+ {
+ final AccessibilityEvent event = getEventForVirtualViewId(virtualViewId, eventType);
+ sendAccessibilityEvent(event);
+ }
+
+ public void sendAccessibilityEvent(AccessibilityEvent event)
+ {
+ if (event == null)
+ return;
final ViewGroup group = (ViewGroup) m_view.getParent();
if (group == null) {
Log.w(TAG, "Could not send AccessibilityEvent because group was null. This should really not happen.");
- return false;
+ return;
}
- final AccessibilityEvent event;
- event = getEventForVirtualViewId(virtualViewId, eventType);
- return group.requestSendAccessibilityEvent(m_view, event);
+ group.requestSendAccessibilityEvent(m_view, event);
}
public void invalidateVirtualViewId(int virtualViewId)
{
- sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ final AccessibilityEvent event = getEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+
+ if (event == null)
+ return;
+
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ sendAccessibilityEvent(event);
}
private void setHoveredVirtualViewId(int virtualViewId)
@@ -228,6 +281,14 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
private AccessibilityEvent getEventForVirtualViewId(int virtualViewId, int eventType)
{
+ if ((virtualViewId == INVALID_ID) || !m_manager.isEnabled()) {
+ Log.w(TAG, "getEventForVirtualViewId for invalid view");
+ return null;
+ }
+
+ if (m_layout.getChildCount() == 0)
+ return null;
+
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setEnabled(true);
@@ -242,15 +303,17 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
return event;
}
+ // This can be used for debug by performActionForVirtualViewId()
+ /** @noinspection unused*/
private void dumpNodes(int parentId)
{
Log.i(TAG, "A11Y hierarchy: " + parentId + " parent: " + QtNativeAccessibility.parentId(parentId));
Log.i(TAG, " desc: " + QtNativeAccessibility.descriptionForAccessibleObject(parentId) + " rect: " + QtNativeAccessibility.screenRect(parentId));
Log.i(TAG, " NODE: " + getNodeForVirtualViewId(parentId));
int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(parentId);
- for (int i = 0; i < ids.length; ++i) {
- Log.i(TAG, parentId + " has child: " + ids[i]);
- dumpNodes(ids[i]);
+ for (int id : ids) {
+ Log.i(TAG, parentId + " has child: " + id);
+ dumpNodes(id);
}
}
@@ -287,12 +350,30 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
result.setPackageName(source.getPackageName());
result.setClassName(source.getClassName());
-// Spit out the entire hierarchy for debugging purposes
-// dumpNodes(-1);
+ // Spit out the entire hierarchy for debugging purposes
+ // dumpNodes(-1);
- int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(-1);
- for (int i = 0; i < ids.length; ++i)
- result.addChild(m_view, ids[i]);
+ if (m_layout.getChildCount() != 0) {
+ int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(-1);
+ for (int id : ids)
+ result.addChild(m_view, id);
+ }
+
+ // The offset values have changed, so we need to re-focus the
+ // currently focused item, otherwise it will have an incorrect
+ // focus frame
+ if ((m_oldOffsetX != offsetX) || (m_oldOffsetY != offsetY)) {
+ m_oldOffsetX = offsetX;
+ m_oldOffsetY = offsetY;
+ if (m_focusedVirtualViewId != INVALID_ID) {
+ m_nodeProvider.performAction(m_focusedVirtualViewId,
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
+ new Bundle());
+ m_nodeProvider.performAction(m_focusedVirtualViewId,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS,
+ new Bundle());
+ }
+ }
return result;
}
@@ -304,8 +385,9 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
node.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME);
node.setPackageName(m_view.getContext().getPackageName());
- if (!QtNativeAccessibility.populateNode(virtualViewId, node))
+ if (m_layout.getChildCount() == 0 || !QtNativeAccessibility.populateNode(virtualViewId, node)) {
return node;
+ }
// set only if valid, otherwise we return a node that is invalid and will crash when accessed
node.setSource(m_view, virtualViewId);
@@ -322,32 +404,39 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
screenRect.offset(offsetX, offsetY);
node.setBoundsInScreen(screenRect);
- Rect rectInParent = screenRect;
Rect parentScreenRect = QtNativeAccessibility.screenRect(parentId);
- rectInParent.offset(-parentScreenRect.left, -parentScreenRect.top);
- node.setBoundsInParent(rectInParent);
+ screenRect.offset(-parentScreenRect.left, -parentScreenRect.top);
+ node.setBoundsInParent(screenRect);
// Manage internal accessibility focus state.
if (m_focusedVirtualViewId == virtualViewId) {
node.setAccessibilityFocused(true);
- node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
} else {
node.setAccessibilityFocused(false);
- node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
}
int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(virtualViewId);
- for (int i = 0; i < ids.length; ++i)
- node.addChild(m_view, ids[i]);
+ for (int id : ids)
+ node.addChild(m_view, id);
+ if (node.isScrollable()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ node.setCollectionInfo(new CollectionInfo(ids.length, 1, false));
+ } else {
+ node.setCollectionInfo(CollectionInfo.obtain(ids.length, 1, false));
+ }
+ }
+
return node;
}
- private AccessibilityNodeProvider m_nodeProvider = new AccessibilityNodeProvider()
+ private final AccessibilityNodeProvider m_nodeProvider = new AccessibilityNodeProvider()
{
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId)
{
- if (virtualViewId == View.NO_ID) {
+ if (virtualViewId == View.NO_ID || m_layout.getChildCount() == 0) {
return getNodeForView();
}
return getNodeForVirtualViewId(virtualViewId);
@@ -389,16 +478,19 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
return m_view.performAccessibilityAction(action, arguments);
}
}
- handled |= performActionForVirtualViewId(virtualViewId, action, arguments);
+ handled |= performActionForVirtualViewId(virtualViewId, action);
return handled;
}
};
- protected boolean performActionForVirtualViewId(int virtualViewId, int action, Bundle arguments)
+ protected boolean performActionForVirtualViewId(int virtualViewId, int action)
{
-// Log.i(TAG, "ACTION " + action + " on " + virtualViewId);
-// dumpNodes(virtualViewId);
+ //noinspection CommentedOutCode
+ {
+ // Log.i(TAG, "ACTION " + action + " on " + virtualViewId);
+ // dumpNodes(virtualViewId);
+ }
boolean success = false;
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
new file mode 100644
index 0000000000..3cb6ba220e
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
@@ -0,0 +1,325 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+
+public class QtActivityBase extends Activity
+{
+ private String m_applicationParams = "";
+ private boolean m_isCustomThemeSet = false;
+ private boolean m_retainNonConfigurationInstance = false;
+
+ private QtActivityDelegate m_delegate;
+
+ public static final String EXTRA_SOURCE_INFO = "org.qtproject.qt.android.sourceInfo";
+
+ private void addReferrer(Intent intent)
+ {
+ if (intent.getExtras() != null && intent.getExtras().getString(EXTRA_SOURCE_INFO) != null)
+ return;
+
+ String browserApplicationId = "";
+ if (intent.getExtras() != null)
+ browserApplicationId = intent.getExtras().getString(Browser.EXTRA_APPLICATION_ID);
+
+ String sourceInformation = "";
+ if (browserApplicationId != null && !browserApplicationId.isEmpty()) {
+ sourceInformation = browserApplicationId;
+ } else {
+ Uri referrer = getReferrer();
+ if (referrer != null)
+ sourceInformation = referrer.toString().replaceFirst("android-app://", "");
+ }
+
+ intent.putExtra(EXTRA_SOURCE_INFO, sourceInformation);
+ }
+
+ // Append any parameters to your application.
+ // Either a whitespace or a tab is accepted as a separator between parameters.
+ /** @noinspection unused*/
+ public void appendApplicationParameters(String params)
+ {
+ if (params == null || params.isEmpty())
+ return;
+
+ if (!m_applicationParams.isEmpty())
+ m_applicationParams += " ";
+ m_applicationParams += params;
+ }
+
+ private void handleActivityRestart() {
+ if (QtNative.getStateDetails().isStarted) {
+ boolean updated = m_delegate.updateActivityAfterRestart(this);
+ if (!updated) {
+ // could not update the activity so restart the application
+ Intent intent = Intent.makeRestartActivityTask(getComponentName());
+ startActivity(intent);
+ QtNative.quitApp();
+ Runtime.getRuntime().exit(0);
+ }
+ }
+ }
+
+ @Override
+ public void setTheme(int resId) {
+ super.setTheme(resId);
+ m_isCustomThemeSet = true;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_ACTION_BAR);
+
+ if (!m_isCustomThemeSet) {
+ setTheme(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ?
+ android.R.style.Theme_DeviceDefault_DayNight :
+ android.R.style.Theme_Holo_Light);
+ }
+
+ m_delegate = new QtActivityDelegate(this);
+
+ handleActivityRestart();
+ addReferrer(getIntent());
+
+ QtActivityLoader loader = new QtActivityLoader(this);
+ loader.appendApplicationParameters(m_applicationParams);
+
+ loader.loadQtLibraries();
+ m_delegate.startNativeApplication(loader.getApplicationParameters(),
+ loader.getMainLibraryPath());
+ }
+
+ @Override
+ protected void onStart()
+ {
+ super.onStart();
+ }
+
+ @Override
+ protected void onRestart()
+ {
+ super.onRestart();
+ }
+
+ @Override
+ protected void onPause()
+ {
+ super.onPause();
+ if (Build.VERSION.SDK_INT < 24 || !isInMultiWindowMode())
+ QtNative.setApplicationState(QtNative.ApplicationState.ApplicationInactive);
+ m_delegate.displayManager().unregisterDisplayListener();
+ }
+
+ @Override
+ protected void onResume()
+ {
+ super.onResume();
+ QtNative.setApplicationState(QtNative.ApplicationState.ApplicationActive);
+ if (QtNative.getStateDetails().isStarted) {
+ m_delegate.displayManager().registerDisplayListener();
+ QtNative.updateWindow();
+ // Suspending the app clears the immersive mode, so we need to set it again.
+ m_delegate.displayManager().updateFullScreen();
+ }
+ }
+
+ @Override
+ protected void onStop()
+ {
+ super.onStop();
+ QtNative.setApplicationState(QtNative.ApplicationState.ApplicationSuspended);
+ }
+
+ @Override
+ protected void onDestroy()
+ {
+ super.onDestroy();
+ if (!m_retainNonConfigurationInstance) {
+ QtNative.terminateQt();
+ QtNative.setActivity(null);
+ QtNative.getQtThread().exit();
+ System.exit(0);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig)
+ {
+ super.onConfigurationChanged(newConfig);
+ m_delegate.handleUiModeChange(newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item)
+ {
+ m_delegate.setContextMenuVisible(false);
+ return QtNative.onContextItemSelected(item.getItemId(), item.isChecked());
+ }
+
+ @Override
+ public void onContextMenuClosed(Menu menu)
+ {
+ if (!m_delegate.isContextMenuVisible())
+ return;
+ m_delegate.setContextMenuVisible(false);
+ QtNative.onContextMenuClosed(menu);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
+ {
+ menu.clearHeader();
+ QtNative.onCreateContextMenu(menu);
+ m_delegate.setContextMenuVisible(true);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event)
+ {
+ boolean handleResult = m_delegate.getInputDelegate().handleDispatchKeyEvent(event);
+ if (QtNative.getStateDetails().isStarted && handleResult)
+ return true;
+
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event)
+ {
+ boolean handled = m_delegate.getInputDelegate().handleDispatchGenericMotionEvent(event);
+ if (QtNative.getStateDetails().isStarted && handled)
+ return true;
+
+ return super.dispatchGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ QtNative.ApplicationStateDetails stateDetails = QtNative.getStateDetails();
+ if (!stateDetails.isStarted || !stateDetails.nativePluginIntegrationReady)
+ return false;
+
+ return m_delegate.getInputDelegate().onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ QtNative.ApplicationStateDetails stateDetails = QtNative.getStateDetails();
+ if (!stateDetails.isStarted || !stateDetails.nativePluginIntegrationReady)
+ return false;
+
+ return m_delegate.getInputDelegate().onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ menu.clear();
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ boolean res = QtNative.onPrepareOptionsMenu(menu);
+ m_delegate.setActionBarVisibility(res && menu.size() > 0);
+ return res;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked());
+ }
+
+ @Override
+ public void onOptionsMenuClosed(Menu menu)
+ {
+ QtNative.onOptionsMenuClosed(menu);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState)
+ {
+ super.onRestoreInstanceState(savedInstanceState);
+ QtNative.setStarted(savedInstanceState.getBoolean("Started"));
+ int savedSystemUiVisibility = savedInstanceState.getInt("SystemUiVisibility");
+ m_delegate.displayManager().setSystemUiVisibility(savedSystemUiVisibility);
+ // FIXME restore all surfaces
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance()
+ {
+ super.onRetainNonConfigurationInstance();
+ m_retainNonConfigurationInstance = true;
+ return true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ outState.putInt("SystemUiVisibility", m_delegate.displayManager().systemUiVisibility());
+ outState.putBoolean("Started", QtNative.getStateDetails().isStarted);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus)
+ {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus)
+ m_delegate.displayManager().updateFullScreen();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent)
+ {
+ QtNative.onNewIntent(intent);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+ QtNative.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
+ {
+ QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @UsedFromNativeCode
+ public void hideSplashScreen(final int duration)
+ {
+ m_delegate.hideSplashScreen(duration);
+ }
+
+ @UsedFromNativeCode
+ QtActivityDelegateBase getActivityDelegate()
+ {
+ return m_delegate;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
new file mode 100644
index 0000000000..f9f1dc3b10
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
@@ -0,0 +1,444 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsetsController;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+
+import java.util.HashMap;
+
+class QtActivityDelegate extends QtActivityDelegateBase
+{
+ private static final String QtTAG = "QtActivityDelegate";
+
+ private QtRootLayout m_layout = null;
+ private ImageView m_splashScreen = null;
+ private boolean m_splashScreenSticky = false;
+
+ private View m_dummyView = null;
+ private HashMap<Integer, View> m_nativeViews = new HashMap<Integer, View>();
+
+
+ QtActivityDelegate(Activity activity)
+ {
+ super(activity);
+
+ setActionBarVisibility(false);
+ setActivityBackgroundDrawable();
+ }
+
+
+ @UsedFromNativeCode
+ @Override
+ QtLayout getQtLayout()
+ {
+ return m_layout;
+ }
+
+ @UsedFromNativeCode
+ @Override
+ void setSystemUiVisibility(int systemUiVisibility)
+ {
+ QtNative.runAction(() -> {
+ m_displayManager.setSystemUiVisibility(systemUiVisibility);
+ m_layout.requestLayout();
+ QtNative.updateWindow();
+ });
+ }
+
+ @Override
+ public boolean updateActivityAfterRestart(Activity activity) {
+ boolean updated = super.updateActivityAfterRestart(activity);
+ // TODO verify whether this is even needed, the last I checked the initMembers
+ // recreates the layout anyway
+ // update the new activity content view to old layout
+ ViewGroup layoutParent = (ViewGroup)m_layout.getParent();
+ if (layoutParent != null)
+ layoutParent.removeView(m_layout);
+
+ m_activity.setContentView(m_layout);
+
+ return updated;
+ }
+
+ @Override
+ void startNativeApplicationImpl(String appParams, String mainLib)
+ {
+ m_layout.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ QtNative.startApplication(appParams, mainLib);
+ m_layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
+ @Override
+ protected void setUpLayout()
+ {
+ int orientation = m_activity.getResources().getConfiguration().orientation;
+ m_layout = new QtRootLayout(m_activity);
+
+ setUpSplashScreen(orientation);
+ m_activity.registerForContextMenu(m_layout);
+ m_activity.setContentView(m_layout,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ QtDisplayManager.handleOrientationChanges(m_activity);
+
+ handleUiModeChange(m_activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
+
+ Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+ ? m_activity.getWindowManager().getDefaultDisplay()
+ : m_activity.getDisplay();
+ QtDisplayManager.handleRefreshRateChanged(QtDisplayManager.getRefreshRate(display));
+
+ m_layout.getViewTreeObserver().addOnPreDrawListener(() -> {
+ if (!m_inputDelegate.isKeyboardVisible())
+ return true;
+
+ Rect r = new Rect();
+ m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
+ DisplayMetrics metrics = new DisplayMetrics();
+ m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ final int kbHeight = metrics.heightPixels - r.bottom;
+ if (kbHeight < 0) {
+ m_inputDelegate.setKeyboardVisibility(false, System.nanoTime());
+ return true;
+ }
+ final int[] location = new int[2];
+ m_layout.getLocationOnScreen(location);
+ QtInputDelegate.keyboardGeometryChanged(location[0], r.bottom - location[1],
+ r.width(), kbHeight);
+ return true;
+ });
+ registerGlobalFocusChangeListener(m_layout);
+ m_inputDelegate.setEditPopupMenu(new EditPopupMenu(m_activity, m_layout));
+ }
+
+ @Override
+ protected void setUpSplashScreen(int orientation)
+ {
+ try {
+ ActivityInfo info = m_activity.getPackageManager().getActivityInfo(
+ m_activity.getComponentName(),
+ PackageManager.GET_META_DATA);
+
+ String splashScreenKey = "android.app.splash_screen_drawable_"
+ + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
+ if (!info.metaData.containsKey(splashScreenKey))
+ splashScreenKey = "android.app.splash_screen_drawable";
+
+ if (info.metaData.containsKey(splashScreenKey)) {
+ m_splashScreenSticky =
+ info.metaData.containsKey("android.app.splash_screen_sticky") &&
+ info.metaData.getBoolean("android.app.splash_screen_sticky");
+
+ int id = info.metaData.getInt(splashScreenKey);
+ m_splashScreen = new ImageView(m_activity);
+ m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(
+ id, m_activity.getTheme()));
+ m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
+ m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ m_layout.addView(m_splashScreen);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void hideSplashScreen(final int duration)
+ {
+ QtNative.runAction(() -> {
+ if (m_splashScreen == null)
+ return;
+
+ if (duration <= 0) {
+ m_layout.removeView(m_splashScreen);
+ m_splashScreen = null;
+ return;
+ }
+
+ final Animation fadeOut = new AlphaAnimation(1, 0);
+ fadeOut.setInterpolator(new AccelerateInterpolator());
+ fadeOut.setDuration(duration);
+
+ fadeOut.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ hideSplashScreen(0);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+ });
+
+ m_splashScreen.startAnimation(fadeOut);
+ });
+ }
+
+ @UsedFromNativeCode
+ public void initializeAccessibility()
+ {
+ QtNative.runAction(() -> {
+ // FIXME make QtAccessibilityDelegate window based
+ if (m_layout != null)
+ m_accessibilityDelegate = new QtAccessibilityDelegate(m_layout);
+ else
+ Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
+ });
+ }
+
+ void handleUiModeChange(int uiMode)
+ {
+ // QTBUG-108365
+ if (Build.VERSION.SDK_INT >= 30) {
+ // Since 29 version we are using Theme_DeviceDefault_DayNight
+ Window window = m_activity.getWindow();
+ WindowInsetsController controller = window.getInsetsController();
+ if (controller != null) {
+ // set APPEARANCE_LIGHT_STATUS_BARS if needed
+ int appearanceLight = Color.luminance(window.getStatusBarColor()) > 0.5 ?
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS : 0;
+ controller.setSystemBarsAppearance(appearanceLight,
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
+ }
+ }
+ switch (uiMode) {
+ case Configuration.UI_MODE_NIGHT_NO:
+ ExtractStyle.runIfNeeded(m_activity, false);
+ QtDisplayManager.handleUiDarkModeChanged(0);
+ break;
+ case Configuration.UI_MODE_NIGHT_YES:
+ ExtractStyle.runIfNeeded(m_activity, true);
+ QtDisplayManager.handleUiDarkModeChanged(1);
+ break;
+ }
+ }
+
+ @UsedFromNativeCode
+ public void resetOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.invalidateOptionsMenu());
+ }
+
+ @UsedFromNativeCode
+ public void openOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.openOptionsMenu());
+ }
+
+ private boolean m_contextMenuVisible = false;
+
+ public void onCreatePopupMenu(Menu menu)
+ {
+ QtNative.fillContextMenu(menu);
+ m_contextMenuVisible = true;
+ }
+
+ @UsedFromNativeCode
+ @Override
+ public void openContextMenu(final int x, final int y, final int w, final int h)
+ {
+ m_layout.postDelayed(() -> {
+ final QtEditText focusedEditText = m_inputDelegate.getCurrentQtEditText();
+ if (focusedEditText == null) {
+ Log.w(QtTAG, "No focused view when trying to open context menu");
+ return;
+ }
+ m_layout.setLayoutParams(focusedEditText, new QtLayout.LayoutParams(w, h, x, y), false);
+ PopupMenu popup = new PopupMenu(m_activity, focusedEditText);
+ QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
+ popup.setOnMenuItemClickListener(menuItem ->
+ m_activity.onContextItemSelected(menuItem));
+ popup.setOnDismissListener(popupMenu ->
+ m_activity.onContextMenuClosed(popupMenu.getMenu()));
+ popup.show();
+ }, 100);
+ }
+
+ @UsedFromNativeCode
+ public void closeContextMenu()
+ {
+ QtNative.runAction(() -> m_activity.closeContextMenu());
+ }
+
+ @Override
+ void setActionBarVisibility(boolean visible)
+ {
+ if (m_activity.getActionBar() == null)
+ return;
+ if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible)
+ m_activity.getActionBar().hide();
+ else
+ m_activity.getActionBar().show();
+ }
+
+ @UsedFromNativeCode
+ @Override
+ public void addTopLevelWindow(final QtWindow window)
+ {
+ if (window == null)
+ return;
+
+ QtNative.runAction(()-> {
+ if (m_topLevelWindows.size() == 0) {
+ if (m_dummyView != null) {
+ m_layout.removeView(m_dummyView);
+ m_dummyView = null;
+ }
+ }
+
+ window.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ m_layout.addView(window, m_topLevelWindows.size());
+ m_topLevelWindows.put(window.getId(), window);
+ if (!m_splashScreenSticky)
+ hideSplashScreen();
+ });
+ }
+
+ @UsedFromNativeCode
+ @Override
+ void removeTopLevelWindow(final int id)
+ {
+ QtNative.runAction(()-> {
+ if (m_topLevelWindows.containsKey(id)) {
+ QtWindow window = m_topLevelWindows.remove(id);
+ if (m_topLevelWindows.isEmpty()) {
+ // Keep last frame in stack until it is replaced to get correct
+ // shutdown transition
+ m_dummyView = window;
+ } else {
+ m_layout.removeView(window);
+ }
+ }
+ });
+ }
+
+ @UsedFromNativeCode
+ @Override
+ void bringChildToFront(final int id)
+ {
+ QtNative.runAction(() -> {
+ QtWindow window = m_topLevelWindows.get(id);
+ if (window != null)
+ m_layout.moveChild(window, m_topLevelWindows.size() - 1);
+ });
+ }
+
+ @UsedFromNativeCode
+ @Override
+ void bringChildToBack(int id)
+ {
+ QtNative.runAction(() -> {
+ QtWindow window = m_topLevelWindows.get(id);
+ if (window != null)
+ m_layout.moveChild(window, 0);
+ });
+ }
+
+ @Override
+ QtAccessibilityDelegate createAccessibilityDelegate()
+ {
+ if (m_layout != null)
+ return new QtAccessibilityDelegate(m_layout);
+
+ Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
+ return null;
+ }
+
+ private void setActivityBackgroundDrawable()
+ {
+ TypedValue attr = new TypedValue();
+ m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground,
+ attr, true);
+ Drawable backgroundDrawable;
+ if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+ attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ backgroundDrawable = new ColorDrawable(attr.data);
+ } else {
+ backgroundDrawable = m_activity.getResources().
+ getDrawable(attr.resourceId, m_activity.getTheme());
+ }
+
+ m_activity.getWindow().setBackgroundDrawable(backgroundDrawable);
+ }
+
+ // TODO: QTBUG-122761 To be removed after QtAndroidAutomotive does not depend on it.
+ @UsedFromNativeCode
+ public void insertNativeView(int id, View view, int x, int y, int w, int h)
+ {
+ QtNative.runAction(()-> {
+ if (m_dummyView != null) {
+ m_layout.removeView(m_dummyView);
+ m_dummyView = null;
+ }
+
+ if (m_nativeViews.containsKey(id))
+ m_layout.removeView(m_nativeViews.remove(id));
+
+ if (w < 0 || h < 0) {
+ view.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ } else {
+ view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
+ }
+
+ view.setId(id);
+ m_layout.addView(view);
+ m_nativeViews.put(id, view);
+ });
+ }
+
+ // TODO: QTBUG-122761 To be removed after QtAndroidAutomotive does not depend on it.
+ @UsedFromNativeCode
+ public void setNativeViewGeometry(int id, int x, int y, int w, int h)
+ {
+ QtNative.runAction(() -> {
+ if (m_nativeViews.containsKey(id)) {
+ View view = m_nativeViews.get(id);
+ view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
+ } else {
+ Log.e(QtTAG, "View " + id + " not found!");
+ }
+ });
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java
new file mode 100644
index 0000000000..8625c1f601
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java
@@ -0,0 +1,259 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsetsController;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+
+import java.util.HashMap;
+
+abstract class QtActivityDelegateBase
+{
+ protected Activity m_activity;
+ protected HashMap<Integer, QtWindow> m_topLevelWindows;
+ protected QtAccessibilityDelegate m_accessibilityDelegate = null;
+ protected QtDisplayManager m_displayManager = null;
+ protected QtInputDelegate m_inputDelegate = null;
+
+ private boolean m_membersInitialized = false;
+ private boolean m_contextMenuVisible = false;
+
+ // Subclass must implement these
+ abstract void startNativeApplicationImpl(String appParams, String mainLib);
+ abstract QtAccessibilityDelegate createAccessibilityDelegate();
+ abstract QtLayout getQtLayout();
+
+ // With these we are okay with default implementation doing nothing
+ void setUpLayout() {}
+ void setUpSplashScreen(int orientation) {}
+ void hideSplashScreen(final int duration) {}
+ void openContextMenu(final int x, final int y, final int w, final int h) {}
+ void setActionBarVisibility(boolean visible) {}
+ void addTopLevelWindow(final QtWindow window) {}
+ void removeTopLevelWindow(final int id) {}
+ void bringChildToFront(final int id) {}
+ void bringChildToBack(int id) {}
+ void setSystemUiVisibility(int systemUiVisibility) {}
+
+ QtActivityDelegateBase(Activity activity)
+ {
+ m_activity = activity;
+ // Set native context
+ QtNative.setActivity(m_activity);
+ }
+
+ QtDisplayManager displayManager() {
+ return m_displayManager;
+ }
+
+ @UsedFromNativeCode
+ QtInputDelegate getInputDelegate() {
+ return m_inputDelegate;
+ }
+
+ void setContextMenuVisible(boolean contextMenuVisible)
+ {
+ m_contextMenuVisible = contextMenuVisible;
+ }
+
+ boolean isContextMenuVisible()
+ {
+ return m_contextMenuVisible;
+ }
+
+ public boolean updateActivityAfterRestart(Activity activity) {
+ try {
+ // set new activity
+ m_activity = activity;
+ QtNative.setActivity(m_activity);
+
+ // force c++ native activity object to update
+ return QtNative.updateNativeActivity();
+ } catch (Exception e) {
+ Log.w(QtNative.QtTAG, "Failed to update the activity.");
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public void startNativeApplication(String appParams, String mainLib)
+ {
+ if (m_membersInitialized)
+ return;
+ initMembers();
+ startNativeApplicationImpl(appParams, mainLib);
+ }
+
+ void initMembers()
+ {
+ m_membersInitialized = true;
+ m_topLevelWindows = new HashMap<Integer, QtWindow>();
+
+ m_displayManager = new QtDisplayManager(m_activity);
+ m_displayManager.registerDisplayListener();
+
+ QtInputDelegate.KeyboardVisibilityListener keyboardVisibilityListener =
+ () -> m_displayManager.updateFullScreen();
+ m_inputDelegate = new QtInputDelegate(m_activity, keyboardVisibilityListener);
+
+ try {
+ PackageManager pm = m_activity.getPackageManager();
+ ActivityInfo activityInfo = pm.getActivityInfo(m_activity.getComponentName(), 0);
+ m_inputDelegate.setSoftInputMode(activityInfo.softInputMode);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ setUpLayout();
+ }
+
+ protected void registerGlobalFocusChangeListener(final View view) {
+ view.getViewTreeObserver().addOnGlobalFocusChangeListener(this::onGlobalFocusChanged);
+ }
+
+ private void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ if (newFocus instanceof QtEditText) {
+ final QtWindow newWindow = (QtWindow) newFocus.getParent();
+ QtWindow.windowFocusChanged(true, newWindow.getId());
+ m_inputDelegate.setFocusedView((QtEditText) newFocus);
+ } else {
+ int id = -1;
+ if (oldFocus instanceof QtEditText) {
+ final QtWindow oldWindow = (QtWindow) oldFocus.getParent();
+ id = oldWindow.getId();
+ }
+ QtWindow.windowFocusChanged(false, id);
+ m_inputDelegate.setFocusedView(null);
+ }
+ }
+
+ public void hideSplashScreen()
+ {
+ hideSplashScreen(0);
+ }
+
+ @UsedFromNativeCode
+ public void notifyLocationChange(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyLocationChange(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyObjectHide(int viewId, int parentId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyObjectFocus(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyObjectFocus(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyValueChanged(int viewId, String value)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyValueChanged(viewId, value);
+ }
+
+ @UsedFromNativeCode
+ public void notifyScrolledEvent(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyScrolledEvent(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void initializeAccessibility()
+ {
+ QtNative.runAction(() -> {
+ m_accessibilityDelegate = createAccessibilityDelegate();
+ });
+ }
+
+ void handleUiModeChange(int uiMode)
+ {
+ // QTBUG-108365
+ if (Build.VERSION.SDK_INT >= 30) {
+ // Since 29 version we are using Theme_DeviceDefault_DayNight
+ Window window = m_activity.getWindow();
+ WindowInsetsController controller = window.getInsetsController();
+ if (controller != null) {
+ // set APPEARANCE_LIGHT_STATUS_BARS if needed
+ int appearanceLight = Color.luminance(window.getStatusBarColor()) > 0.5 ?
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS : 0;
+ controller.setSystemBarsAppearance(appearanceLight,
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
+ }
+ }
+ switch (uiMode) {
+ case Configuration.UI_MODE_NIGHT_NO:
+ ExtractStyle.runIfNeeded(m_activity, false);
+ QtDisplayManager.handleUiDarkModeChanged(0);
+ break;
+ case Configuration.UI_MODE_NIGHT_YES:
+ ExtractStyle.runIfNeeded(m_activity, true);
+ QtDisplayManager.handleUiDarkModeChanged(1);
+ break;
+ }
+ }
+
+ @UsedFromNativeCode
+ public void resetOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.invalidateOptionsMenu());
+ }
+
+ @UsedFromNativeCode
+ public void openOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.openOptionsMenu());
+ }
+
+ public void onCreatePopupMenu(Menu menu)
+ {
+ QtNative.fillContextMenu(menu);
+ m_contextMenuVisible = true;
+ }
+
+ @UsedFromNativeCode
+ public void closeContextMenu()
+ {
+ QtNative.runAction(() -> m_activity.closeContextMenu());
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java
new file mode 100644
index 0000000000..1b2e8e49a0
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java
@@ -0,0 +1,151 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.system.Os;
+import android.util.Base64;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+
+class QtActivityLoader extends QtLoader {
+ private final Activity m_activity;
+
+ public QtActivityLoader(Activity activity)
+ {
+ super(new ContextWrapper(activity));
+ m_activity = activity;
+
+ extractContextMetaData();
+ }
+
+ private void showErrorDialog() {
+ if (m_activity == null) {
+ Log.w(QtTAG, "cannot show the error dialog from a null activity object");
+ return;
+ }
+ Resources resources = m_activity.getResources();
+ String packageName = m_activity.getPackageName();
+ AlertDialog errorDialog = new AlertDialog.Builder(m_activity).create();
+ @SuppressLint("DiscouragedApi") int id = resources.getIdentifier(
+ "fatal_error_msg", "string", packageName);
+ errorDialog.setMessage(resources.getString(id));
+ errorDialog.setButton(Dialog.BUTTON_POSITIVE, resources.getString(android.R.string.ok),
+ (dialog, which) -> finish());
+ errorDialog.show();
+ }
+
+ @Override
+ protected void finish() {
+ if (m_activity == null) {
+ Log.w(QtTAG, "finish() called when activity object is null");
+ return;
+ }
+ showErrorDialog();
+ m_activity.finish();
+ }
+
+ private String getDecodedUtfString(String str)
+ {
+ byte[] decodedExtraEnvVars = Base64.decode(str, Base64.DEFAULT);
+ return new String(decodedExtraEnvVars, StandardCharsets.UTF_8);
+ }
+
+ int getAppIconSize()
+ {
+ int size = m_activity.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ if (size < 36 || size > 512) { // check size sanity
+ DisplayMetrics metrics = new DisplayMetrics();
+ m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ size = metrics.densityDpi / 10 * 3;
+ if (size < 36)
+ size = 36;
+
+ if (size > 512)
+ size = 512;
+ }
+
+ return size;
+ }
+
+ private void setupStyleExtraction()
+ {
+ int displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
+ setEnvironmentVariable("QT_ANDROID_THEME_DISPLAY_DPI", String.valueOf(displayDensity));
+
+ String extractOption = getMetaData("android.app.extract_android_style");
+ if (extractOption.equals("full"))
+ setEnvironmentVariable("QT_USE_ANDROID_NATIVE_STYLE", String.valueOf(1));
+
+ String stylePath = ExtractStyle.setup(m_activity, extractOption, displayDensity);
+ setEnvironmentVariable("ANDROID_STYLE_PATH", stylePath);
+ }
+
+ @Override
+ protected void extractContextMetaData()
+ {
+ super.extractContextMetaData();
+
+ setEnvironmentVariable("QT_USE_ANDROID_NATIVE_DIALOGS", String.valueOf(1));
+ setEnvironmentVariable("QT_ANDROID_APP_ICON_SIZE", String.valueOf(getAppIconSize()));
+
+ setupStyleExtraction();
+
+ Intent intent = m_activity.getIntent();
+ if (intent == null) {
+ Log.w(QtTAG, "Null Intent from the current Activity.");
+ return;
+ }
+
+ String intentArgs = intent.getStringExtra("applicationArguments");
+ if (intentArgs != null)
+ appendApplicationParameters(intentArgs);
+
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ Log.w(QtTAG, "Null extras from the Activity's intent.");
+ return;
+ }
+
+ int flags = m_activity.getApplicationInfo().flags;
+ boolean isDebuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+
+ if (isDebuggable) {
+ if (extras.containsKey("extraenvvars")) {
+ String extraEnvVars = extras.getString("extraenvvars");
+ setEnvironmentVariables(getDecodedUtfString(extraEnvVars));
+ }
+
+ if (extras.containsKey("extraappparams")) {
+ String extraAppParams = extras.getString("extraappparams");
+ appendApplicationParameters(getDecodedUtfString(extraAppParams));
+ }
+
+ m_debuggerSleepMs = 3000;
+ if (Os.getenv("QT_ANDROID_DEBUGGER_MAIN_THREAD_SLEEP_MS") != null) {
+ try {
+ m_debuggerSleepMs = Integer.parseInt(Os.getenv("QT_ANDROID_DEBUGGER_MAIN_THREAD_SLEEP_MS"));
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ } else {
+ Log.d(QtNative.QtTAG, "Not in debug mode! It is not allowed to use extra arguments " +
+ "in non-debug mode.");
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java
new file mode 100644
index 0000000000..de572266b9
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Application;
+
+public class QtApplicationBase extends Application {
+ @Override
+ public void onTerminate() {
+ QtNative.terminateQt();
+ QtNative.getQtThread().exit();
+ super.onTerminate();
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtClipboardManager.java b/src/android/jar/src/org/qtproject/qt/android/QtClipboardManager.java
new file mode 100644
index 0000000000..ac0d4e1890
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtClipboardManager.java
@@ -0,0 +1,232 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.concurrent.Semaphore;
+
+class QtClipboardManager
+{
+ public static native void onClipboardDataChanged(long nativePointer);
+
+ private final static String TAG = "QtClipboardManager";
+ private ClipboardManager m_clipboardManager = null;
+ private boolean m_usePrimaryClip = false;
+ private final long m_nativePointer;
+
+ public QtClipboardManager(Context context, long nativePointer)
+ {
+ m_nativePointer = nativePointer;
+ registerClipboardManager(context);
+ }
+
+ private void registerClipboardManager(Context context)
+ {
+ if (context != null) {
+ final Semaphore semaphore = new Semaphore(0);
+ QtNative.runAction(() -> {
+ m_clipboardManager =
+ (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (m_clipboardManager != null) {
+ m_clipboardManager.addPrimaryClipChangedListener(
+ () -> onClipboardDataChanged(m_nativePointer));
+ }
+ semaphore.release();
+ });
+ try {
+ semaphore.acquire();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @UsedFromNativeCode
+ public void clearClipData()
+ {
+ if (m_clipboardManager != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ m_clipboardManager.clearPrimaryClip();
+ } else {
+ String[] mimeTypes = { "application/octet-stream" };
+ ClipData data = new ClipData("", mimeTypes, new ClipData.Item(new Intent()));
+ m_clipboardManager.setPrimaryClip(data);
+ }
+ }
+ m_usePrimaryClip = false;
+ }
+
+ @UsedFromNativeCode
+ public void setClipboardText(Context context, String text)
+ {
+ if (m_clipboardManager != null) {
+ ClipData clipData = ClipData.newPlainText("text/plain", text);
+ updatePrimaryClip(clipData, context);
+ }
+ }
+
+ public static boolean hasClipboardText(Context context)
+ {
+ ClipboardManager clipboardManager =
+ (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+
+ if (clipboardManager == null)
+ return false;
+
+ ClipDescription description = clipboardManager.getPrimaryClipDescription();
+ // getPrimaryClipDescription can fail if the app does not have input focus
+ if (description == null)
+ return false;
+
+ for (int i = 0; i < description.getMimeTypeCount(); ++i) {
+ String itemMimeType = description.getMimeType(i);
+ if (itemMimeType.matches("text/(.*)"))
+ return true;
+ }
+ return false;
+ }
+
+ @UsedFromNativeCode
+ public boolean hasClipboardText()
+ {
+ return hasClipboardMimeType("text/(.*)");
+ }
+
+ @UsedFromNativeCode
+ public String getClipboardText()
+ {
+ try {
+ if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
+ ClipData primaryClip = m_clipboardManager.getPrimaryClip();
+ if (primaryClip != null) {
+ for (int i = 0; i < primaryClip.getItemCount(); ++i)
+ if (primaryClip.getItemAt(i).getText() != null)
+ return primaryClip.getItemAt(i).getText().toString();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get clipboard data", e);
+ }
+ return "";
+ }
+
+ private void updatePrimaryClip(ClipData clipData, Context context)
+ {
+ try {
+ if (m_usePrimaryClip) {
+ ClipData clip = m_clipboardManager.getPrimaryClip();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Objects.requireNonNull(clip).addItem(context.getContentResolver(),
+ clipData.getItemAt(0));
+ } else {
+ Objects.requireNonNull(clip).addItem(clipData.getItemAt(0));
+ }
+ m_clipboardManager.setPrimaryClip(clip);
+ } else {
+ m_clipboardManager.setPrimaryClip(clipData);
+ m_usePrimaryClip = true;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to set clipboard data", e);
+ }
+ }
+
+ @UsedFromNativeCode
+ public void setClipboardHtml(Context context, String text, String html)
+ {
+ if (m_clipboardManager != null) {
+ ClipData clipData = ClipData.newHtmlText("text/html", text, html);
+ updatePrimaryClip(clipData, context);
+ }
+ }
+
+ private boolean hasClipboardMimeType(String mimeType)
+ {
+ if (m_clipboardManager == null)
+ return false;
+
+ ClipDescription description = m_clipboardManager.getPrimaryClipDescription();
+ // getPrimaryClipDescription can fail if the app does not have input focus
+ if (description == null)
+ return false;
+
+ for (int i = 0; i < description.getMimeTypeCount(); ++i) {
+ String itemMimeType = description.getMimeType(i);
+ if (itemMimeType.matches(mimeType))
+ return true;
+ }
+ return false;
+ }
+
+ @UsedFromNativeCode
+ public boolean hasClipboardHtml()
+ {
+ return hasClipboardMimeType("text/html");
+ }
+
+ @UsedFromNativeCode
+ public String getClipboardHtml()
+ {
+ try {
+ if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
+ ClipData primaryClip = m_clipboardManager.getPrimaryClip();
+ if (primaryClip != null) {
+ for (int i = 0; i < primaryClip.getItemCount(); ++i)
+ if (primaryClip.getItemAt(i).getHtmlText() != null)
+ return primaryClip.getItemAt(i).getHtmlText();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get clipboard data", e);
+ }
+ return "";
+ }
+
+ @UsedFromNativeCode
+ public void setClipboardUri(Context context, String uriString)
+ {
+ if (m_clipboardManager != null) {
+ ClipData clipData = ClipData.newUri(context.getContentResolver(), "text/uri-list",
+ Uri.parse(uriString));
+ updatePrimaryClip(clipData, context);
+ }
+ }
+
+ @UsedFromNativeCode
+ public boolean hasClipboardUri()
+ {
+ return hasClipboardMimeType("text/uri-list");
+ }
+
+ @UsedFromNativeCode
+ private String[] getClipboardUris()
+ {
+ ArrayList<String> uris = new ArrayList<>();
+ try {
+ if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
+ ClipData primaryClip = m_clipboardManager.getPrimaryClip();
+ if (primaryClip != null) {
+ for (int i = 0; i < primaryClip.getItemCount(); ++i)
+ if (primaryClip.getItemAt(i).getUri() != null)
+ uris.add(primaryClip.getItemAt(i).getUri().toString());
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get clipboard data", e);
+ }
+ String[] strings = new String[uris.size()];
+ strings = uris.toArray(strings);
+ return strings;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java
new file mode 100644
index 0000000000..b6a52fb22f
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java
@@ -0,0 +1,287 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Size;
+import android.view.Display;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class QtDisplayManager {
+
+ // screen methods
+ public static native void setDisplayMetrics(int screenWidthPixels, int screenHeightPixels,
+ int availableLeftPixels, int availableTopPixels,
+ int availableWidthPixels, int availableHeightPixels,
+ double XDpi, double YDpi, double scaledDensity,
+ double density, float refreshRate);
+ public static native void handleOrientationChanged(int newRotation, int nativeOrientation);
+ public static native void handleRefreshRateChanged(float refreshRate);
+ public static native void handleUiDarkModeChanged(int newUiMode);
+ public static native void handleScreenAdded(int displayId);
+ public static native void handleScreenChanged(int displayId);
+ public static native void handleScreenRemoved(int displayId);
+ // screen methods
+
+ // Keep in sync with QtAndroid::SystemUiVisibility in androidjnimain.h
+ public static final int SYSTEM_UI_VISIBILITY_NORMAL = 0;
+ public static final int SYSTEM_UI_VISIBILITY_FULLSCREEN = 1;
+ public static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2;
+ private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
+
+ private static int m_previousRotation = -1;
+
+ private DisplayManager.DisplayListener m_displayListener = null;
+ private final Activity m_activity;
+
+ QtDisplayManager(Activity activity)
+ {
+ m_activity = activity;
+ initDisplayListener();
+ }
+
+ private void initDisplayListener() {
+ m_displayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ QtDisplayManager.handleScreenAdded(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+ ? m_activity.getWindowManager().getDefaultDisplay()
+ : m_activity.getDisplay();
+ float refreshRate = getRefreshRate(display);
+ QtDisplayManager.handleRefreshRateChanged(refreshRate);
+ QtDisplayManager.handleScreenChanged(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ QtDisplayManager.handleScreenRemoved(displayId);
+ }
+ };
+ }
+
+ static void handleOrientationChanges(Activity activity)
+ {
+ int currentRotation = getDisplayRotation(activity);
+ if (m_previousRotation == currentRotation)
+ return;
+ int nativeOrientation = getNativeOrientation(activity, currentRotation);
+ QtDisplayManager.handleOrientationChanged(currentRotation, nativeOrientation);
+ m_previousRotation = currentRotation;
+ }
+
+ public static int getDisplayRotation(Activity activity) {
+ Display display = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ?
+ activity.getWindowManager().getDefaultDisplay() :
+ activity.getDisplay();
+
+ return display != null ? display.getRotation() : 0;
+ }
+
+ private static int getNativeOrientation(Activity activity, int rotation)
+ {
+ int orientation = activity.getResources().getConfiguration().orientation;
+ boolean rot90 = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ boolean isLandscape = (orientation == Configuration.ORIENTATION_LANDSCAPE);
+ if ((isLandscape && !rot90) || (!isLandscape && rot90))
+ return Configuration.ORIENTATION_LANDSCAPE;
+
+ return Configuration.ORIENTATION_PORTRAIT;
+ }
+
+ static float getRefreshRate(Display display)
+ {
+ return display != null ? display.getRefreshRate() : 60.0f;
+ }
+
+ public void registerDisplayListener()
+ {
+ DisplayManager displayManager =
+ (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.registerDisplayListener(m_displayListener, null);
+ }
+
+ public void unregisterDisplayListener()
+ {
+ DisplayManager displayManager =
+ (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.unregisterDisplayListener(m_displayListener);
+ }
+
+ public void setSystemUiVisibility(int systemUiVisibility)
+ {
+ if (m_systemUiVisibility == systemUiVisibility)
+ return;
+
+ m_systemUiVisibility = systemUiVisibility;
+
+ int systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_VISIBLE;
+ switch (m_systemUiVisibility) {
+ case SYSTEM_UI_VISIBILITY_NORMAL:
+ m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ }
+ break;
+ case SYSTEM_UI_VISIBILITY_FULLSCREEN:
+ m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.INVISIBLE;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+ }
+ break;
+ case SYSTEM_UI_VISIBILITY_TRANSLUCENT:
+ m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ }
+ break;
+ }
+ m_activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags);
+ }
+
+ public int systemUiVisibility()
+ {
+ return m_systemUiVisibility;
+ }
+
+ public void updateFullScreen()
+ {
+ if (m_systemUiVisibility == SYSTEM_UI_VISIBILITY_FULLSCREEN) {
+ m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
+ setSystemUiVisibility(SYSTEM_UI_VISIBILITY_FULLSCREEN);
+ }
+ }
+
+ @UsedFromNativeCode
+ public static Display getDisplay(Context context, int displayId)
+ {
+ DisplayManager displayManager =
+ (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ if (displayManager != null) {
+ return displayManager.getDisplay(displayId);
+ }
+ return null;
+ }
+
+ @UsedFromNativeCode
+ public static List<Display> getAvailableDisplays(Context context)
+ {
+ DisplayManager displayManager =
+ (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ if (displayManager != null) {
+ Display[] displays = displayManager.getDisplays();
+ return Arrays.asList(displays);
+ }
+ return new ArrayList<>();
+ }
+
+ @UsedFromNativeCode
+ public static Size getDisplaySize(Context displayContext, Display display)
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ DisplayMetrics realMetrics = new DisplayMetrics();
+ display.getRealMetrics(realMetrics);
+ return new Size(realMetrics.widthPixels, realMetrics.heightPixels);
+ }
+
+ Context windowsContext = displayContext.createWindowContext(
+ WindowManager.LayoutParams.TYPE_APPLICATION, null);
+ WindowManager windowManager =
+ (WindowManager) windowsContext.getSystemService(Context.WINDOW_SERVICE);
+ WindowMetrics windowsMetrics = windowManager.getCurrentWindowMetrics();
+ Rect bounds = windowsMetrics.getBounds();
+ return new Size(bounds.width(), bounds.height());
+ }
+
+ public static void setApplicationDisplayMetrics(Activity activity, int width, int height)
+ {
+ if (activity == null)
+ return;
+
+ final WindowInsets rootInsets = activity.getWindow().getDecorView().getRootWindowInsets();
+ final WindowManager windowManager = activity.getWindowManager();
+ Display display;
+
+ int insetLeft;
+ int insetTop;
+
+ int maxWidth;
+ int maxHeight;
+
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ display = windowManager.getDefaultDisplay();
+
+ final DisplayMetrics maxMetrics = new DisplayMetrics();
+ display.getRealMetrics(maxMetrics);
+ maxWidth = maxMetrics.widthPixels;
+ maxHeight = maxMetrics.heightPixels;
+
+ insetLeft = rootInsets.getStableInsetLeft();
+ insetTop = rootInsets.getStableInsetTop();
+ } else {
+ display = activity.getDisplay();
+
+ final WindowMetrics maxMetrics = windowManager.getMaximumWindowMetrics();
+ maxWidth = maxMetrics.getBounds().width();
+ maxHeight = maxMetrics.getBounds().height();
+
+ insetLeft = rootInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).left;
+ insetTop = rootInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).top;
+ }
+
+ final DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
+
+ double density = displayMetrics.density;
+ double scaledDensity = displayMetrics.scaledDensity;
+
+ setDisplayMetrics(maxWidth, maxHeight, insetLeft, insetTop,
+ width, height, getXDpi(displayMetrics), getYDpi(displayMetrics),
+ scaledDensity, density, getRefreshRate(display));
+ }
+
+ public static float getXDpi(final DisplayMetrics metrics) {
+ if (metrics.xdpi < android.util.DisplayMetrics.DENSITY_LOW)
+ return android.util.DisplayMetrics.DENSITY_LOW;
+ return metrics.xdpi;
+ }
+
+ public static float getYDpi(final DisplayMetrics metrics) {
+ if (metrics.ydpi < android.util.DisplayMetrics.DENSITY_LOW)
+ return android.util.DisplayMetrics.DENSITY_LOW;
+ return metrics.ydpi;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt/android/QtEditText.java
new file mode 100644
index 0000000000..4524887242
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEditText.java
@@ -0,0 +1,221 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.text.InputType;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.KeyEvent;
+
+import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
+
+class QtEditText extends View
+{
+ int m_initialCapsMode = 0;
+ int m_imeOptions = 0;
+ int m_inputType = InputType.TYPE_CLASS_TEXT;
+ boolean m_optionsChanged = false;
+ QtInputConnection m_inputConnection = null;
+
+ // input method hints - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
+ private final int ImhHiddenText = 0x1;
+ private final int ImhSensitiveData = 0x2;
+ private final int ImhNoAutoUppercase = 0x4;
+ private final int ImhPreferNumbers = 0x8;
+ private final int ImhPreferUppercase = 0x10;
+ private final int ImhPreferLowercase = 0x20;
+ private final int ImhNoPredictiveText = 0x40;
+
+ private final int ImhDate = 0x80;
+ private final int ImhTime = 0x100;
+
+ private final int ImhPreferLatin = 0x200;
+
+ private final int ImhMultiLine = 0x400;
+
+ private final int ImhDigitsOnly = 0x10000;
+ private final int ImhFormattedNumbersOnly = 0x20000;
+ private final int ImhUppercaseOnly = 0x40000;
+ private final int ImhLowercaseOnly = 0x80000;
+ private final int ImhDialableCharactersOnly = 0x100000;
+ private final int ImhEmailCharactersOnly = 0x200000;
+ private final int ImhUrlCharactersOnly = 0x400000;
+ private final int ImhLatinOnly = 0x800000;
+
+ private final QtInputConnectionListener m_qtInputConnectionListener;
+
+ public QtEditText(Context context, QtInputConnectionListener listener)
+ {
+ super(context);
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ m_qtInputConnectionListener = listener;
+ }
+
+ private void setImeOptions(int imeOptions)
+ {
+ if (m_imeOptions == imeOptions)
+ return;
+ m_imeOptions = m_imeOptions;
+ m_optionsChanged = true;
+ }
+
+ private void setInitialCapsMode(int initialCapsMode)
+ {
+ if (m_initialCapsMode == initialCapsMode)
+ return;
+ m_initialCapsMode = initialCapsMode;
+ m_optionsChanged = true;
+ }
+
+
+ private void setInputType(int inputType)
+ {
+ if (m_inputType == inputType)
+ return;
+ m_inputType = m_inputType;
+ m_optionsChanged = true;
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs)
+ {
+ outAttrs.inputType = m_inputType;
+ outAttrs.imeOptions = m_imeOptions;
+ outAttrs.initialCapsMode = m_initialCapsMode;
+ m_inputConnection = new QtInputConnection(this,m_qtInputConnectionListener);
+ return m_inputConnection;
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor ()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown (int keyCode, KeyEvent event)
+ {
+ if (null != m_inputConnection)
+ m_inputConnection.restartImmInput();
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // DEBUG CODE
+ // canvas.drawARGB(127, 255, 0, 255);
+ super.onDraw(canvas);
+ }
+
+
+ public void setEditTextOptions(int enterKeyType, int inputHints)
+ {
+ int initialCapsMode = 0;
+ int imeOptions = imeOptionsFromEnterKeyType(enterKeyType);
+ int inputType = android.text.InputType.TYPE_CLASS_TEXT;
+
+ if ((inputHints & (ImhPreferNumbers | ImhDigitsOnly | ImhFormattedNumbersOnly)) != 0) {
+ inputType = android.text.InputType.TYPE_CLASS_NUMBER;
+ if ((inputHints & ImhFormattedNumbersOnly) != 0) {
+ inputType |= (android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL
+ | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
+ }
+
+ if ((inputHints & ImhHiddenText) != 0)
+ inputType |= android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+ } else if ((inputHints & ImhDialableCharactersOnly) != 0) {
+ inputType = android.text.InputType.TYPE_CLASS_PHONE;
+ } else if ((inputHints & (ImhDate | ImhTime)) != 0) {
+ inputType = android.text.InputType.TYPE_CLASS_DATETIME;
+ if ((inputHints & (ImhDate | ImhTime)) != (ImhDate | ImhTime)) {
+ if ((inputHints & ImhDate) != 0)
+ inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_DATE;
+ else
+ inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_TIME;
+ } // else { TYPE_DATETIME_VARIATION_NORMAL(0) }
+ } else { // CLASS_TEXT
+ if ((inputHints & ImhHiddenText) != 0) {
+ inputType |= android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
+ } else if ((inputHints & ImhSensitiveData) != 0 ||
+ isDisablePredictiveTextWorkaround(inputHints)) {
+ inputType |= android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
+ } else if ((inputHints & ImhUrlCharactersOnly) != 0) {
+ inputType |= android.text.InputType.TYPE_TEXT_VARIATION_URI;
+ if (enterKeyType == 0) // not explicitly overridden
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
+ } else if ((inputHints & ImhEmailCharactersOnly) != 0) {
+ inputType |= android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+ }
+
+ if ((inputHints & ImhMultiLine) != 0) {
+ inputType |= android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+ // Clear imeOptions for Multi-Line Type
+ // User should be able to insert new line in such case
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
+ }
+ if ((inputHints & (ImhNoPredictiveText | ImhSensitiveData | ImhHiddenText)) != 0)
+ inputType |= android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+
+ if ((inputHints & ImhUppercaseOnly) != 0) {
+ initialCapsMode |= android.text.TextUtils.CAP_MODE_CHARACTERS;
+ inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
+ } else if ((inputHints & ImhLowercaseOnly) == 0
+ && (inputHints & ImhNoAutoUppercase) == 0) {
+ initialCapsMode |= android.text.TextUtils.CAP_MODE_SENTENCES;
+ inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+ }
+ }
+
+ if (enterKeyType == 0 && (inputHints & ImhMultiLine) != 0)
+ imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+
+ setInitialCapsMode(initialCapsMode);
+ setImeOptions(imeOptions);
+ setInputType(inputType);
+ }
+
+ private int imeOptionsFromEnterKeyType(int enterKeyType)
+ {
+ int imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
+
+ // enter key type - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
+ switch (enterKeyType) {
+ case 0: // EnterKeyDefault
+ break;
+ case 1: // EnterKeyReturn
+ imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+ break;
+ case 2: // EnterKeyDone
+ break;
+ case 3: // EnterKeyGo
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
+ break;
+ case 4: // EnterKeySend
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEND;
+ break;
+ case 5: // EnterKeySearch
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH;
+ break;
+ case 6: // EnterKeyNext
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
+ break;
+ case 7: // EnterKeyPrevious
+ imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS;
+ break;
+ }
+ return imeOptions;
+ }
+
+ private boolean isDisablePredictiveTextWorkaround(int inputHints)
+ {
+ return (inputHints & ImhNoPredictiveText) != 0 &&
+ System.getenv("QT_ANDROID_ENABLE_WORKAROUND_TO_DISABLE_PREDICTIVE_TEXT") != null;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
new file mode 100644
index 0000000000..1c0fd0f7d8
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
@@ -0,0 +1,174 @@
+// Copyright (C) 2024 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
+
+package org.qtproject.qt.android;
+
+import static org.qtproject.qt.android.QtNative.ApplicationState.*;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppStateDetailsListener {
+ // TODO simplistic implementation with one QtView, expand to support multiple views QTBUG-117649
+ private QtView m_view;
+ private long m_rootWindowRef = 0L;
+ private QtNative.ApplicationStateDetails m_stateDetails;
+ private boolean m_windowLoaded = false;
+
+ private static native void createRootWindow(View rootView, int x, int y, int width, int height);
+ static native void deleteWindow(long windowReference);
+
+ public QtEmbeddedDelegate(Activity context) {
+ super(context);
+
+ m_stateDetails = QtNative.getStateDetails();
+ QtNative.registerAppStateListener(this);
+
+ m_activity.getApplication().registerActivityLifecycleCallbacks(
+ new Application.ActivityLifecycleCallbacks() {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+
+ @Override
+ public void onActivityStarted(Activity activity) {}
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ QtNative.setApplicationState(ApplicationActive);
+ QtNative.updateWindow();
+ }
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
+ !activity.isInMultiWindowMode()) {
+ QtNative.setApplicationState(ApplicationInactive);
+ }
+ }
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ QtNative.setApplicationState(ApplicationSuspended);
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ m_activity.getApplication().unregisterActivityLifecycleCallbacks(this);
+ QtNative.unregisterAppStateListener(QtEmbeddedDelegate.this);
+ QtEmbeddedDelegateFactory.remove(m_activity);
+ QtNative.terminateQt();
+ QtNative.setActivity(null);
+ QtNative.getQtThread().exit();
+ onDestroy();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) {
+ synchronized (this) {
+ m_stateDetails = details;
+ if (m_stateDetails.nativePluginIntegrationReady) {
+ QtNative.runAction(() -> {
+ DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
+ QtDisplayManager.setApplicationDisplayMetrics(m_activity,
+ metrics.widthPixels,
+ metrics.heightPixels);
+
+ });
+ createRootWindow();
+ }
+ }
+ }
+
+ @Override
+ void startNativeApplicationImpl(String appParams, String mainLib)
+ {
+ QtNative.startApplication(appParams, mainLib);
+ }
+
+ @Override
+ QtAccessibilityDelegate createAccessibilityDelegate()
+ {
+ // FIXME make QtAccessibilityDelegate window based or verify current way works
+ // also for child windows: QTBUG-120685
+ return null;
+ }
+
+ @UsedFromNativeCode
+ @Override
+ QtLayout getQtLayout()
+ {
+ // TODO verify if returning m_view here works, this is used by the androidjniinput
+ // when e.g. showing a keyboard, so depends on getting the keyboard focus working
+ // QTBUG-118873
+ if (m_view == null)
+ return null;
+ return m_view.getQtWindow();
+ }
+
+ public void queueLoadWindow()
+ {
+ synchronized (this) {
+ if (m_stateDetails.nativePluginIntegrationReady)
+ createRootWindow();
+ }
+ }
+
+ void setView(QtView view) {
+ m_view = view;
+ updateInputDelegate();
+ if (m_view != null)
+ registerGlobalFocusChangeListener(m_view);
+ }
+
+ private void updateInputDelegate() {
+ if (m_view == null) {
+ m_inputDelegate.setEditPopupMenu(null);
+ return;
+ }
+ m_inputDelegate.setEditPopupMenu(new EditPopupMenu(m_activity, m_view));
+ }
+
+
+ public void setRootWindowRef(long ref) {
+ m_rootWindowRef = ref;
+ }
+
+ public void onDestroy() {
+ if (m_rootWindowRef != 0L)
+ deleteWindow(m_rootWindowRef);
+ m_rootWindowRef = 0L;
+ }
+
+ private void createRootWindow() {
+ if (m_view != null && !m_windowLoaded) {
+ createRootWindow(m_view, m_view.getLeft(), m_view.getTop(), m_view.getWidth(), m_view.getHeight());
+ m_windowLoaded = true;
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java
new file mode 100644
index 0000000000..8cf89e5bc3
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import java.util.HashMap;
+
+class QtEmbeddedDelegateFactory {
+ private static final HashMap<Activity, QtEmbeddedDelegate> m_delegates = new HashMap<>();
+ private static final Object m_delegateLock = new Object();
+
+ @UsedFromNativeCode
+ public static QtActivityDelegateBase getActivityDelegate(Activity activity) {
+ synchronized (m_delegateLock) {
+ return m_delegates.get(activity);
+ }
+ }
+
+ public static QtEmbeddedDelegate create(Activity activity) {
+ synchronized (m_delegateLock) {
+ if (!m_delegates.containsKey(activity))
+ m_delegates.put(activity, new QtEmbeddedDelegate(activity));
+
+ return m_delegates.get(activity);
+ }
+ }
+
+ public static void remove(Activity activity) {
+ synchronized (m_delegateLock) {
+ m_delegates.remove(activity);
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
new file mode 100644
index 0000000000..65cfcbeef1
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2024 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.SurfaceView;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import dalvik.system.DexClassLoader;
+import android.content.res.Resources;
+
+class QtEmbeddedLoader extends QtLoader {
+ private static final String TAG = "QtEmbeddedLoader";
+
+ public QtEmbeddedLoader(Context context) {
+ super(new ContextWrapper(context));
+ // TODO Service context handling QTBUG-118874
+ int displayDensity = m_context.getResources().getDisplayMetrics().densityDpi;
+ setEnvironmentVariable("QT_ANDROID_THEME_DISPLAY_DPI", String.valueOf(displayDensity));
+ String stylePath = ExtractStyle.setup(m_context, "minimal", displayDensity);
+ setEnvironmentVariable("ANDROID_STYLE_PATH", stylePath);
+ }
+
+ @Override
+ protected void finish() {
+ // Called when loading fails - clear the delegate to make sure we don't hold reference
+ // to the embedding Context
+ QtEmbeddedDelegateFactory.remove((Activity)m_context.getBaseContext());
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java
index 7c621a11e2..1bfe05e7ac 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java
@@ -1,51 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.view.WindowMetrics;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputMethodManager;
+import android.view.KeyEvent;
import android.graphics.Rect;
import android.app.Activity;
import android.util.DisplayMetrics;
@@ -82,26 +50,11 @@ class QtNativeInputConnection
static native boolean copyURL();
static native boolean paste();
static native boolean updateCursorPosition();
+ static native void reportFullscreenMode(boolean enabled);
+ static native boolean fullscreenMode();
}
-class HideKeyboardRunnable implements Runnable {
- private long m_hideTimeStamp = System.nanoTime();
-
- @Override
- public void run() {
- // Check that the keyboard is really no longer there.
- Activity activity = QtNative.activity();
- Rect r = new Rect();
- activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
- DisplayMetrics metrics = new DisplayMetrics();
- activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- final int kbHeight = metrics.heightPixels - r.bottom;
- if (kbHeight < 100)
- QtNative.activityDelegate().setKeyboardVisibility(false, m_hideTimeStamp);
- }
-}
-
-public class QtInputConnection extends BaseInputConnection
+class QtInputConnection extends BaseInputConnection
{
private static final int ID_SELECT_ALL = android.R.id.selectAll;
private static final int ID_CUT = android.R.id.cut;
@@ -111,21 +64,71 @@ public class QtInputConnection extends BaseInputConnection
private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
- private QtEditText m_view = null;
+ private static final String QtTAG = "QtInputConnection";
+
+ private final QtInputConnectionListener m_qtInputConnectionListener;
+
+ class HideKeyboardRunnable implements Runnable {
+ @Override
+ public void run() {
+ // Check that the keyboard is really no longer there.
+ Activity activity = QtNative.activity();
+ if (activity == null) {
+ Log.w(QtTAG, "HideKeyboardRunnable: The activity reference is null");
+ return;
+ }
+
+ Rect r = new Rect();
+ activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
+
+ int screenHeight;
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ screenHeight = metrics.heightPixels;
+ } else {
+ final WindowMetrics maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics();
+ screenHeight = maximumWindowMetrics.getBounds().height();
+ }
+ final int kbHeight = screenHeight - r.bottom;
+ if (kbHeight < 100)
+ m_qtInputConnectionListener.onHideKeyboardRunnableDone(false, System.nanoTime());
+ }
+ }
+
+ public interface QtInputConnectionListener {
+ void onSetClosing(boolean closing);
+ void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp);
+ void onSendKeyEventDefaultCase();
+ }
+
+ private final QtEditText m_view;
+ private final InputMethodManager m_imm;
private void setClosing(boolean closing)
{
- if (closing) {
+ if (closing)
m_view.postDelayed(new HideKeyboardRunnable(), 100);
- } else {
- QtNative.activityDelegate().setKeyboardVisibility(true, System.nanoTime());
- }
+ else
+ m_qtInputConnectionListener.onSetClosing(false);
}
- public QtInputConnection(QtEditText targetView)
+ public QtInputConnection(QtEditText targetView, QtInputConnectionListener listener)
{
super(targetView, true);
m_view = targetView;
+ m_imm = (InputMethodManager)m_view.getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+ m_qtInputConnectionListener = listener;
+ }
+
+ public void restartImmInput()
+ {
+ if (QtNativeInputConnection.fullscreenMode()) {
+ if (m_imm != null)
+ m_imm.restartInput(m_view);
+ }
+
}
@Override
@@ -136,6 +139,18 @@ public class QtInputConnection extends BaseInputConnection
}
@Override
+ public boolean reportFullscreenMode (boolean enabled)
+ {
+ QtNativeInputConnection.reportFullscreenMode(enabled);
+ // Always ignored on calling editor.
+ // Always false on Android 8 and later, true with earlier.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ return false;
+
+ return true;
+ }
+
+ @Override
public boolean endBatchEdit()
{
setClosing(false);
@@ -153,6 +168,7 @@ public class QtInputConnection extends BaseInputConnection
public boolean commitText(CharSequence text, int newCursorPosition)
{
setClosing(false);
+ restartImmInput();
return QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
}
@@ -218,23 +234,25 @@ public class QtInputConnection extends BaseInputConnection
{
switch (id) {
case ID_SELECT_ALL:
+ restartImmInput();
return QtNativeInputConnection.selectAll();
case ID_COPY:
+ restartImmInput();
return QtNativeInputConnection.copy();
case ID_COPY_URL:
+ restartImmInput();
return QtNativeInputConnection.copyURL();
case ID_CUT:
+ restartImmInput();
return QtNativeInputConnection.cut();
case ID_PASTE:
+ restartImmInput();
return QtNativeInputConnection.paste();
-
case ID_SWITCH_INPUT_METHOD:
- InputMethodManager imm = (InputMethodManager)m_view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null)
- imm.showInputMethodPicker();
+ if (m_imm != null)
+ m_imm.showInputMethodPicker();
return true;
-
case ID_ADD_TO_DICTIONARY:
// TODO
// String word = m_editable.subSequence(0, m_editable.length()).toString();
@@ -250,6 +268,43 @@ public class QtInputConnection extends BaseInputConnection
}
@Override
+ public boolean sendKeyEvent(KeyEvent event)
+ {
+ // QTBUG-85715
+ // If the sendKeyEvent was invoked, it means that the button not related with composingText was used
+ // In such case composing text (if it exists) should be finished immediately
+ finishComposingText();
+ if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER && m_view != null) {
+ KeyEvent fakeEvent;
+ switch (m_view.m_imeOptions) {
+ case android.view.inputmethod.EditorInfo.IME_ACTION_NEXT:
+ fakeEvent = new KeyEvent(event.getDownTime(),
+ event.getEventTime(),
+ event.getAction(),
+ KeyEvent.KEYCODE_TAB,
+ event.getRepeatCount(),
+ event.getMetaState());
+ return super.sendKeyEvent(fakeEvent);
+ case android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS:
+ fakeEvent = new KeyEvent(event.getDownTime(),
+ event.getEventTime(),
+ event.getAction(),
+ KeyEvent.KEYCODE_TAB,
+ event.getRepeatCount(),
+ KeyEvent.META_SHIFT_ON);
+ return super.sendKeyEvent(fakeEvent);
+ case android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION:
+ restartImmInput();
+ break;
+ default:
+ m_qtInputConnectionListener.onSendKeyEventDefaultCase();
+ break;
+ }
+ }
+ return super.sendKeyEvent(event);
+ }
+
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition)
{
setClosing(false);
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java
new file mode 100644
index 0000000000..cfa273e410
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java
@@ -0,0 +1,654 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.text.method.MetaKeyKeyListener;
+import android.util.DisplayMetrics;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
+
+/** @noinspection FieldCanBeLocal*/
+class QtInputDelegate implements QtInputConnection.QtInputConnectionListener {
+
+ // keyboard methods
+ public static native void keyDown(int key, int unicode, int modifier, boolean autoRepeat);
+ public static native void keyUp(int key, int unicode, int modifier, boolean autoRepeat);
+ public static native void keyboardVisibilityChanged(boolean visibility);
+ public static native void keyboardGeometryChanged(int x, int y, int width, int height);
+ // keyboard methods
+
+ // dispatch events methods
+ public static native boolean dispatchGenericMotionEvent(MotionEvent event);
+ public static native boolean dispatchKeyEvent(KeyEvent event);
+ // dispatch events methods
+
+ // handle methods
+ public static native void handleLocationChanged(int id, int x, int y);
+ // handle methods
+
+ private QtEditText m_currentEditText = null;
+ private final InputMethodManager m_imm;
+
+ private boolean m_keyboardIsVisible = false;
+ private boolean m_isKeyboardHidingAnimationOngoing = false;
+ private long m_showHideTimeStamp = System.nanoTime();
+ private int m_portraitKeyboardHeight = 0;
+ private int m_landscapeKeyboardHeight = 0;
+ private int m_probeKeyboardHeightDelayMs = 50;
+ private CursorHandle m_cursorHandle;
+ private CursorHandle m_leftSelectionHandle;
+ private CursorHandle m_rightSelectionHandle;
+ private EditPopupMenu m_editPopupMenu;
+
+ private int m_softInputMode = 0;
+
+ // Values coming from QAndroidInputContext::CursorHandleShowMode
+ private static final int CursorHandleNotShown = 0;
+ private static final int CursorHandleShowNormal = 1;
+ private static final int CursorHandleShowSelection = 2;
+ private static final int CursorHandleShowEdit = 0x100;
+
+ // Handle IDs
+ public static final int IdCursorHandle = 1;
+ public static final int IdLeftHandle = 2;
+ public static final int IdRightHandle = 3;
+
+ private static Boolean m_tabletEventSupported = null;
+
+ private static int m_oldX, m_oldY;
+
+
+ private long m_metaState;
+ private int m_lastChar = 0;
+ private boolean m_backKeyPressedSent = false;
+
+ // Note: because of the circular call to updateFullScreen() from the delegate, we need
+ // a listener to be able to do that call from the delegate, because that's where that
+ // logic lives
+ public interface KeyboardVisibilityListener {
+ void onKeyboardVisibilityChange();
+ }
+
+ private final KeyboardVisibilityListener m_keyboardVisibilityListener;
+
+ QtInputDelegate(Activity activity, KeyboardVisibilityListener listener)
+ {
+ this.m_keyboardVisibilityListener = listener;
+ m_imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+
+ // QtInputConnectionListener methods
+ @Override
+ public void onSetClosing(boolean closing) {
+ if (!closing)
+ setKeyboardVisibility(true, System.nanoTime());
+ }
+
+ @Override
+ public void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp) {
+ setKeyboardVisibility(visibility, hideTimeStamp);
+ }
+
+ @Override
+ public void onSendKeyEventDefaultCase() {
+ hideSoftwareKeyboard();
+ }
+ // QtInputConnectionListener methods
+
+ public boolean isKeyboardVisible()
+ {
+ return m_keyboardIsVisible;
+ }
+
+ // Is the keyboard fully visible i.e. visible and no ongoing animation
+ @UsedFromNativeCode
+ public boolean isSoftwareKeyboardVisible()
+ {
+ return isKeyboardVisible() && !m_isKeyboardHidingAnimationOngoing;
+ }
+
+ void setSoftInputMode(int inputMode)
+ {
+ m_softInputMode = inputMode;
+ }
+
+ QtEditText getCurrentQtEditText()
+ {
+ return m_currentEditText;
+ }
+
+ void setEditPopupMenu(EditPopupMenu editPopupMenu)
+ {
+ m_editPopupMenu = editPopupMenu;
+ }
+
+ private void keyboardVisibilityUpdated(boolean visibility)
+ {
+ m_isKeyboardHidingAnimationOngoing = false;
+ QtInputDelegate.keyboardVisibilityChanged(visibility);
+ }
+
+ public void setKeyboardVisibility(boolean visibility, long timeStamp)
+ {
+ if (m_showHideTimeStamp > timeStamp)
+ return;
+ m_showHideTimeStamp = timeStamp;
+
+ if (m_keyboardIsVisible == visibility)
+ return;
+ m_keyboardIsVisible = visibility;
+ keyboardVisibilityUpdated(m_keyboardIsVisible);
+
+ // Hiding the keyboard clears the immersive mode, so we need to set it again.
+ if (!visibility)
+ m_keyboardVisibilityListener.onKeyboardVisibilityChange();
+
+ }
+
+ @UsedFromNativeCode
+ public void resetSoftwareKeyboard()
+ {
+ if (m_imm == null || m_currentEditText == null)
+ return;
+ m_currentEditText.postDelayed(() -> {
+ m_imm.restartInput(m_currentEditText);
+ m_currentEditText.m_optionsChanged = false;
+ }, 5);
+ }
+
+ void setFocusedView(QtEditText currentEditText)
+ {
+ m_currentEditText = currentEditText;
+ }
+
+ public void showSoftwareKeyboard(Activity activity, QtLayout layout,
+ final int x, final int y, final int width, final int height,
+ final int inputHints, final int enterKeyType)
+ {
+ QtNative.runAction(() -> {
+ if (m_imm == null || m_currentEditText == null)
+ return;
+
+ if (updateSoftInputMode(activity, height))
+ return;
+
+ m_currentEditText.setEditTextOptions(enterKeyType, inputHints);
+
+ m_currentEditText.postDelayed(() -> {
+ m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case InputMethodManager.RESULT_SHOWN:
+ QtNativeInputConnection.updateCursorPosition();
+ //FALLTHROUGH
+ case InputMethodManager.RESULT_UNCHANGED_SHOWN:
+ setKeyboardVisibility(true, System.nanoTime());
+ if (m_softInputMode == 0) {
+ probeForKeyboardHeight(layout, activity,
+ x, y, width, height, inputHints, enterKeyType);
+ }
+ break;
+ case InputMethodManager.RESULT_HIDDEN:
+ case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
+ setKeyboardVisibility(false, System.nanoTime());
+ break;
+ }
+ }
+ });
+ if (m_currentEditText.m_optionsChanged) {
+ m_imm.restartInput(m_currentEditText);
+ m_currentEditText.m_optionsChanged = false;
+ }
+ }, 15);
+ });
+ }
+
+ private boolean updateSoftInputMode(Activity activity, int height)
+ {
+ DisplayMetrics metrics = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+ // If the screen is in portrait mode than we estimate that keyboard height
+ // will not be higher than 2/5 of the screen. Otherwise we estimate that keyboard height
+ // will not be higher than 2/3 of the screen
+ final int visibleHeight;
+ if (metrics.widthPixels < metrics.heightPixels) {
+ visibleHeight = m_portraitKeyboardHeight != 0 ?
+ m_portraitKeyboardHeight : metrics.heightPixels * 3 / 5;
+ } else {
+ visibleHeight = m_landscapeKeyboardHeight != 0 ?
+ m_landscapeKeyboardHeight : metrics.heightPixels / 3;
+ }
+
+ if (m_softInputMode != 0) {
+ activity.getWindow().setSoftInputMode(m_softInputMode);
+ int stateHidden = WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
+ return (m_softInputMode & stateHidden) != 0;
+ } else {
+ int stateUnchanged = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+ if (height > visibleHeight) {
+ int adjustResize = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ activity.getWindow().setSoftInputMode(stateUnchanged | adjustResize);
+ } else {
+ int adjustPan = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+ activity.getWindow().setSoftInputMode(stateUnchanged | adjustPan);
+ }
+ }
+ return false;
+ }
+
+ private void probeForKeyboardHeight(QtLayout layout, Activity activity, int x, int y,
+ int width, int height, int inputHints, int enterKeyType)
+ {
+ layout.postDelayed(() -> {
+ if (!m_keyboardIsVisible)
+ return;
+ DisplayMetrics metrics = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ Rect r = new Rect();
+ activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
+ if (metrics.heightPixels != r.bottom) {
+ if (metrics.widthPixels > metrics.heightPixels) { // landscape
+ if (m_landscapeKeyboardHeight != r.bottom) {
+ m_landscapeKeyboardHeight = r.bottom;
+ showSoftwareKeyboard(activity, layout, x, y, width, height,
+ inputHints, enterKeyType);
+ }
+ } else {
+ if (m_portraitKeyboardHeight != r.bottom) {
+ m_portraitKeyboardHeight = r.bottom;
+ showSoftwareKeyboard(activity, layout, x, y, width, height,
+ inputHints, enterKeyType);
+ }
+ }
+ } else {
+ // no luck ?
+ // maybe the delay was too short, so let's make it longer
+ if (m_probeKeyboardHeightDelayMs < 1000)
+ m_probeKeyboardHeightDelayMs *= 2;
+ }
+ }, m_probeKeyboardHeightDelayMs);
+ }
+
+ public void hideSoftwareKeyboard()
+ {
+ m_isKeyboardHidingAnimationOngoing = true;
+ QtNative.runAction(() -> {
+ if (m_imm == null || m_currentEditText == null)
+ return;
+
+ m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0,
+ new ResultReceiver(new Handler()) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case InputMethodManager.RESULT_SHOWN:
+ case InputMethodManager.RESULT_UNCHANGED_SHOWN:
+ setKeyboardVisibility(true, System.nanoTime());
+ break;
+ case InputMethodManager.RESULT_HIDDEN:
+ case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
+ setKeyboardVisibility(false, System.nanoTime());
+ break;
+ }
+ }
+ });
+ });
+ }
+
+ @UsedFromNativeCode
+ public void updateSelection(final int selStart, final int selEnd,
+ final int candidatesStart, final int candidatesEnd)
+ {
+ QtNative.runAction(() -> {
+ if (m_imm == null)
+ return;
+
+ m_imm.updateSelection(m_currentEditText, selStart, selEnd, candidatesStart, candidatesEnd);
+ });
+ }
+
+ @UsedFromNativeCode
+ public int getSelectHandleWidth()
+ {
+ int width = 0;
+ if (m_leftSelectionHandle != null && m_rightSelectionHandle != null) {
+ width = Math.max(m_leftSelectionHandle.width(), m_rightSelectionHandle.width());
+ } else if (m_cursorHandle != null) {
+ width = m_cursorHandle.width();
+ }
+ return width;
+ }
+
+ /* called from the C++ code when the position of the cursor or selection handles needs to
+ be adjusted.
+ mode is one of QAndroidInputContext::CursorHandleShowMode
+ */
+ @UsedFromNativeCode
+ public void updateHandles(Activity activity, QtLayout layout, int mode,
+ int editX, int editY, int editButtons,
+ int x1, int y1, int x2, int y2, boolean rtl)
+ {
+ QtNative.runAction(() -> updateHandleImpl(activity, layout, mode, editX, editY, editButtons,
+ x1, y1, x2, y2, rtl));
+ }
+
+ private void updateHandleImpl(Activity activity, QtLayout layout, int mode,
+ int editX, int editY, int editButtons,
+ int x1, int y1, int x2, int y2, boolean rtl)
+ {
+ switch (mode & 0xff)
+ {
+ case CursorHandleNotShown:
+ if (m_cursorHandle != null) {
+ m_cursorHandle.hide();
+ m_cursorHandle = null;
+ }
+ if (m_rightSelectionHandle != null) {
+ m_rightSelectionHandle.hide();
+ m_leftSelectionHandle.hide();
+ m_rightSelectionHandle = null;
+ m_leftSelectionHandle = null;
+ }
+ if (m_editPopupMenu != null)
+ m_editPopupMenu.hide();
+ break;
+
+ case CursorHandleShowNormal:
+ if (m_cursorHandle == null) {
+ m_cursorHandle = new CursorHandle(activity, layout, IdCursorHandle,
+ android.R.attr.textSelectHandle, false);
+ }
+ m_cursorHandle.setPosition(x1, y1);
+ if (m_rightSelectionHandle != null) {
+ m_rightSelectionHandle.hide();
+ m_leftSelectionHandle.hide();
+ m_rightSelectionHandle = null;
+ m_leftSelectionHandle = null;
+ }
+ break;
+
+ case CursorHandleShowSelection:
+ if (m_rightSelectionHandle == null) {
+ m_leftSelectionHandle = new CursorHandle(activity, layout, IdLeftHandle,
+ !rtl ? android.R.attr.textSelectHandleLeft :
+ android.R.attr.textSelectHandleRight,
+ rtl);
+ m_rightSelectionHandle = new CursorHandle(activity, layout, IdRightHandle,
+ !rtl ? android.R.attr.textSelectHandleRight :
+ android.R.attr.textSelectHandleLeft,
+ rtl);
+ }
+ m_leftSelectionHandle.setPosition(x1,y1);
+ m_rightSelectionHandle.setPosition(x2,y2);
+ if (m_cursorHandle != null) {
+ m_cursorHandle.hide();
+ m_cursorHandle = null;
+ }
+ mode |= CursorHandleShowEdit;
+ break;
+ }
+
+ if (!QtClipboardManager.hasClipboardText(activity))
+ editButtons &= ~EditContextView.PASTE_BUTTON;
+
+ if (m_editPopupMenu != null) {
+ if ((mode & CursorHandleShowEdit) == CursorHandleShowEdit && editButtons != 0) {
+ m_editPopupMenu.setPosition(editX, editY, editButtons,
+ m_cursorHandle, m_leftSelectionHandle, m_rightSelectionHandle);
+ } else {
+ m_editPopupMenu.hide();
+ }
+ }
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event);
+ int metaState = MetaKeyKeyListener.getMetaState(m_metaState) | event.getMetaState();
+ int c = event.getUnicodeChar(metaState);
+ int lc = c;
+ m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState);
+
+ if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
+ c = KeyEvent.getDeadChar(m_lastChar, c);
+ }
+
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_MUTE)
+ && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
+ return false;
+ }
+
+ m_lastChar = lc;
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ m_backKeyPressedSent = !isKeyboardVisible();
+ if (!m_backKeyPressedSent)
+ return true;
+ }
+
+ QtInputDelegate.keyDown(keyCode, c, event.getMetaState(), event.getRepeatCount() > 0);
+
+ return true;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_MUTE)
+ && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
+ return false;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) {
+ hideSoftwareKeyboard();
+ setKeyboardVisibility(false, System.nanoTime());
+ return true;
+ }
+
+ m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event);
+ boolean autoRepeat = event.getRepeatCount() > 0;
+ QtInputDelegate.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState(), autoRepeat);
+
+ return true;
+ }
+
+ public boolean handleDispatchKeyEvent(KeyEvent event)
+ {
+ if (event.getAction() == KeyEvent.ACTION_MULTIPLE
+ && event.getCharacters() != null
+ && event.getCharacters().length() == 1
+ && event.getKeyCode() == 0) {
+ keyDown(0, event.getCharacters().charAt(0), event.getMetaState(),
+ event.getRepeatCount() > 0);
+ keyUp(0, event.getCharacters().charAt(0), event.getMetaState(),
+ event.getRepeatCount() > 0);
+ }
+
+ return dispatchKeyEvent(event);
+ }
+
+ public boolean handleDispatchGenericMotionEvent(MotionEvent event)
+ {
+ return dispatchGenericMotionEvent(event);
+ }
+
+ //////////////////////////////
+ // Mouse and Touch Input //
+ //////////////////////////////
+
+ // tablet methods
+ public static native boolean isTabletEventSupported();
+ public static native void tabletEvent(int winId, int deviceId, long time, int action,
+ int pointerType, int buttonState, float x, float y,
+ float pressure);
+ // tablet methods
+
+ // pointer methods
+ public static native void mouseDown(int winId, int x, int y, int mouseButtonState);
+ public static native void mouseUp(int winId, int x, int y, int mouseButtonState);
+ public static native void mouseMove(int winId, int x, int y);
+ public static native void mouseWheel(int winId, int x, int y, float hDelta, float vDelta);
+ public static native void touchBegin(int winId);
+ public static native void touchAdd(int winId, int pointerId, int action, boolean primary,
+ int x, int y, float major, float minor, float rotation,
+ float pressure);
+ public static native void touchEnd(int winId, int action);
+ public static native void touchCancel(int winId);
+ public static native void longPress(int winId, int x, int y);
+ // pointer methods
+
+ static private int getAction(int index, MotionEvent event)
+ {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_MOVE) {
+ int hsz = event.getHistorySize();
+ if (hsz > 0) {
+ float x = event.getX(index);
+ float y = event.getY(index);
+ for (int h = 0; h < hsz; ++h) {
+ if ( event.getHistoricalX(index, h) != x ||
+ event.getHistoricalY(index, h) != y )
+ return 1;
+ }
+ return 2;
+ }
+ return 1;
+ }
+ if (action == MotionEvent.ACTION_DOWN
+ || action == MotionEvent.ACTION_POINTER_DOWN && index == event.getActionIndex()) {
+ return 0;
+ } else if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_POINTER_UP && index == event.getActionIndex()) {
+ return 3;
+ }
+ return 2;
+ }
+
+ static public void sendTouchEvent(MotionEvent event, int id)
+ {
+ int pointerType = 0;
+
+ if (m_tabletEventSupported == null)
+ m_tabletEventSupported = isTabletEventSupported();
+
+ switch (event.getToolType(0)) {
+ case MotionEvent.TOOL_TYPE_STYLUS:
+ pointerType = 1; // QTabletEvent::Pen
+ break;
+ case MotionEvent.TOOL_TYPE_ERASER:
+ pointerType = 3; // QTabletEvent::Eraser
+ break;
+ }
+
+ if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ sendMouseEvent(event, id);
+ } else if (m_tabletEventSupported && pointerType != 0) {
+ tabletEvent(id, event.getDeviceId(), event.getEventTime(), event.getActionMasked(),
+ pointerType, event.getButtonState(),
+ event.getX(), event.getY(), event.getPressure());
+ } else {
+ touchBegin(id);
+ for (int i = 0; i < event.getPointerCount(); ++i) {
+ touchAdd(id,
+ event.getPointerId(i),
+ getAction(i, event),
+ i == 0,
+ (int)event.getX(i),
+ (int)event.getY(i),
+ event.getTouchMajor(i),
+ event.getTouchMinor(i),
+ event.getOrientation(i),
+ event.getPressure(i));
+ }
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ touchEnd(id, 0);
+ break;
+
+ case MotionEvent.ACTION_UP:
+ touchEnd(id, 2);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ touchCancel(id);
+ break;
+
+ default:
+ touchEnd(id, 1);
+ }
+ }
+ }
+
+ static public void sendTrackballEvent(MotionEvent event, int id)
+ {
+ sendMouseEvent(event,id);
+ }
+
+ static public boolean sendGenericMotionEvent(MotionEvent event, int id)
+ {
+ int scrollOrHoverMove = MotionEvent.ACTION_SCROLL | MotionEvent.ACTION_HOVER_MOVE;
+ int pointerDeviceModifier = (event.getSource() & InputDevice.SOURCE_CLASS_POINTER);
+ boolean isPointerDevice = pointerDeviceModifier == InputDevice.SOURCE_CLASS_POINTER;
+
+ if ((event.getAction() & scrollOrHoverMove) == 0 || !isPointerDevice )
+ return false;
+
+ return sendMouseEvent(event, id);
+ }
+
+ static public boolean sendMouseEvent(MotionEvent event, int id)
+ {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ mouseUp(id, (int) event.getX(), (int) event.getY(), event.getButtonState());
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ mouseDown(id, (int) event.getX(), (int) event.getY(), event.getButtonState());
+ m_oldX = (int) event.getX();
+ m_oldY = (int) event.getY();
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_MOVE:
+ if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ mouseMove(id, (int) event.getX(), (int) event.getY());
+ } else {
+ int dx = (int) (event.getX() - m_oldX);
+ int dy = (int) (event.getY() - m_oldY);
+ if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
+ mouseMove(id, (int) event.getX(), (int) event.getY());
+ m_oldX = (int) event.getX();
+ m_oldY = (int) event.getY();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_SCROLL:
+ mouseWheel(id, (int) event.getX(), (int) event.getY(),
+ event.getAxisValue(MotionEvent.AXIS_HSCROLL),
+ event.getAxisValue(MotionEvent.AXIS_VSCROLL));
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java
index 63993f81b5..aedc845014 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java
@@ -1,60 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-package org.qtproject.qt5.android;
+package org.qtproject.qt.android;
import android.app.Activity;
import android.content.Context;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowInsets;
-public class QtLayout extends ViewGroup
-{
- private Runnable m_startApplicationRunnable;
- public QtLayout(Context context, Runnable startRunnable)
+class QtLayout extends ViewGroup {
+
+ public QtLayout(Context context)
{
super(context);
- m_startApplicationRunnable = startRunnable;
}
public QtLayout(Context context, AttributeSet attrs)
@@ -68,41 +32,6 @@ public class QtLayout extends ViewGroup
}
@Override
- protected void onSizeChanged (int w, int h, int oldw, int oldh)
- {
- WindowInsets insets = getRootWindowInsets();
-
- DisplayMetrics realMetrics = new DisplayMetrics();
- ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRealMetrics(realMetrics);
-
- boolean isFullScreenView = h == realMetrics.heightPixels;
-
- int insetLeft = isFullScreenView ? insets.getSystemWindowInsetLeft() : 0;
- int insetTop = isFullScreenView ? insets.getSystemWindowInsetTop() : 0;
- int insetRight = isFullScreenView ? insets.getSystemWindowInsetRight() : 0;
- int insetBottom = isFullScreenView ? insets.getSystemWindowInsetBottom() : 0;
-
- int usableAreaWidth = w - insetLeft - insetRight;
- int usableAreaHeight = h - insetTop - insetBottom;
-
- QtNative.setApplicationDisplayMetrics(realMetrics.widthPixels,
- realMetrics.heightPixels,
- insetLeft,
- insetTop,
- usableAreaWidth,
- usableAreaHeight,
- realMetrics.xdpi,
- realMetrics.ydpi,
- realMetrics.scaledDensity,
- realMetrics.density);
-
- if (m_startApplicationRunnable != null) {
- m_startApplicationRunnable.run();
- m_startApplicationRunnable = null;
- }
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int count = getChildCount();
@@ -120,11 +49,15 @@ public class QtLayout extends ViewGroup
int childRight;
int childBottom;
- QtLayout.LayoutParams lp
- = (QtLayout.LayoutParams) child.getLayoutParams();
-
- childRight = lp.x + child.getMeasuredWidth();
- childBottom = lp.y + child.getMeasuredHeight();
+ if (child.getLayoutParams() instanceof QtLayout.LayoutParams) {
+ QtLayout.LayoutParams lp
+ = (QtLayout.LayoutParams) child.getLayoutParams();
+ childRight = lp.x + child.getMeasuredWidth();
+ childBottom = lp.y + child.getMeasuredHeight();
+ } else {
+ childRight = child.getMeasuredWidth();
+ childBottom = child.getMeasuredHeight();
+ }
maxWidth = Math.max(maxWidth, childRight);
maxHeight = Math.max(maxHeight, childBottom);
@@ -158,7 +91,6 @@ public class QtLayout extends ViewGroup
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int count = getChildCount();
-
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
@@ -167,10 +99,11 @@ public class QtLayout extends ViewGroup
int childLeft = lp.x;
int childTop = lp.y;
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
-
+ int childRight = (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) ?
+ r - l : childLeft + child.getMeasuredWidth();
+ int childBottom = (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) ?
+ b - t : childTop + child.getMeasuredHeight();
+ child.layout(childLeft, childTop, childRight, childBottom);
}
}
}
@@ -190,8 +123,7 @@ public class QtLayout extends ViewGroup
/**
* Per-child layout information associated with AbsoluteLayout.
- * See
- * {@link android.R.styleable#AbsoluteLayout_Layout Absolute Layout Attributes}
+ * See {android.R.styleable#AbsoluteLayout_Layout Absolute Layout Attributes}
* for a list of all child view attributes that this class supports.
*/
public static class LayoutParams extends ViewGroup.LayoutParams
@@ -223,6 +155,11 @@ public class QtLayout extends ViewGroup
this.y = y;
}
+ public LayoutParams(int width, int height)
+ {
+ super(width, height);
+ }
+
/**
* {@inheritDoc}
*/
@@ -248,7 +185,7 @@ public class QtLayout extends ViewGroup
/**
* set the layout params on a child view.
- *
+ * <p>
* Note: This function adds the child view if it's not in the
* layout already.
*/
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java
new file mode 100644
index 0000000000..a00c4795f7
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java
@@ -0,0 +1,558 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2019, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ComponentInfo;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Objects;
+
+import dalvik.system.DexClassLoader;
+
+abstract class QtLoader {
+
+ protected static final String QtTAG = "QtLoader";
+
+ private final Resources m_resources;
+ private final String m_packageName;
+ private String m_preferredAbi = null;
+ private String m_nativeLibrariesDir = null;
+ private ClassLoader m_classLoader;
+
+ protected final ContextWrapper m_context;
+ protected ComponentInfo m_contextInfo;
+
+ protected String m_mainLibPath;
+ protected String m_mainLibName;
+ protected String m_applicationParameters = "";
+ protected HashMap<String, String> m_environmentVariables = new HashMap<>();
+
+ protected int m_debuggerSleepMs = 0;
+
+ /**
+ * Sets and initialize the basic pieces.
+ * Initializes the class loader since it doesn't rely on anything
+ * other than the context.
+ * Also, we can already initialize the static classes contexts here.
+ **/
+ public QtLoader(ContextWrapper context) {
+ m_context = context;
+ m_resources = m_context.getResources();
+ m_packageName = m_context.getPackageName();
+
+ initClassLoader();
+ initStaticClasses();
+ initContextInfo();
+ }
+
+ /**
+ * Implements the logic for finish the extended context, mostly called
+ * in error cases.
+ **/
+ abstract protected void finish();
+
+ /**
+ * Initializes the context info instance which is used to retrieve
+ * the app metadata from the AndroidManifest.xml or other xml resources.
+ * Some values are dependent on the context being an Activity or Service.
+ **/
+ protected void initContextInfo() {
+ try {
+ Context context = m_context.getBaseContext();
+ if (context instanceof Activity) {
+ m_contextInfo = context.getPackageManager().getActivityInfo(
+ ((Activity)context).getComponentName(), PackageManager.GET_META_DATA);
+ } else if (context instanceof Service) {
+ m_contextInfo = context.getPackageManager().getServiceInfo(
+ new ComponentName(context, context.getClass()),
+ PackageManager.GET_META_DATA);
+ } else {
+ Log.w(QtTAG, "Context is not an instance of Activity or Service, could not get " +
+ "context info for it");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ finish();
+ }
+ }
+
+ /**
+ * Extract the common metadata in the base implementation. And the extended methods
+ * call context specific metadata extraction. This also sets the various environment
+ * variables and application parameters.
+ **/
+ protected void extractContextMetaData() {
+ setEnvironmentVariable("QT_ANDROID_FONTS", "Roboto;Droid Sans;Droid Sans Fallback");
+ String monospaceFonts = "Droid Sans Mono;Droid Sans;Droid Sans Fallback";
+ setEnvironmentVariable("QT_ANDROID_FONTS_MONOSPACE", monospaceFonts);
+ setEnvironmentVariable("QT_ANDROID_FONTS_SERIF", "Droid Serif");
+ setEnvironmentVariable("HOME", m_context.getFilesDir().getAbsolutePath());
+ setEnvironmentVariable("TMPDIR", m_context.getCacheDir().getAbsolutePath());
+ String backgroundRunning = getMetaData("android.app.background_running");
+ setEnvironmentVariable("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED", backgroundRunning);
+ setEnvironmentVariable("QTRACE_LOCATION", getMetaData("android.app.trace_location"));
+ appendApplicationParameters(getMetaData("android.app.arguments"));
+ }
+
+ private ArrayList<String> preferredAbiLibs(String[] libs) {
+ HashMap<String, ArrayList<String>> abiLibs = new HashMap<>();
+ for (String lib : libs) {
+ String[] archLib = lib.split(";", 2);
+ if (m_preferredAbi != null && !archLib[0].equals(m_preferredAbi))
+ continue;
+ if (!abiLibs.containsKey(archLib[0]))
+ abiLibs.put(archLib[0], new ArrayList<>());
+ Objects.requireNonNull(abiLibs.get(archLib[0])).add(archLib[1]);
+ }
+
+ if (m_preferredAbi != null) {
+ if (abiLibs.containsKey(m_preferredAbi)) {
+ return abiLibs.get(m_preferredAbi);
+ }
+ return new ArrayList<>();
+ }
+
+ for (String abi : Build.SUPPORTED_ABIS) {
+ if (abiLibs.containsKey(abi)) {
+ m_preferredAbi = abi;
+ return abiLibs.get(abi);
+ }
+ }
+ return new ArrayList<>();
+ }
+
+ private void initStaticClasses() {
+ Context context = m_context.getBaseContext();
+ boolean isActivity = context instanceof Activity;
+ for (String className : getStaticInitClasses()) {
+ try {
+ Class<?> initClass = m_classLoader.loadClass(className);
+ Object staticInitDataObject = initClass.newInstance(); // create an instance
+
+ if (isActivity) {
+ try {
+ Method m = initClass.getMethod("setActivity", Activity.class, Object.class);
+ m.invoke(staticInitDataObject, (Activity) context, this);
+ } catch (InvocationTargetException | NoSuchMethodException e) {
+ Log.d(QtTAG, "Class " + initClass.getName() + " does not implement " +
+ "setActivity method");
+ }
+ } else {
+ try {
+ Method m = initClass.getMethod("setService", Service.class, Object.class);
+ m.invoke(staticInitDataObject, (Service) context, this);
+ } catch (InvocationTargetException | NoSuchMethodException e) {
+ Log.d(QtTAG, "Class " + initClass.getName() + " does not implement " +
+ "setService method");
+ }
+ }
+
+ try {
+ // For modules that don't need/have setActivity/setService
+ Method m = initClass.getMethod("setContext", Context.class);
+ m.invoke(staticInitDataObject, context);
+ } catch (InvocationTargetException | NoSuchMethodException e) {
+ Log.d(QtTAG, "Class " + initClass.getName() + " does not implement " +
+ "setContext method");
+ }
+ } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) {
+ Log.d(QtTAG, "Could not instantiate class " + className + ", " + e);
+ }
+ }
+ }
+
+ /**
+ * Initialize the class loader instance and sets it via QtNative.
+ * This would also be used by QJniObject API.
+ **/
+ private void initClassLoader()
+ {
+ // directory where optimized DEX files should be written.
+ String outDexPath = m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath();
+ String sourceDir = m_context.getApplicationInfo().sourceDir;
+ m_classLoader = new DexClassLoader(sourceDir, outDexPath, null, m_context.getClassLoader());
+ QtNative.setClassLoader(m_classLoader);
+ }
+
+ /**
+ * Returns the context's main library absolute path,
+ * or null if the library hasn't been loaded yet.
+ **/
+ public String getMainLibraryPath() {
+ return m_mainLibPath;
+ }
+
+ /**
+ * Set the name of the main app library to libName, which is the name of the library,
+ * not including the path, target architecture or .so suffix. This matches the target name
+ * of the app target in CMakeLists.txt.
+ * This method can be used when the name is not provided by androiddeployqt, for example when
+ * embedding QML views to a native Android app.
+ **/
+ public void setMainLibraryName(String libName) {
+ m_mainLibName = libName;
+ }
+
+ /**
+ * Returns the context's parameters that are used when calling
+ * the main library's main() function. This is assembled from
+ * a combination of static values and also metadata dependent values.
+ **/
+ public String getApplicationParameters() {
+ return m_applicationParameters;
+ }
+
+ /**
+ * Adds a list of parameters to the internal array list of parameters.
+ * Either a whitespace or a tab is accepted as a separator between parameters.
+ **/
+ public void appendApplicationParameters(String params)
+ {
+ if (params == null || params.isEmpty())
+ return;
+
+ if (!m_applicationParameters.isEmpty())
+ m_applicationParameters += " ";
+ m_applicationParameters += params;
+ }
+
+ /**
+ * Sets a single key/value environment variable pair.
+ **/
+ public void setEnvironmentVariable(String key, String value)
+ {
+ try {
+ android.system.Os.setenv(key, value, true);
+ m_environmentVariables.put(key, value);
+ } catch (Exception e) {
+ Log.e(QtTAG, "Could not set environment variable:" + key + "=" + value);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Sets a list of keys/values string to as environment variables.
+ * This expects the key/value to be separated by '=', and parameters
+ * to be separated by tabs or space.
+ **/
+ public void setEnvironmentVariables(String environmentVariables)
+ {
+ if (environmentVariables == null || environmentVariables.isEmpty())
+ return;
+
+ environmentVariables = environmentVariables.replaceAll("\t", " ");
+
+ for (String variable : environmentVariables.split(" ")) {
+ String[] keyValue = variable.split("=", 2);
+ if (keyValue.length < 2 || keyValue[0].isEmpty())
+ continue;
+
+ setEnvironmentVariable(keyValue[0], keyValue[1]);
+ }
+ }
+
+ /**
+ * Parses the native libraries dir. If the libraries are part of the APK,
+ * the path is set to the APK extracted libs path.
+ * Otherwise, it looks for the system level dir, that's either set in the Manifest,
+ * the deployment libs.xml.
+ * If none of the above are valid, it falls back to predefined system path.
+ **/
+ private void parseNativeLibrariesDir() {
+ if (isBundleQtLibs()) {
+ String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/";
+ File nativeLibraryDir = new File(nativeLibraryPrefix);
+ if (nativeLibraryDir.exists()) {
+ String[] list = nativeLibraryDir.list();
+ if (nativeLibraryDir.isDirectory() && list != null && list.length > 0) {
+ m_nativeLibrariesDir = nativeLibraryPrefix;
+ }
+ }
+ } else {
+ // First check if user has provided system libs prefix in AndroidManifest
+ String systemLibsPrefix = getApplicationMetaData("android.app.system_libs_prefix");
+
+ // If not, check if it's provided by androiddeployqt in libs.xml
+ if (systemLibsPrefix.isEmpty())
+ systemLibsPrefix = getSystemLibsPrefix();
+
+ if (systemLibsPrefix.isEmpty()) {
+ final String SYSTEM_LIB_PATH = "/system/lib/";
+ systemLibsPrefix = SYSTEM_LIB_PATH;
+ Log.e(QtTAG, "Using " + SYSTEM_LIB_PATH + " as default libraries path. "
+ + "It looks like the app is deployed using Unbundled "
+ + "deployment. It may be necessary to specify the path to "
+ + "the directory where Qt libraries are installed using either "
+ + "android.app.system_libs_prefix metadata variable in your "
+ + "AndroidManifest.xml or QT_ANDROID_SYSTEM_LIBS_PATH in your "
+ + "CMakeLists.txt");
+ }
+
+ File systemLibraryDir = new File(systemLibsPrefix);
+ String[] list = systemLibraryDir.list();
+ if (systemLibraryDir.exists()) {
+ if (systemLibraryDir.isDirectory() && list != null && list.length > 0)
+ m_nativeLibrariesDir = systemLibsPrefix;
+ else
+ Log.e(QtTAG, "System library directory " + systemLibsPrefix + " is empty.");
+ } else {
+ Log.e(QtTAG, "System library directory " + systemLibsPrefix + " does not exist.");
+ }
+ }
+
+ if (m_nativeLibrariesDir != null && !m_nativeLibrariesDir.endsWith("/"))
+ m_nativeLibrariesDir += "/";
+ }
+
+ /**
+ * Returns the application level metadata.
+ *
+ * @noinspection SameParameterValue*/
+ private String getApplicationMetaData(String key) {
+ if (m_contextInfo == null)
+ return "";
+
+ ApplicationInfo applicationInfo = m_contextInfo.applicationInfo;
+ if (applicationInfo == null)
+ return "";
+
+ Bundle metadata = applicationInfo.metaData;
+ if (metadata == null || !metadata.containsKey(key))
+ return "";
+
+ return metadata.getString(key);
+ }
+
+ /**
+ * Returns the context level metadata.
+ **/
+ protected String getMetaData(String key) {
+ if (m_contextInfo == null)
+ return "";
+
+ Bundle metadata = m_contextInfo.metaData;
+ if (metadata == null || !metadata.containsKey(key))
+ return "";
+
+ return String.valueOf(metadata.get(key));
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getQtLibrariesList() {
+ int id = m_resources.getIdentifier("qt_libs", "array", m_packageName);
+ return preferredAbiLibs(m_resources.getStringArray(id));
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private boolean useLocalQtLibs() {
+ int id = m_resources.getIdentifier("use_local_qt_libs", "string", m_packageName);
+ return Integer.parseInt(m_resources.getString(id)) == 1;
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private boolean isBundleQtLibs() {
+ int id = m_resources.getIdentifier("bundle_local_qt_libs", "string", m_packageName);
+ return Integer.parseInt(m_resources.getString(id)) == 1;
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private String getSystemLibsPrefix() {
+ int id = m_resources.getIdentifier("system_libs_prefix", "string", m_packageName);
+ return m_resources.getString(id);
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getLocalLibrariesList() {
+ int id = m_resources.getIdentifier("load_local_libs", "array", m_packageName);
+ ArrayList<String> localLibs = new ArrayList<>();
+ for (String arrayItem : preferredAbiLibs(m_resources.getStringArray(id))) {
+ Collections.addAll(localLibs, arrayItem.split(":"));
+ }
+ return localLibs;
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getStaticInitClasses() {
+ int id = m_resources.getIdentifier("static_init_classes", "string", m_packageName);
+ String[] classes = m_resources.getString(id).split(":");
+ ArrayList<String> finalClasses = new ArrayList<>();
+ for (String element : classes) {
+ if (!element.isEmpty()) {
+ finalClasses.add(element);
+ }
+ }
+ return finalClasses;
+ }
+
+ @SuppressLint("DiscouragedApi")
+ private String[] getBundledLibs() {
+ int id = m_resources.getIdentifier("bundled_libs", "array", m_packageName);
+ return m_resources.getStringArray(id);
+ }
+
+ /**
+ * Loads all Qt native bundled libraries and main library.
+ **/
+ public void loadQtLibraries() {
+ if (!useLocalQtLibs()) {
+ Log.w(QtTAG, "Use local Qt libs is false");
+ finish();
+ return;
+ }
+
+ if (m_nativeLibrariesDir == null)
+ parseNativeLibrariesDir();
+
+ if (m_nativeLibrariesDir == null || m_nativeLibrariesDir.isEmpty()) {
+ Log.e(QtTAG, "The native libraries directory is null or empty");
+ finish();
+ return;
+ }
+
+ setEnvironmentVariable("QT_PLUGIN_PATH", m_nativeLibrariesDir);
+ setEnvironmentVariable("QML_PLUGIN_PATH", m_nativeLibrariesDir);
+
+ // Load native Qt APK libraries
+ ArrayList<String> nativeLibraries = getQtLibrariesList();
+ nativeLibraries.addAll(getLocalLibrariesList());
+
+ if (m_debuggerSleepMs > 0) {
+ Log.i(QtTAG, "Sleeping for " + m_debuggerSleepMs +
+ "ms, helping the native debugger to settle. " +
+ "Use the env QT_ANDROID_DEBUGGER_MAIN_THREAD_SLEEP_MS variable to change this value.");
+ QtNative.getQtThread().sleep(m_debuggerSleepMs);
+ }
+
+ if (!loadLibraries(nativeLibraries)) {
+ Log.e(QtTAG, "Loading Qt native libraries failed");
+ finish();
+ return;
+ }
+
+ // add all bundled Qt libs to loader params
+ ArrayList<String> bundledLibraries = new ArrayList<>(preferredAbiLibs(getBundledLibs()));
+ if (!loadLibraries(bundledLibraries)) {
+ Log.e(QtTAG, "Loading Qt bundled libraries failed");
+ finish();
+ return;
+ }
+
+ if (m_mainLibName == null)
+ m_mainLibName = getMetaData("android.app.lib_name");
+ // Load main lib
+ if (!loadMainLibrary(m_mainLibName + "_" + m_preferredAbi)) {
+ Log.e(QtTAG, "Loading main library failed");
+ finish();
+ }
+ }
+
+ // Loading libraries using System.load() uses full lib paths
+ @SuppressLint("UnsafeDynamicallyLoadedCode")
+ private String loadLibraryHelper(String library)
+ {
+ String loadedLib = null;
+ try {
+ File libFile = new File(library);
+ if (libFile.exists()) {
+ System.load(library);
+ loadedLib = library;
+ } else {
+ Log.e(QtTAG, "Can't find '" + library + "'");
+ }
+ } catch (Exception e) {
+ Log.e(QtTAG, "Can't load '" + library + "'", e);
+ }
+
+ return loadedLib;
+ }
+
+ /**
+ * Returns an array with absolute library paths from a list of file names only.
+ **/
+ private ArrayList<String> getLibrariesFullPaths(final ArrayList<String> libraries)
+ {
+ if (libraries == null)
+ return null;
+
+ ArrayList<String> absolutePathLibraries = new ArrayList<>();
+ for (String libName : libraries) {
+ // Add lib and .so to the lib name only if it doesn't already end with .so,
+ // this means some names don't necessarily need to have the lib prefix
+ if (!libName.endsWith(".so")) {
+ libName = libName + ".so";
+ libName = "lib" + libName;
+ }
+
+ File file = new File(m_nativeLibrariesDir + libName);
+ absolutePathLibraries.add(file.getAbsolutePath());
+ }
+
+ return absolutePathLibraries;
+ }
+
+ /**
+ * Loads the main library.
+ * Returns true if loading was successful, and sets the absolute
+ * path to the main library. Otherwise, returns false and the path
+ * to the main library is null.
+ **/
+ private boolean loadMainLibrary(String mainLibName)
+ {
+ ArrayList<String> oneEntryArray = new ArrayList<>(Collections.singletonList(mainLibName));
+ String mainLibPath = getLibrariesFullPaths(oneEntryArray).get(0);
+ final boolean[] success = {true};
+ QtNative.getQtThread().run(() -> {
+ m_mainLibPath = loadLibraryHelper(mainLibPath);
+ if (m_mainLibPath == null)
+ success[0] = false;
+ });
+
+ return success[0];
+ }
+
+ /**
+ * Loads a list of libraries.
+ * Returns true if all libraries were loaded successfully,
+ * and false if any library failed. Stops loading at the first failure.
+ **/
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean loadLibraries(final ArrayList<String> libraries)
+ {
+ if (libraries == null)
+ return false;
+
+ ArrayList<String> fullPathLibs = getLibrariesFullPaths(libraries);
+
+ final boolean[] success = {true};
+ QtNative.getQtThread().run(() -> {
+ for (int i = 0; i < fullPathLibs.size(); ++i) {
+ String libName = fullPathLibs.get(i);
+ if (loadLibraryHelper(libName) == null) {
+ success[0] = false;
+ break;
+ }
+ }
+ });
+
+ return success[0];
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtMessageDialogHelper.java b/src/android/jar/src/org/qtproject/qt/android/QtMessageDialogHelper.java
new file mode 100644
index 0000000000..e13abbbadd
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtMessageDialogHelper.java
@@ -0,0 +1,336 @@
+// Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.content.ClipboardManager;
+import android.text.Html;
+import android.text.Spanned;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+class QtNativeDialogHelper
+{
+ static native void dialogResult(long handler, int buttonID);
+}
+
+class ButtonStruct implements View.OnClickListener
+{
+ ButtonStruct(QtMessageDialogHelper dialog, int id, String text)
+ {
+ m_dialog = dialog;
+ m_id = id;
+ m_text = Html.fromHtml(text);
+ }
+ QtMessageDialogHelper m_dialog;
+ private final int m_id;
+ Spanned m_text;
+
+ @Override
+ public void onClick(View view) {
+ QtNativeDialogHelper.dialogResult(m_dialog.handler(), m_id);
+ }
+}
+
+class QtMessageDialogHelper
+{
+
+ public QtMessageDialogHelper(Activity activity)
+ {
+ m_activity = activity;
+ }
+
+ @UsedFromNativeCode
+ public void setStandardIcon(int icon)
+ {
+ m_standardIcon = icon;
+
+ }
+
+ private Drawable getIconDrawable()
+ {
+ if (m_standardIcon == 0)
+ return null;
+
+ // Information, Warning, Critical, Question
+ switch (m_standardIcon)
+ {
+ case 1: // Information
+ return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_info,
+ m_activity.getTheme());
+ case 2: // Warning
+ return m_activity.getResources().getDrawable(android.R.drawable.stat_sys_warning,
+ m_activity.getTheme());
+ case 3: // Critical
+ return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_alert,
+ m_activity.getTheme());
+ case 4: // Question
+ return m_activity.getResources().getDrawable(android.R.drawable.ic_menu_help,
+ m_activity.getTheme());
+ }
+ return null;
+ }
+
+ @UsedFromNativeCode
+ public void setTile(String title)
+ {
+ m_title = Html.fromHtml(title);
+ }
+
+ @UsedFromNativeCode
+ public void setText(String text)
+ {
+ m_text = Html.fromHtml(text);
+ }
+
+ @UsedFromNativeCode
+ public void setInformativeText(String informativeText)
+ {
+ m_informativeText = Html.fromHtml(informativeText);
+ }
+
+ @UsedFromNativeCode
+ public void setDetailedText(String text)
+ {
+ m_detailedText = Html.fromHtml(text);
+ }
+
+ @UsedFromNativeCode
+ public void addButton(int id, String text)
+ {
+ if (m_buttonsList == null)
+ m_buttonsList = new ArrayList<>();
+ m_buttonsList.add(new ButtonStruct(this, id, text));
+ }
+
+ private Drawable getStyledDrawable(int id)
+ {
+ int[] attrs = { id };
+ final TypedArray a = m_theme.obtainStyledAttributes(attrs);
+ Drawable d = a.getDrawable(0);
+ a.recycle();
+ return d;
+ }
+
+ @UsedFromNativeCode
+ public void show(long handler)
+ {
+ m_handler = handler;
+ m_activity.runOnUiThread(() -> {
+ if (m_dialog != null && m_dialog.isShowing())
+ m_dialog.dismiss();
+
+ m_dialog = new AlertDialog.Builder(m_activity).create();
+ Window window = m_dialog.getWindow();
+ if (window != null)
+ m_theme = window.getContext().getTheme();
+ else
+ Log.w(QtTAG, "show(): cannot set theme from null window!");
+
+ if (m_title != null)
+ m_dialog.setTitle(m_title);
+ m_dialog.setOnCancelListener(dialogInterface -> QtNativeDialogHelper.dialogResult(handler(), -1));
+ m_dialog.setCancelable(m_buttonsList == null);
+ m_dialog.setCanceledOnTouchOutside(m_buttonsList == null);
+ m_dialog.setIcon(getIconDrawable());
+ ScrollView scrollView = new ScrollView(m_activity);
+ RelativeLayout dialogLayout = new RelativeLayout(m_activity);
+ int id = 1;
+ View lastView = null;
+ View.OnLongClickListener copyText = view -> {
+ TextView tv = (TextView)view;
+ if (tv != null) {
+ ClipboardManager cm = (ClipboardManager) m_activity.getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(ClipData.newPlainText(tv.getText(), tv.getText()));
+ }
+ return true;
+ };
+ if (m_text != null)
+ {
+ TextView view = new TextView(m_activity);
+ view.setId(id++);
+ view.setOnLongClickListener(copyText);
+ view.setLongClickable(true);
+
+ view.setText(m_text);
+ view.setTextAppearance(android.R.style.TextAppearance_Medium);
+
+ RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ layout.setMargins(16, 8, 16, 8);
+ layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ dialogLayout.addView(view, layout);
+ lastView = view;
+ }
+
+ if (m_informativeText != null)
+ {
+ TextView view= new TextView(m_activity);
+ view.setId(id++);
+ view.setOnLongClickListener(copyText);
+ view.setLongClickable(true);
+
+ view.setText(m_informativeText);
+ view.setTextAppearance(android.R.style.TextAppearance_Medium);
+
+ RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ layout.setMargins(16, 8, 16, 8);
+ if (lastView != null)
+ layout.addRule(RelativeLayout.BELOW, lastView.getId());
+ else
+ layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ dialogLayout.addView(view, layout);
+ lastView = view;
+ }
+
+ if (m_detailedText != null)
+ {
+ TextView view= new TextView(m_activity);
+ view.setId(id++);
+ view.setOnLongClickListener(copyText);
+ view.setLongClickable(true);
+
+ view.setText(m_detailedText);
+ view.setTextAppearance(android.R.style.TextAppearance_Small);
+
+ RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ layout.setMargins(16, 8, 16, 8);
+ if (lastView != null)
+ layout.addRule(RelativeLayout.BELOW, lastView.getId());
+ else
+ layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ dialogLayout.addView(view, layout);
+ lastView = view;
+ }
+
+ if (m_buttonsList != null)
+ {
+ LinearLayout buttonsLayout = new LinearLayout(m_activity);
+ buttonsLayout.setOrientation(LinearLayout.HORIZONTAL);
+ buttonsLayout.setId(id++);
+ boolean firstButton = true;
+ for (ButtonStruct button: m_buttonsList)
+ {
+ Button bv;
+ try {
+ bv = new Button(m_activity, null, android.R.attr.borderlessButtonStyle);
+ } catch (Exception e) {
+ bv = new Button(m_activity);
+ e.printStackTrace();
+ }
+
+ bv.setText(button.m_text);
+ bv.setOnClickListener(button);
+ if (!firstButton) // first button
+ {
+ View spacer = new View(m_activity);
+ try {
+ LinearLayout.LayoutParams layout = new LinearLayout.LayoutParams(1,
+ RelativeLayout.LayoutParams.MATCH_PARENT);
+ spacer.setBackground(getStyledDrawable(android.R.attr.dividerVertical));
+ buttonsLayout.addView(spacer, layout);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ LinearLayout.LayoutParams layout = new LinearLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT, 1.0f);
+ buttonsLayout.addView(bv, layout);
+ firstButton = false;
+ }
+
+ try {
+ View horizontalDivider = new View(m_activity);
+ horizontalDivider.setId(id);
+ horizontalDivider.setBackground(getStyledDrawable(
+ android.R.attr.dividerHorizontal));
+ RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT, 1);
+ relativeParams.setMargins(0, 10, 0, 0);
+ if (lastView != null) {
+ relativeParams.addRule(RelativeLayout.BELOW, lastView.getId());
+ }
+ else
+ relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ dialogLayout.addView(horizontalDivider, relativeParams);
+ lastView = horizontalDivider;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ if (lastView != null) {
+ relativeParams.addRule(RelativeLayout.BELOW, lastView.getId());
+ }
+ else
+ relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ relativeParams.setMargins(2, 0, 2, 0);
+ dialogLayout.addView(buttonsLayout, relativeParams);
+ }
+ scrollView.addView(dialogLayout);
+ m_dialog.setView(scrollView);
+ m_dialog.show();
+ });
+ }
+
+ @UsedFromNativeCode
+ public void hide()
+ {
+ m_activity.runOnUiThread(() -> {
+ if (m_dialog != null && m_dialog.isShowing())
+ m_dialog.dismiss();
+ reset();
+ });
+ }
+
+ public long handler()
+ {
+ return m_handler;
+ }
+
+ public void reset()
+ {
+ m_standardIcon = 0;
+ m_title = null;
+ m_text = null;
+ m_informativeText = null;
+ m_detailedText = null;
+ m_buttonsList = null;
+ m_dialog = null;
+ m_handler = 0;
+ }
+
+ private static final String QtTAG = "QtMessageDialogHelper";
+ private final Activity m_activity;
+ private int m_standardIcon = 0;
+ private Spanned m_title, m_text, m_informativeText, m_detailedText;
+ private ArrayList<ButtonStruct> m_buttonsList;
+ private AlertDialog m_dialog;
+ private long m_handler = 0;
+ private Resources.Theme m_theme;
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
new file mode 100644
index 0000000000..97a45ef8fa
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
@@ -0,0 +1,456 @@
+// Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriPermission;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+class QtNative
+{
+ private static WeakReference<Activity> m_activity = null;
+ private static WeakReference<Service> m_service = null;
+ public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
+
+ private static final ApplicationStateDetails m_stateDetails = new ApplicationStateDetails();
+
+ public static final String QtTAG = "Qt JAVA";
+
+ // a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
+ private static final ArrayList<Runnable> m_lostActions = new ArrayList<>();
+
+ private static final QtThread m_qtThread = new QtThread();
+ private static ClassLoader m_classLoader = null;
+
+ private static final Runnable runPendingCppRunnablesRunnable = QtNative::runPendingCppRunnables;
+ private static final ArrayList<AppStateDetailsListener> m_appStateListeners = new ArrayList<>();
+ private static final Object m_appStateListenersLock = new Object();
+
+ @UsedFromNativeCode
+ public static ClassLoader classLoader()
+ {
+ return m_classLoader;
+ }
+
+ public static void setClassLoader(ClassLoader classLoader)
+ {
+ m_classLoader = classLoader;
+ }
+
+ public static void setActivity(Activity qtMainActivity)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_activity = new WeakReference<>(qtMainActivity);
+ }
+ }
+
+ public static void setService(Service qtMainService)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_service = new WeakReference<>(qtMainService);
+ }
+ }
+
+ @UsedFromNativeCode
+ public static Activity activity()
+ {
+ synchronized (m_mainActivityMutex) {
+ return m_activity != null ? m_activity.get() : null;
+ }
+ }
+
+ public static boolean isActivityValid()
+ {
+ return m_activity != null && m_activity.get() != null;
+ }
+
+ @UsedFromNativeCode
+ public static Service service()
+ {
+ synchronized (m_mainActivityMutex) {
+ return m_service != null ? m_service.get() : null;
+ }
+ }
+
+ public static boolean isServiceValid()
+ {
+ return m_service != null && m_service.get() != null;
+ }
+
+ @UsedFromNativeCode
+ public static Context getContext() {
+ if (isActivityValid())
+ return m_activity.get();
+ return service();
+ }
+
+ @UsedFromNativeCode
+ public static String[] getStringArray(String joinedString)
+ {
+ return joinedString.split(",");
+ }
+
+ private static String getCurrentMethodNameLog()
+ {
+ return new Exception().getStackTrace()[1].getMethodName() + ": ";
+ }
+
+ /** @noinspection SameParameterValue*/
+ private static Uri getUriWithValidPermission(Context context, String uri, String openMode)
+ {
+ Uri parsedUri;
+ try {
+ parsedUri = Uri.parse(uri);
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ try {
+ String scheme = parsedUri.getScheme();
+
+ // We only want to check permissions for content Uris
+ if (scheme != null && scheme.compareTo("content") != 0)
+ return parsedUri;
+
+ List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions();
+ String uriStr = parsedUri.getPath();
+
+ for (int i = 0; i < permissions.size(); ++i) {
+ Uri iterUri = permissions.get(i).getUri();
+ boolean isRequestPermission = permissions.get(i).isReadPermission();
+
+ if (!openMode.equals("r"))
+ isRequestPermission = permissions.get(i).isWritePermission();
+
+ if (Objects.equals(iterUri.getPath(), uriStr) && isRequestPermission)
+ return iterUri;
+ }
+
+ // if we only have transient permissions on uri all the above will fail,
+ // but we will be able to read the file anyway, so continue with uri here anyway
+ // and check for SecurityExceptions later
+ return parsedUri;
+ } catch (SecurityException e) {
+ Log.e(QtTAG, getCurrentMethodNameLog() + e);
+ return parsedUri;
+ }
+ }
+
+ @UsedFromNativeCode
+ public static boolean openURL(Context context, String url, String mime)
+ {
+ final Uri uri = getUriWithValidPermission(context, url, "r");
+ if (uri == null) {
+ Log.e(QtTAG, getCurrentMethodNameLog() + "received invalid/null Uri");
+ return false;
+ }
+
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (!mime.isEmpty())
+ intent.setDataAndType(uri, mime);
+
+ Activity activity = activity();
+ if (activity == null) {
+ Log.w(QtTAG, "openURL(): The activity reference is null");
+ return false;
+ }
+
+ activity.startActivity(intent);
+
+ return true;
+ } catch (Exception e) {
+ Log.e(QtTAG, getCurrentMethodNameLog() + e);
+ return false;
+ }
+ }
+
+ static QtThread getQtThread() {
+ return m_qtThread;
+ }
+
+ interface AppStateDetailsListener {
+ void onAppStateDetailsChanged(ApplicationStateDetails details);
+ }
+
+ // Keep in sync with src/corelib/global/qnamespace.h
+ public static class ApplicationState {
+ static final int ApplicationSuspended = 0x0;
+ static final int ApplicationHidden = 0x1;
+ static final int ApplicationInactive = 0x2;
+ static final int ApplicationActive = 0x4;
+ }
+
+ public static class ApplicationStateDetails {
+ int state = ApplicationState.ApplicationSuspended;
+ boolean nativePluginIntegrationReady = false;
+ boolean isStarted = false;
+ }
+
+ public static ApplicationStateDetails getStateDetails()
+ {
+ return m_stateDetails;
+ }
+
+ public static void setStarted(boolean started)
+ {
+ m_stateDetails.isStarted = started;
+ notifyAppStateDetailsChanged(m_stateDetails);
+ }
+
+ @UsedFromNativeCode
+ public static void notifyNativePluginIntegrationReady(boolean ready)
+ {
+ m_stateDetails.nativePluginIntegrationReady = ready;
+ notifyAppStateDetailsChanged(m_stateDetails);
+ }
+
+ public static void setApplicationState(int state)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_stateDetails.state = state;
+ if (state == ApplicationState.ApplicationActive) {
+ for (Runnable mLostAction : m_lostActions)
+ runAction(mLostAction);
+ m_lostActions.clear();
+ }
+ }
+ updateApplicationState(state);
+ notifyAppStateDetailsChanged(m_stateDetails);
+ }
+
+ static void registerAppStateListener(AppStateDetailsListener listener) {
+ synchronized (m_appStateListenersLock) {
+ if (!m_appStateListeners.contains(listener))
+ m_appStateListeners.add(listener);
+ }
+ }
+
+ static void unregisterAppStateListener(AppStateDetailsListener listener) {
+ synchronized (m_appStateListenersLock) {
+ m_appStateListeners.remove(listener);
+ }
+ }
+
+ static void notifyAppStateDetailsChanged(ApplicationStateDetails details) {
+ synchronized (m_appStateListenersLock) {
+ for (AppStateDetailsListener listener : m_appStateListeners)
+ listener.onAppStateDetailsChanged(details);
+ }
+ }
+
+ // Post a runnable to Main (UI) Thread if the app is active,
+ // otherwise, queue it to be posted when the the app is active again
+ public static void runAction(Runnable action)
+ {
+ runAction(action, true);
+ }
+
+ public static void runAction(Runnable action, boolean queueWhenInactive)
+ {
+ synchronized (m_mainActivityMutex) {
+ final Looper mainLooper = Looper.getMainLooper();
+ final Handler handler = new Handler(mainLooper);
+
+ if (queueWhenInactive) {
+ final boolean isStateVisible =
+ (m_stateDetails.state != ApplicationState.ApplicationSuspended)
+ && (m_stateDetails.state != ApplicationState.ApplicationHidden);
+ final boolean active = (isActivityValid() && isStateVisible) || isServiceValid();
+ if (!active || !handler.post(action))
+ m_lostActions.add(action);
+ } else {
+ handler.post(action);
+ }
+ }
+ }
+
+ @UsedFromNativeCode
+ private static void runPendingCppRunnablesOnAndroidThread()
+ {
+ synchronized (m_mainActivityMutex) {
+ if (isActivityValid()) {
+ if (m_stateDetails.state == ApplicationState.ApplicationActive)
+ m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable);
+ else
+ runAction(runPendingCppRunnablesRunnable);
+ } else {
+ final Looper mainLooper = Looper.getMainLooper();
+ final Thread looperThread = mainLooper.getThread();
+ if (looperThread.equals(Thread.currentThread())) {
+ runPendingCppRunnablesRunnable.run();
+ } else {
+ final Handler handler = new Handler(mainLooper);
+ handler.post(runPendingCppRunnablesRunnable);
+ }
+ }
+ }
+ }
+
+ @UsedFromNativeCode
+ private static void setViewVisibility(final View view, final boolean visible)
+ {
+ runAction(() -> view.setVisibility(visible ? View.VISIBLE : View.GONE));
+ }
+
+ public static void startApplication(String params, String mainLib)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_qtThread.run(() -> {
+ final String qtParams = mainLib + " " + params;
+ if (!startQtAndroidPlugin(qtParams))
+ Log.e(QtTAG, "An error occurred while starting the Qt Android plugin");
+ });
+ m_qtThread.post(QtNative::startQtApplication);
+ waitForServiceSetup();
+ m_stateDetails.isStarted = true;
+ notifyAppStateDetailsChanged(m_stateDetails);
+ }
+ }
+
+ public static void quitApp()
+ {
+ runAction(() -> {
+ quitQtAndroidPlugin();
+ if (isActivityValid())
+ m_activity.get().finish();
+ if (isServiceValid())
+ m_service.get().stopSelf();
+ m_stateDetails.isStarted = false;
+ // Likely no use to call notifyAppStateDetailsChanged at this point since we are exiting
+ });
+ }
+
+ @UsedFromNativeCode
+ public static int checkSelfPermission(String permission)
+ {
+ synchronized (m_mainActivityMutex) {
+ Context context = getContext();
+ PackageManager pm = context.getPackageManager();
+ return pm.checkPermission(permission, context.getPackageName());
+ }
+ }
+
+ @UsedFromNativeCode
+ private static byte[][] getSSLCertificates()
+ {
+ ArrayList<byte[]> certificateList = new ArrayList<>();
+
+ try {
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init((KeyStore) null);
+
+ for (TrustManager manager : factory.getTrustManagers()) {
+ if (manager instanceof X509TrustManager) {
+ X509TrustManager trustManager = (X509TrustManager) manager;
+
+ for (X509Certificate certificate : trustManager.getAcceptedIssuers()) {
+ byte[] buffer = certificate.getEncoded();
+ certificateList.add(buffer);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(QtTAG, "Failed to get certificates", e);
+ }
+
+ byte[][] certificateArray = new byte[certificateList.size()][];
+ certificateArray = certificateList.toArray(certificateArray);
+ return certificateArray;
+ }
+
+ @UsedFromNativeCode
+ private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
+ String [] list;
+ ArrayList<String> res = new ArrayList<>();
+ try {
+ list = asset.list(path);
+ if (list != null) {
+ for (String file : list) {
+ try {
+ String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
+ if (isDir != null && isDir.length > 0)
+ file += "/";
+ res.add(file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return res.toArray(new String[0]);
+ }
+
+ // application methods
+ public static native boolean startQtAndroidPlugin(String params);
+ public static native void startQtApplication();
+ public static native void waitForServiceSetup();
+ public static native void quitQtCoreApplication();
+ public static native void quitQtAndroidPlugin();
+ public static native void terminateQt();
+ public static native boolean updateNativeActivity();
+ // application methods
+
+ // surface methods
+ public static native void setSurface(int id, Object surface);
+ // surface methods
+
+ // window methods
+ public static native void updateWindow();
+ // window methods
+
+ // application methods
+ public static native void updateApplicationState(int state);
+
+ // menu methods
+ public static native boolean onPrepareOptionsMenu(Menu menu);
+ public static native boolean onOptionsItemSelected(int itemId, boolean checked);
+ public static native void onOptionsMenuClosed(Menu menu);
+
+ public static native void onCreateContextMenu(ContextMenu menu);
+ public static native void fillContextMenu(Menu menu);
+ public static native boolean onContextItemSelected(int itemId, boolean checked);
+ public static native void onContextMenuClosed(Menu menu);
+ // menu methods
+
+ // activity methods
+ public static native void onActivityResult(int requestCode, int resultCode, Intent data);
+ public static native void onNewIntent(Intent data);
+
+ public static native void runPendingCppRunnables();
+
+ public static native void sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
+ // activity methods
+
+ // service methods
+ public static native IBinder onBind(Intent intent);
+ // service methods
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java b/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java
new file mode 100644
index 0000000000..dd2cead8cd
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java
@@ -0,0 +1,22 @@
+// 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
+
+package org.qtproject.qt.android;
+
+import android.graphics.Rect;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+class QtNativeAccessibility
+{
+ static native void setActive(boolean enable);
+ static native int[] childIdListForAccessibleObject(int objectId);
+ static native int parentId(int objectId);
+ static native String descriptionForAccessibleObject(int objectId);
+ static native Rect screenRect(int objectId);
+ static native int hitTest(float x, float y);
+ static native boolean clickAction(int objectId);
+ static native boolean scrollForward(int objectId);
+ static native boolean scrollBackward(int objectId);
+
+ static native boolean populateNode(int objectId, AccessibilityNodeInfo node);
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java b/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java
new file mode 100644
index 0000000000..3dae587a71
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.content.res.Configuration;
+import android.view.Surface;
+
+/**
+ A layout which corresponds to one Activity, i.e. is the root layout where the top level window
+ and handles orientation changes.
+*/
+public class QtRootLayout extends QtLayout
+{
+ private int m_activityDisplayRotation = -1;
+ private int m_ownDisplayRotation = -1;
+ private int m_nativeOrientation = -1;
+ private int m_previousRotation = -1;
+
+ public QtRootLayout(Context context)
+ {
+ super(context);
+ }
+
+ public void setActivityDisplayRotation(int rotation)
+ {
+ m_activityDisplayRotation = rotation;
+ }
+
+ public void setNativeOrientation(int orientation)
+ {
+ m_nativeOrientation = orientation;
+ }
+
+ public int displayRotation()
+ {
+ return m_ownDisplayRotation;
+ }
+
+ @Override
+ protected void onSizeChanged (int w, int h, int oldw, int oldh)
+ {
+ Activity activity = (Activity)getContext();
+ if (activity == null)
+ return;
+
+ DisplayMetrics realMetrics = new DisplayMetrics();
+ Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+ ? activity.getWindowManager().getDefaultDisplay()
+ : activity.getDisplay();
+
+ if (display == null)
+ return;
+
+ display.getRealMetrics(realMetrics);
+ if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) {
+ // This is an intermediate state during display rotation.
+ // The new size is still reported for old orientation, while
+ // realMetrics contain sizes for new orientation. Setting
+ // such parameters will produce inconsistent results, so
+ // we just skip them.
+ // We will have another onSizeChanged() with normal values
+ // a bit later.
+ return;
+ }
+ QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
+ QtDisplayManager.handleOrientationChanges(activity);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration)
+ {
+ Context context = getContext();
+ if (context instanceof Activity) {
+ Activity activity = (Activity)context;
+ //if orientation change is betwen invertedPortrait and portrait or
+ //invertedLandscape and landscape, we do not get sizeChanged callback.
+ int rotation = QtDisplayManager.getDisplayRotation(activity);
+ if (isSameSizeForOrientations(rotation, m_previousRotation))
+ QtDisplayManager.handleOrientationChanges(activity);
+ m_previousRotation = rotation;
+ }
+ }
+
+ public boolean isSameSizeForOrientations(int r1, int r2) {
+ return (r1 == r2) ||
+ (r1 == Surface.ROTATION_0 && r2 == Surface.ROTATION_180)
+ || (r1 == Surface.ROTATION_180 && r2 == Surface.ROTATION_0)
+ || (r1 == Surface.ROTATION_90 && r2 == Surface.ROTATION_270)
+ || (r1 == Surface.ROTATION_270 && r2 == Surface.ROTATION_90);
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java
new file mode 100644
index 0000000000..b69dea4416
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+public class QtServiceBase extends Service {
+ @Override
+ public void onCreate()
+ {
+ super.onCreate();
+
+ // the application has already started, do not reload everything again
+ if (QtNative.getStateDetails().isStarted) {
+ Log.w(QtNative.QtTAG,
+ "A QtService tried to start in the same process as an initiated " +
+ "QtActivity. That is not supported. This results in the service " +
+ "functioning as an Android Service detached from Qt.");
+ return;
+ }
+
+ QtNative.setService(this);
+
+ QtServiceLoader loader = new QtServiceLoader(this);
+ loader.loadQtLibraries();
+ QtNative.startApplication(loader.getApplicationParameters(), loader.getMainLibraryPath());
+ QtNative.setApplicationState(QtNative.ApplicationState.ApplicationHidden);
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ QtNative.quitQtCoreApplication();
+ QtNative.terminateQt();
+ QtNative.setService(null);
+ QtNative.getQtThread().exit();
+ System.exit(0);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ synchronized (this) {
+ return QtNative.onBind(intent);
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java
new file mode 100644
index 0000000000..ce74ec21e7
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.app.Service;
+import android.content.ContextWrapper;
+import android.util.Log;
+
+class QtServiceLoader extends QtLoader {
+ private final Service m_service;
+
+ public QtServiceLoader(Service service) {
+ super(new ContextWrapper(service));
+ m_service = service;
+
+ extractContextMetaData();
+ }
+
+ @Override
+ protected void finish() {
+ if (m_service != null)
+ m_service.stopSelf();
+ else
+ Log.w(QtTAG, "finish() called when service object is null");
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt/android/QtSurface.java
new file mode 100644
index 0000000000..3165de4811
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtSurface.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// 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
+
+package org.qtproject.qt.android;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+@SuppressLint("ViewConstructor")
+class QtSurface extends SurfaceView implements SurfaceHolder.Callback
+{
+ private QtSurfaceInterface m_surfaceCallback;
+
+ public QtSurface(Context context, QtSurfaceInterface surfaceCallback, boolean onTop, int imageDepth)
+ {
+ super(context);
+ setFocusable(false);
+ setFocusableInTouchMode(false);
+ setZOrderMediaOverlay(onTop);
+ m_surfaceCallback = surfaceCallback;
+ getHolder().addCallback(this);
+ if (imageDepth == 16)
+ getHolder().setFormat(PixelFormat.RGB_565);
+ else
+ getHolder().setFormat(PixelFormat.RGBA_8888);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder)
+ {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
+ {
+ if (width < 1 || height < 1)
+ return;
+ if (m_surfaceCallback != null)
+ m_surfaceCallback.onSurfaceChanged(holder.getSurface());
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder)
+ {
+ if (m_surfaceCallback != null)
+ m_surfaceCallback.onSurfaceChanged(null);
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java
new file mode 100644
index 0000000000..8df442f730
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java
@@ -0,0 +1,13 @@
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// 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
+
+package org.qtproject.qt.android;
+
+import android.view.Surface;
+
+
+public interface QtSurfaceInterface
+{
+ void onSurfaceChanged(Surface surface);
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java b/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java
new file mode 100644
index 0000000000..828838a9f0
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// 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
+
+package org.qtproject.qt.android;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
+import android.util.Log;
+import android.view.Surface;
+import android.view.TextureView;
+
+public class QtTextureView extends TextureView implements TextureView.SurfaceTextureListener
+{
+ private QtSurfaceInterface m_surfaceCallback;
+ private boolean m_staysOnTop;
+ private Surface m_surface;
+
+ public QtTextureView(Context context, QtSurfaceInterface surfaceCallback, boolean isOpaque)
+ {
+ super(context);
+ setFocusable(false);
+ setFocusableInTouchMode(false);
+ m_surfaceCallback = surfaceCallback;
+ setSurfaceTextureListener(this);
+ setOpaque(isOpaque);
+ setSurfaceTexture(new SurfaceTexture(false));
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
+ m_surface = new Surface(surfaceTexture);
+ m_surfaceCallback.onSurfaceChanged(m_surface);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
+ m_surface = new Surface(surfaceTexture);
+ m_surfaceCallback.onSurfaceChanged(m_surface);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ m_surfaceCallback.onSurfaceChanged(null);
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtThread.java b/src/android/jar/src/org/qtproject/qt/android/QtThread.java
new file mode 100644
index 0000000000..0943ad3265
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtThread.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2018 BogDan Vatra <bogdan@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+class QtThread {
+ private final ArrayList<Runnable> m_pendingRunnables = new ArrayList<>();
+ private boolean m_exit = false;
+ private final Thread m_qtThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (!m_exit) {
+ try {
+ ArrayList<Runnable> pendingRunnables;
+ synchronized (m_qtThread) {
+ if (m_pendingRunnables.size() == 0)
+ m_qtThread.wait();
+ pendingRunnables = new ArrayList<>(m_pendingRunnables);
+ m_pendingRunnables.clear();
+ }
+ for (Runnable runnable : pendingRunnables)
+ runnable.run();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+
+ QtThread() {
+ m_qtThread.setName("qtMainLoopThread");
+ m_qtThread.start();
+ }
+
+ public void post(final Runnable runnable) {
+ synchronized (m_qtThread) {
+ m_pendingRunnables.add(runnable);
+ m_qtThread.notify();
+ }
+ }
+
+ public void sleep(int milliseconds) {
+ try {
+ m_qtThread.sleep(milliseconds);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void run(final Runnable runnable) {
+ final Semaphore sem = new Semaphore(0);
+ synchronized (m_qtThread) {
+ m_pendingRunnables.add(() -> {
+ runnable.run();
+ sem.release();
+ });
+ m_qtThread.notify();
+ }
+ try {
+ sem.acquire();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void exit()
+ {
+ m_exit = true;
+ synchronized (m_qtThread) {
+ m_qtThread.notify();
+ }
+ try {
+ m_qtThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtView.java b/src/android/jar/src/org/qtproject/qt/android/QtView.java
new file mode 100644
index 0000000000..6836171187
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtView.java
@@ -0,0 +1,187 @@
+// Copyright (C) 2024 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
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+
+// Base class for embedding QWindow into native Android view hierarchy. Extend to implement
+// the creation of appropriate window to embed.
+abstract class QtView extends ViewGroup {
+ private final static String TAG = "QtView";
+
+ public interface QtWindowListener {
+ // Called when the QWindow has been created and it's Java counterpart embedded into
+ // QtView
+ void onQtWindowLoaded();
+ }
+
+ protected QtWindow m_window;
+ protected long m_windowReference;
+ protected QtWindowListener m_windowListener;
+ protected QtEmbeddedDelegate m_delegate;
+ // Implement in subclass to handle the creation of the QWindow and its parent container.
+ // TODO could we take care of the parent window creation and parenting outside of the
+ // window creation method to simplify things if user would extend this? Preferably without
+ // too much JNI back and forth. Related to parent window creation, so handle with QTBUG-121511.
+ abstract protected void createWindow(long parentWindowRef);
+
+ private static native void setWindowVisible(long windowReference, boolean visible);
+ private static native void resizeWindow(long windowReference,
+ int x, int y, int width, int height);
+
+ /**
+ * Create QtView for embedding a QWindow. Instantiating a QtView will load the Qt libraries
+ * if they have not already been loaded, including the app library specified by appName, and
+ * starting the said Qt app.
+ * @param context the hosting Context
+ * @param appLibName the name of the Qt app library to load and start. This corresponds to the
+ target name set in Qt app's CMakeLists.txt
+ **/
+ public QtView(Context context, String appLibName) throws InvalidParameterException {
+ super(context);
+ if (appLibName == null || appLibName.isEmpty()) {
+ throw new InvalidParameterException("QtView: argument 'appLibName' may not be empty "+
+ "or null");
+ }
+
+ QtEmbeddedLoader loader = new QtEmbeddedLoader(context);
+ m_delegate = QtEmbeddedDelegateFactory.create((Activity)context);
+ loader.setMainLibraryName(appLibName);
+ addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (m_windowReference != 0L) {
+ final int oldWidth = oldRight - oldLeft;
+ final int oldHeight = oldBottom - oldTop;
+ final int newWidth = right - left;
+ final int newHeight = bottom - top;
+ if (oldWidth != newWidth || oldHeight != newHeight || left != oldLeft ||
+ top != oldTop) {
+ resizeWindow(m_windowReference, left, top, newWidth, newHeight);
+ }
+ }
+ }
+ });
+ loader.loadQtLibraries();
+ // Start Native Qt application
+ m_delegate.startNativeApplication(loader.getApplicationParameters(),
+ loader.getMainLibraryPath());
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ m_delegate.setView(this);
+ m_delegate.queueLoadWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ destroyWindow();
+ m_delegate.setView(null);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (m_window != null)
+ m_window.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ final int count = getChildCount();
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+
+ // Find out how big everyone wants to be
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ // Find rightmost and bottom-most child
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ }
+ }
+
+ // Check against minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
+ resolveSize(maxHeight, heightMeasureSpec));
+ }
+
+
+ public void setQtWindowListener(QtWindowListener listener) {
+ m_windowListener = listener;
+ }
+
+ void setWindowReference(long windowReference) {
+ m_windowReference = windowReference;
+ }
+
+ long windowReference() {
+ return m_windowReference;
+ }
+
+ // Set the visibility of the underlying QWindow. If visible is true, showNormal() is called.
+ // If false, the window is hidden.
+ void setWindowVisible(boolean visible) {
+ if (m_windowReference != 0L)
+ setWindowVisible(m_windowReference, true);
+ }
+
+ // Called from Qt when the QWindow has been created.
+ // window - the Java QtWindow of the created QAndroidPlatformWindow, to embed into the QtView
+ // viewReference - the reference to the created QQuickView
+ void addQtWindow(QtWindow window, long viewReference, long parentWindowRef) {
+ setWindowReference(viewReference);
+ m_delegate.setRootWindowRef(parentWindowRef);
+ final Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ m_window = window;
+ m_window.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(m_window, 0);
+ // Call show window + parent
+ setWindowVisible(true);
+ if (m_windowListener != null)
+ m_windowListener.onQtWindowLoaded();
+ }
+ });
+ }
+
+ // Destroy the underlying QWindow
+ void destroyWindow() {
+ if (m_windowReference != 0L)
+ QtEmbeddedDelegate.deleteWindow(m_windowReference);
+ m_windowReference = 0L;
+ }
+
+ QtWindow getQtWindow() {
+ return m_window;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java
new file mode 100644
index 0000000000..d72e69d32a
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java
@@ -0,0 +1,215 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.util.Log;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+class QtWindow extends QtLayout implements QtSurfaceInterface {
+ private final static String TAG = "QtWindow";
+
+ private View m_surfaceContainer;
+ private View m_nativeView;
+ private HashMap<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>();
+ private QtWindow m_parentWindow;
+ private GestureDetector m_gestureDetector;
+ private final QtEditText m_editText;
+
+ private static native void setSurface(int windowId, Surface surface);
+ static native void windowFocusChanged(boolean hasFocus, int id);
+
+ public QtWindow(Context context, QtWindow parentWindow, QtInputDelegate delegate)
+ {
+ super(context);
+ setId(View.generateViewId());
+ m_editText = new QtEditText(context, delegate);
+ setParent(parentWindow);
+ setFocusableInTouchMode(true);
+ addView(m_editText, new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ QtNative.runAction(() -> {
+ m_gestureDetector =
+ new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+ public void onLongPress(MotionEvent event) {
+ QtInputDelegate.longPress(getId(), (int) event.getX(), (int) event.getY());
+ }
+ });
+ m_gestureDetector.setIsLongpressEnabled(true);
+ });
+ }
+
+ void setVisible(boolean visible) {
+ QtNative.runAction(() -> {
+ if (visible)
+ setVisibility(View.VISIBLE);
+ else
+ setVisibility(View.INVISIBLE);
+ });
+ }
+
+ @Override
+ public void onSurfaceChanged(Surface surface)
+ {
+ setSurface(getId(), surface);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ m_editText.requestFocus();
+ event.setLocation(event.getX() + getX(), event.getY() + getY());
+ QtInputDelegate.sendTouchEvent(event, getId());
+ m_gestureDetector.onTouchEvent(event);
+ return true;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event)
+ {
+ QtInputDelegate.sendTrackballEvent(event, getId());
+ return true;
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event)
+ {
+ return QtInputDelegate.sendGenericMotionEvent(event, getId());
+ }
+
+ public void removeWindow()
+ {
+ if (m_parentWindow != null)
+ m_parentWindow.removeChildWindow(getId());
+ }
+
+ public void createSurface(final boolean onTop,
+ final int x, final int y, final int w, final int h,
+ final int imageDepth, final boolean isOpaque,
+ final int surfaceContainerType) // TODO constant for type
+ {
+ QtNative.runAction(()-> {
+ if (m_surfaceContainer != null)
+ removeView(m_surfaceContainer);
+
+ setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
+ if (surfaceContainerType == 0) {
+ m_surfaceContainer = new QtSurface(getContext(), QtWindow.this,
+ onTop, imageDepth);
+ } else {
+ m_surfaceContainer = new QtTextureView(getContext(), QtWindow.this, isOpaque);
+ }
+ m_surfaceContainer.setLayoutParams(new QtLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ // The surface container of this window will be added as the first of the stack.
+ // All other views are stacked based on the order they are created.
+ addView(m_surfaceContainer, 0);
+ });
+ }
+
+ public void destroySurface()
+ {
+ QtNative.runAction(()-> {
+ if (m_surfaceContainer != null) {
+ removeView(m_surfaceContainer);
+ m_surfaceContainer = null;
+ }
+ }, false);
+ }
+
+ public void setGeometry(final int x, final int y, final int w, final int h)
+ {
+ QtNative.runAction(()-> {
+ if (getContext() instanceof QtActivityBase)
+ setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
+ });
+ }
+
+ public void addChildWindow(QtWindow window)
+ {
+ QtNative.runAction(()-> {
+ m_childWindows.put(window.getId(), window);
+ addView(window, getChildCount());
+ });
+ }
+
+ public void removeChildWindow(int id)
+ {
+ QtNative.runAction(()-> {
+ if (m_childWindows.containsKey(id))
+ removeView(m_childWindows.remove(id));
+ });
+ }
+
+ public void setNativeView(final View view,
+ final int x, final int y, final int w, final int h)
+ {
+ QtNative.runAction(()-> {
+ if (m_nativeView != null)
+ removeView(m_nativeView);
+
+ m_nativeView = view;
+ QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
+ m_nativeView.setLayoutParams(new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(m_nativeView);
+ });
+ }
+
+ public void bringChildToFront(int id)
+ {
+ QtNative.runAction(()-> {
+ View view = m_childWindows.get(id);
+ if (view != null) {
+ if (getChildCount() > 0)
+ moveChild(view, getChildCount() - 1);
+ }
+ });
+ }
+
+ public void bringChildToBack(int id) {
+ QtNative.runAction(()-> {
+ View view = m_childWindows.get(id);
+ if (view != null) {
+ moveChild(view, 0);
+ }
+ });
+ }
+
+ public void removeNativeView()
+ {
+ QtNative.runAction(()-> {
+ if (m_nativeView != null) {
+ removeView(m_nativeView);
+ m_nativeView = null;
+ }
+ });
+ }
+
+ void setParent(QtWindow parentWindow)
+ {
+ if (m_parentWindow == parentWindow)
+ return;
+
+ if (m_parentWindow != null)
+ m_parentWindow.removeChildWindow(getId());
+
+ m_parentWindow = parentWindow;
+ if (m_parentWindow != null)
+ m_parentWindow.addChildWindow(this);
+ }
+
+ QtWindow parent()
+ {
+ return m_parentWindow;
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/UsedFromNativeCode.java b/src/android/jar/src/org/qtproject/qt/android/UsedFromNativeCode.java
new file mode 100644
index 0000000000..e0223c083f
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/UsedFromNativeCode.java
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 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
+
+package org.qtproject.qt.android;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.CLASS)
+public @interface UsedFromNativeCode { }
diff --git a/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidBinder.java b/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidBinder.java
new file mode 100644
index 0000000000..bd837570fe
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidBinder.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android.extras;
+
+import android.os.Binder;
+import android.os.Parcel;
+
+import org.qtproject.qt.android.UsedFromNativeCode;
+
+class QtAndroidBinder extends Binder
+{
+ @UsedFromNativeCode
+ public QtAndroidBinder(long id)
+ {
+ m_id = id;
+ }
+
+ public void setId(long id)
+ {
+ synchronized(this)
+ {
+ m_id = id;
+ }
+ }
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ {
+ synchronized(this)
+ {
+ return QtNative.onTransact(m_id, code, data, reply, flags);
+ }
+ }
+
+ private long m_id;
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java b/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
new file mode 100644
index 0000000000..b70b64e3ac
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android.extras;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import org.qtproject.qt.android.UsedFromNativeCode;
+
+class QtAndroidServiceConnection implements ServiceConnection
+{
+ @UsedFromNativeCode
+ public QtAndroidServiceConnection(long id)
+ {
+ m_id = id;
+ }
+
+ public void setId(long id)
+ {
+ synchronized(this)
+ {
+ m_id = id;
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service)
+ {
+ synchronized(this) {
+ QtNative.onServiceConnected(m_id, name.flattenToString(), service);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name)
+ {
+ synchronized(this) {
+ QtNative.onServiceDisconnected(m_id, name.flattenToString());
+ }
+ }
+
+ private long m_id;
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/extras/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/extras/QtNative.java
new file mode 100644
index 0000000000..f7ba8dd9b4
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/extras/QtNative.java
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android.extras;
+
+import android.os.IBinder;
+import android.os.Parcel;
+
+class QtNative {
+ // Binder
+ public static native boolean onTransact(long id, int code, Parcel data, Parcel reply, int flags);
+
+
+ // ServiceConnection
+ public static native void onServiceConnected(long id, String name, IBinder service);
+ public static native void onServiceDisconnected(long id, String name);
+}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/CursorHandle.java b/src/android/jar/src/org/qtproject/qt5/android/CursorHandle.java
deleted file mode 100644
index 38cc695c37..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/CursorHandle.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.ImageView;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.view.MotionEvent;
-import android.widget.PopupWindow;
-import android.app.Activity;
-import android.util.TypedValue;
-import android.view.ViewTreeObserver;
-
-/* This view represents one of the handle (selection or cursor handle) */
-class CursorView extends ImageView
-{
- private CursorHandle mHandle;
- // The coordinare which where clicked
- private float m_offsetX;
- private float m_offsetY;
- private boolean m_pressed = false;
-
- CursorView (Context context, CursorHandle handle) {
- super(context);
- mHandle = handle;
- }
-
- // Called when the handle was moved programatically , with the delta amount in pixels
- public void adjusted(int dx, int dy) {
- m_offsetX += dx;
- m_offsetY += dy;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- m_offsetX = ev.getRawX();
- m_offsetY = ev.getRawY() + getHeight() / 2;
- m_pressed = true;
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- if (!m_pressed)
- return false;
- mHandle.updatePosition(Math.round(ev.getRawX() - m_offsetX),
- Math.round(ev.getRawY() - m_offsetY));
- break;
- }
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- m_pressed = false;
- break;
- }
- return true;
- }
-
-}
-
-// Helper class that manages a cursor or selection handle
-public class CursorHandle implements ViewTreeObserver.OnPreDrawListener
-{
- private View m_layout = null;
- private CursorView m_cursorView = null;
- private PopupWindow m_popup = null;
- private int m_id;
- private int m_attr;
- private Activity m_activity;
- private int m_posX = 0;
- private int m_posY = 0;
- private int m_lastX;
- private int m_lastY;
- int tolerance;
- private boolean m_rtl;
- int m_yShift;
-
- public CursorHandle(Activity activity, View layout, int id, int attr, boolean rtl) {
- m_activity = activity;
- m_id = id;
- m_attr = attr;
- m_layout = layout;
- DisplayMetrics metrics = new DisplayMetrics();
- activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- m_yShift = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, metrics);
- tolerance = Math.min(1, (int)(m_yShift / 2f));
- m_lastX = m_lastY = -1 - tolerance;
- m_rtl = rtl;
- }
-
- private boolean initOverlay(){
- if (m_popup == null){
-
- Context context = m_layout.getContext();
- int[] attrs = {m_attr};
- TypedArray a = context.getTheme().obtainStyledAttributes(attrs);
- Drawable drawable = a.getDrawable(0);
-
- m_cursorView = new CursorView(context, this);
- m_cursorView.setImageDrawable(drawable);
-
- m_popup = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle);
- m_popup.setSplitTouchEnabled(true);
- m_popup.setClippingEnabled(false);
- m_popup.setContentView(m_cursorView);
- m_popup.setWidth(drawable.getIntrinsicWidth());
- m_popup.setHeight(drawable.getIntrinsicHeight());
-
- m_layout.getViewTreeObserver().addOnPreDrawListener(this);
- }
- return true;
- }
-
- // Show the handle at a given position (or move it if it is already shown)
- public void setPosition(final int x, final int y){
- initOverlay();
-
- final int[] location = new int[2];
- m_layout.getLocationOnScreen(location);
-
- int x2 = x + location[0];
- int y2 = y + location[1] + m_yShift;
-
- if (m_id == QtNative.IdCursorHandle) {
- x2 -= m_popup.getWidth() / 2 ;
- } else if ((m_id == QtNative.IdLeftHandle && !m_rtl) || (m_id == QtNative.IdRightHandle && m_rtl)) {
- x2 -= m_popup.getWidth() * 3 / 4;
- } else {
- x2 -= m_popup.getWidth() / 4;
- }
-
- if (m_popup.isShowing()) {
- m_popup.update(x2, y2, -1, -1);
- m_cursorView.adjusted(x - m_posX, y - m_posY);
- } else {
- m_popup.showAtLocation(m_layout, 0, x2, y2);
- }
-
- m_posX = x;
- m_posY = y;
- }
-
- public int bottom()
- {
- initOverlay();
- final int[] location = new int[2];
- m_cursorView.getLocationOnScreen(location);
- return location[1] + m_cursorView.getHeight();
- }
-
- public void hide() {
- if (m_popup != null) {
- m_popup.dismiss();
- }
- }
-
- // The handle was dragged by a given relative position
- public void updatePosition(int x, int y) {
- y -= m_yShift;
- if (Math.abs(m_lastX - x) > tolerance || Math.abs(m_lastY - y) > tolerance) {
- QtNative.handleLocationChanged(m_id, x + m_posX, y + m_posY);
- m_lastX = x;
- m_lastY = y;
- }
- }
-
- @Override
- public boolean onPreDraw() {
- // This hook is called when the view location is changed
- // For example if the keyboard appears.
- // Adjust the position of the handle accordingly
- if (m_popup != null && m_popup.isShowing())
- setPosition(m_posX, m_posY);
-
- return true;
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java b/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java
deleted file mode 100644
index 104132934d..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 BogDan Vatra <bogdan@kde.org>
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.R;
-
-import java.util.HashMap;
-
-public class EditContextView extends LinearLayout implements View.OnClickListener
-{
- public static final int CUT_BUTTON = 1 << 0;
- public static final int COPY_BUTTON = 1 << 1;
- public static final int PASTE_BUTTON = 1 << 2;
- public static final int SALL_BUTTON = 1 << 3;
-
- HashMap<Integer, ContextButton> m_buttons = new HashMap<Integer, ContextButton>(4);
- OnClickListener m_onClickListener;
-
- public interface OnClickListener
- {
- void contextButtonClicked(int buttonId);
- }
-
- private class ContextButton extends TextView
- {
- public int m_buttonId;
- public ContextButton(Context context, int stringId) {
- super(context);
- m_buttonId = stringId;
- setText(stringId);
- setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, 1));
- setGravity(Gravity.CENTER);
- setTextColor(getResources().getColor(R.color.widget_edittext_dark));
- EditContextView.this.setBackground(getResources().getDrawable(R.drawable.editbox_background_normal));
- float scale = getResources().getDisplayMetrics().density;
- int hPadding = (int)(16 * scale + 0.5f);
- int vPadding = (int)(8 * scale + 0.5f);
- setPadding(hPadding, vPadding, hPadding, vPadding);
- setSingleLine();
- setEllipsize(TextUtils.TruncateAt.END);
- setOnClickListener(EditContextView.this);
- }
- }
-
- @Override
- public void onClick(View v)
- {
- ContextButton button = (ContextButton)v;
- m_onClickListener.contextButtonClicked(button.m_buttonId);
- }
-
- void addButton(int id)
- {
- ContextButton button = new ContextButton(getContext(), id);
- m_buttons.put(id, button);
- addView(button);
- }
-
- public void updateButtons(int buttonsLayout)
- {
- m_buttons.get(R.string.cut).setVisibility((buttonsLayout & CUT_BUTTON) != 0 ? View.VISIBLE : View.GONE);
- m_buttons.get(R.string.copy).setVisibility((buttonsLayout & COPY_BUTTON) != 0 ? View.VISIBLE : View.GONE);
- m_buttons.get(R.string.paste).setVisibility((buttonsLayout & PASTE_BUTTON) != 0 ? View.VISIBLE : View.GONE);
- m_buttons.get(R.string.selectAll).setVisibility((buttonsLayout & SALL_BUTTON) != 0 ? View.VISIBLE : View.GONE);
- }
-
- public EditContextView(Context context, OnClickListener onClickListener) {
- super(context);
- m_onClickListener = onClickListener;
- setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
- addButton(R.string.cut);
- addButton(R.string.copy);
- addButton(R.string.paste);
- addButton(R.string.selectAll);
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java b/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java
deleted file mode 100644
index ae06506c96..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java
+++ /dev/null
@@ -1,2036 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.xmlpull.v1.XmlPullParser;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.NinePatch;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.GradientDrawable.Orientation;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.NinePatchDrawable;
-import android.graphics.drawable.RotateDrawable;
-import android.graphics.drawable.ScaleDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Xml;
-import android.view.inputmethod.EditorInfo;
-
-
-public class ExtractStyle {
-
- native static int[] extractChunkInfo20(byte[] chunkData);
- native static int[] extractNativeChunkInfo20(long nativeChunk);
-
- Class<?> styleableClass = getClass("android.R$styleable");
- Class<?> rippleDrawableClass = getClass("android.graphics.drawable.RippleDrawable");
- Class<?> animatedStateListDrawableClass = getClass("android.graphics.drawable.AnimatedStateListDrawable");
- Class<?> vectorDrawableClass = getClass("android.graphics.drawable.VectorDrawable");
-
- final int[] EMPTY_STATE_SET = {};
- final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled};
- final int[] FOCUSED_STATE_SET = {android.R.attr.state_focused};
- final int[] SELECTED_STATE_SET = {android.R.attr.state_selected};
- final int[] PRESSED_STATE_SET = {android.R.attr.state_pressed};
- final int[] WINDOW_FOCUSED_STATE_SET = {android.R.attr.state_window_focused};
- final int[] ENABLED_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET);
- final int[] ENABLED_SELECTED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET);
- final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] FOCUSED_SELECTED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, SELECTED_STATE_SET);
- final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
- final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, SELECTED_STATE_SET);
- final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, FOCUSED_STATE_SET);
- final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
- final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_ENABLED_STATE_SET = stateSetUnion(PRESSED_STATE_SET, ENABLED_STATE_SET);
- final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_ENABLED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, SELECTED_STATE_SET);
- final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_STATE_SET, FOCUSED_STATE_SET);
- final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
- final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
- final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
-
- final int View_background = getField(styleableClass,"View_background");
- final int View_padding = getField(styleableClass,"View_padding");
- final int View_paddingLeft = getField(styleableClass,"View_paddingLeft");
- final int View_paddingTop = getField(styleableClass,"View_paddingTop");
- final int View_paddingRight = getField(styleableClass,"View_paddingRight");
- final int View_paddingBottom = getField(styleableClass,"View_paddingBottom");
- final int View_scrollX = getField(styleableClass,"View_scrollX");
- final int View_scrollY = getField(styleableClass,"View_scrollY");
- final int View_id = getField(styleableClass,"View_id");
- final int View_tag = getField(styleableClass,"View_tag");
- final int View_fitsSystemWindows = getField(styleableClass,"View_fitsSystemWindows");
- final int View_focusable = getField(styleableClass,"View_focusable");
- final int View_focusableInTouchMode = getField(styleableClass,"View_focusableInTouchMode");
- final int View_clickable = getField(styleableClass,"View_clickable");
- final int View_longClickable = getField(styleableClass,"View_longClickable");
- final int View_saveEnabled = getField(styleableClass,"View_saveEnabled");
- final int View_duplicateParentState = getField(styleableClass,"View_duplicateParentState");
- final int View_visibility = getField(styleableClass,"View_visibility");
- final int View_drawingCacheQuality = getField(styleableClass,"View_drawingCacheQuality");
- final int View_contentDescription = getField(styleableClass,"View_contentDescription");
- final int View_soundEffectsEnabled = getField(styleableClass,"View_soundEffectsEnabled");
- final int View_hapticFeedbackEnabled = getField(styleableClass,"View_hapticFeedbackEnabled");
- final int View_scrollbars = getField(styleableClass,"View_scrollbars");
- final int View_fadingEdge = getField(styleableClass,"View_fadingEdge");
- final int View_scrollbarStyle = getField(styleableClass,"View_scrollbarStyle");
- final int View_scrollbarFadeDuration = getField(styleableClass,"View_scrollbarFadeDuration");
- final int View_scrollbarDefaultDelayBeforeFade = getField(styleableClass,"View_scrollbarDefaultDelayBeforeFade");
- final int View_scrollbarSize = getField(styleableClass,"View_scrollbarSize");
- final int View_scrollbarThumbHorizontal = getField(styleableClass,"View_scrollbarThumbHorizontal");
- final int View_scrollbarThumbVertical = getField(styleableClass,"View_scrollbarThumbVertical");
- final int View_scrollbarTrackHorizontal = getField(styleableClass,"View_scrollbarTrackHorizontal");
- final int View_scrollbarTrackVertical = getField(styleableClass,"View_scrollbarTrackVertical");
- final int View_isScrollContainer = getField(styleableClass,"View_isScrollContainer");
- final int View_keepScreenOn = getField(styleableClass,"View_keepScreenOn");
- final int View_filterTouchesWhenObscured = getField(styleableClass,"View_filterTouchesWhenObscured");
- final int View_nextFocusLeft = getField(styleableClass,"View_nextFocusLeft");
- final int View_nextFocusRight = getField(styleableClass,"View_nextFocusRight");
- final int View_nextFocusUp = getField(styleableClass,"View_nextFocusUp");
- final int View_nextFocusDown = getField(styleableClass,"View_nextFocusDown");
- final int View_minWidth = getField(styleableClass,"View_minWidth");
- final int View_minHeight = getField(styleableClass,"View_minHeight");
- final int View_onClick = getField(styleableClass,"View_onClick");
- final int View_overScrollMode = getField(styleableClass,"View_overScrollMode");
- final int View_paddingStart = getField(styleableClass,"View_paddingStart");
- final int View_paddingEnd = getField(styleableClass,"View_paddingEnd");
-
- final int TextAppearance_textColorHighlight = getField(styleableClass,"TextAppearance_textColorHighlight");
- final int TextAppearance_textColor = getField(styleableClass,"TextAppearance_textColor");
- final int TextAppearance_textColorHint = getField(styleableClass,"TextAppearance_textColorHint");
- final int TextAppearance_textColorLink = getField(styleableClass,"TextAppearance_textColorLink");
- final int TextAppearance_textSize = getField(styleableClass,"TextAppearance_textSize");
- final int TextAppearance_typeface = getField(styleableClass,"TextAppearance_typeface");
- final int TextAppearance_textStyle = getField(styleableClass,"TextAppearance_textStyle");
- final int TextAppearance_textAllCaps = getField(styleableClass,"TextAppearance_textAllCaps");
- final int TextView_editable = getField(styleableClass,"TextView_editable");
- final int TextView_inputMethod = getField(styleableClass,"TextView_inputMethod");
- final int TextView_numeric = getField(styleableClass,"TextView_numeric");
- final int TextView_digits = getField(styleableClass,"TextView_digits");
- final int TextView_phoneNumber = getField(styleableClass,"TextView_phoneNumber");
- final int TextView_autoText = getField(styleableClass,"TextView_autoText");
- final int TextView_capitalize = getField(styleableClass,"TextView_capitalize");
- final int TextView_bufferType = getField(styleableClass,"TextView_bufferType");
- final int TextView_selectAllOnFocus = getField(styleableClass,"TextView_selectAllOnFocus");
- final int TextView_autoLink = getField(styleableClass,"TextView_autoLink");
- final int TextView_linksClickable = getField(styleableClass,"TextView_linksClickable");
- final int TextView_drawableLeft = getField(styleableClass,"TextView_drawableLeft");
- final int TextView_drawableTop = getField(styleableClass,"TextView_drawableTop");
- final int TextView_drawableRight = getField(styleableClass,"TextView_drawableRight");
- final int TextView_drawableBottom = getField(styleableClass,"TextView_drawableBottom");
- final int TextView_drawableStart = getField(styleableClass,"TextView_drawableStart");
- final int TextView_drawableEnd = getField(styleableClass,"TextView_drawableEnd");
- final int TextView_drawablePadding = getField(styleableClass,"TextView_drawablePadding");
- final int TextView_textCursorDrawable = getField(styleableClass,"TextView_textCursorDrawable");
- final int TextView_maxLines = getField(styleableClass,"TextView_maxLines");
- final int TextView_maxHeight = getField(styleableClass,"TextView_maxHeight");
- final int TextView_lines = getField(styleableClass,"TextView_lines");
- final int TextView_height = getField(styleableClass,"TextView_height");
- final int TextView_minLines = getField(styleableClass,"TextView_minLines");
- final int TextView_minHeight = getField(styleableClass,"TextView_minHeight");
- final int TextView_maxEms = getField(styleableClass,"TextView_maxEms");
- final int TextView_maxWidth = getField(styleableClass,"TextView_maxWidth");
- final int TextView_ems = getField(styleableClass,"TextView_ems");
- final int TextView_width = getField(styleableClass,"TextView_width");
- final int TextView_minEms = getField(styleableClass,"TextView_minEms");
- final int TextView_minWidth = getField(styleableClass,"TextView_minWidth");
- final int TextView_gravity = getField(styleableClass,"TextView_gravity");
- final int TextView_hint = getField(styleableClass,"TextView_hint");
- final int TextView_text = getField(styleableClass,"TextView_text");
- final int TextView_scrollHorizontally = getField(styleableClass,"TextView_scrollHorizontally");
- final int TextView_singleLine = getField(styleableClass,"TextView_singleLine");
- final int TextView_ellipsize = getField(styleableClass,"TextView_ellipsize");
- final int TextView_marqueeRepeatLimit = getField(styleableClass,"TextView_marqueeRepeatLimit");
- final int TextView_includeFontPadding = getField(styleableClass,"TextView_includeFontPadding");
- final int TextView_cursorVisible = getField(styleableClass,"TextView_cursorVisible");
- final int TextView_maxLength = getField(styleableClass,"TextView_maxLength");
- final int TextView_textScaleX = getField(styleableClass,"TextView_textScaleX");
- final int TextView_freezesText = getField(styleableClass,"TextView_freezesText");
- final int TextView_shadowColor = getField(styleableClass,"TextView_shadowColor");
- final int TextView_shadowDx = getField(styleableClass,"TextView_shadowDx");
- final int TextView_shadowDy = getField(styleableClass,"TextView_shadowDy");
- final int TextView_shadowRadius = getField(styleableClass,"TextView_shadowRadius");
- final int TextView_enabled = getField(styleableClass,"TextView_enabled");
- final int TextView_textColorHighlight = getField(styleableClass,"TextView_textColorHighlight");
- final int TextView_textColor = getField(styleableClass,"TextView_textColor");
- final int TextView_textColorHint = getField(styleableClass,"TextView_textColorHint");
- final int TextView_textColorLink = getField(styleableClass,"TextView_textColorLink");
- final int TextView_textSize = getField(styleableClass,"TextView_textSize");
- final int TextView_typeface = getField(styleableClass,"TextView_typeface");
- final int TextView_textStyle = getField(styleableClass,"TextView_textStyle");
- final int TextView_password = getField(styleableClass,"TextView_password");
- final int TextView_lineSpacingExtra = getField(styleableClass,"TextView_lineSpacingExtra");
- final int TextView_lineSpacingMultiplier = getField(styleableClass,"TextView_lineSpacingMultiplier");
- final int TextView_inputType = getField(styleableClass,"TextView_inputType");
- final int TextView_imeOptions = getField(styleableClass,"TextView_imeOptions");
- final int TextView_imeActionLabel = getField(styleableClass,"TextView_imeActionLabel");
- final int TextView_imeActionId = getField(styleableClass,"TextView_imeActionId");
- final int TextView_privateImeOptions = getField(styleableClass,"TextView_privateImeOptions");
- final int TextView_textSelectHandleLeft = getField(styleableClass,"TextView_textSelectHandleLeft");
- final int TextView_textSelectHandleRight = getField(styleableClass,"TextView_textSelectHandleRight");
- final int TextView_textSelectHandle = getField(styleableClass,"TextView_textSelectHandle");
- final int TextView_textIsSelectable = getField(styleableClass,"TextView_textIsSelectable");
- final int TextView_textAllCaps = getField(styleableClass,"TextView_textAllCaps");
-
- final int ImageView_src = getField(styleableClass,"ImageView_src");
- final int ImageView_baselineAlignBottom = getField(styleableClass,"ImageView_baselineAlignBottom");
- final int ImageView_adjustViewBounds = getField(styleableClass,"ImageView_adjustViewBounds");
- final int ImageView_maxWidth = getField(styleableClass,"ImageView_maxWidth");
- final int ImageView_maxHeight = getField(styleableClass,"ImageView_maxHeight");
- final int ImageView_scaleType = getField(styleableClass,"ImageView_scaleType");
- final int ImageView_tint = getField(styleableClass,"ImageView_tint");
- final int ImageView_cropToPadding = getField(styleableClass,"ImageView_cropToPadding");
-
- final Resources.Theme m_theme;
- final String m_extractPath;
- Context m_context;
- final int defaultBackgroundColor;
- final int defaultTextColor;
- final boolean m_minimal;
-
- class SimpleJsonWriter
- {
- private OutputStreamWriter m_writer;
- private boolean m_addComma = false;
- private int m_indentLevel = 0;
- public SimpleJsonWriter(String filePath) throws FileNotFoundException
- {
- m_writer = new OutputStreamWriter(new FileOutputStream(filePath));
- }
-
- public void close() throws IOException
- {
- m_writer.close();
- }
-
- private void writeIndent() throws IOException
- {
- m_writer.write(" ", 0, m_indentLevel);
- }
-
- SimpleJsonWriter beginObject() throws IOException
- {
- writeIndent();
- m_writer.write("{\n");
- ++m_indentLevel;
- m_addComma = false;
- return this;
- }
-
- SimpleJsonWriter endObject() throws IOException
- {
- m_writer.write("\n");
- writeIndent();
- m_writer.write("}\n");
- --m_indentLevel;
- m_addComma = false;
- return this;
- }
-
- SimpleJsonWriter name(String name) throws IOException
- {
- if (m_addComma) {
- m_writer.write(",\n");
- }
- writeIndent();
- m_writer.write(JSONObject.quote(name) + ": ");
- m_addComma = true;
- return this;
- }
-
- SimpleJsonWriter value(JSONObject value) throws IOException
- {
- m_writer.write(value.toString());
- return this;
- }
- }
-
- class FakeCanvas extends Canvas {
- int[] chunkData = null;
- class Size {
- public int s,e;
- Size(int start, int end)
- {
- s=start;
- e=end;
- }
- }
-
- public boolean isHardwareAccelerated() {
- return true;
- }
-
- public void drawPatch(Bitmap bmp, byte[] chunks, RectF dst, Paint paint) {
- chunkData = extractChunkInfo20(chunks);
- }
- }
-
-
-
- private int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2)
- {
- try
- {
- final int stateSet1Length = stateSet1.length;
- final int stateSet2Length = stateSet2.length;
- final int[] newSet = new int[stateSet1Length + stateSet2Length];
- int k = 0;
- int i = 0;
- int j = 0;
- // This is a merge of the two input state sets and assumes that the
- // input sets are sorted by the order imposed by ViewDrawableStates.
- int[] viewDrawableStatesState=(int[]) styleableClass.getDeclaredField("ViewDrawableStates").get(null);
- for (int viewState : viewDrawableStatesState)
- {
- if (i < stateSet1Length && stateSet1[i] == viewState)
- {
- newSet[k++] = viewState;
- i++;
- } else if (j < stateSet2Length && stateSet2[j] == viewState) {
- newSet[k++] = viewState;
- j++;
- }
- if (k > 1) {
- assert(newSet[k - 1] > newSet[k - 2]);
- }
- }
- return newSet;
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- return null;
- }
-
- private Class<?> getClass(String className) {
- try {
- return Class.forName(className);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- Field getAccessibleField(Class<?> clazz, String fieldName) {
- try {
- Field f = clazz.getDeclaredField(fieldName);
- f.setAccessible(true);
- return f;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- Field tryGetAccessibleField(Class<?> clazz, String fieldName) {
- if (clazz == null)
- return null;
-
- try {
- Field f = clazz.getDeclaredField(fieldName);
- f.setAccessible(true);
- return f;
- } catch (Exception e) {
- for (Class<?> c : clazz.getInterfaces()) {
- Field f = tryGetAccessibleField(c, fieldName);
- if (f != null)
- return f;
- }
- }
- return tryGetAccessibleField(clazz.getSuperclass(), fieldName);
- }
-
- int getField(Class<?> clazz, String fieldName)
- {
- try {
- return clazz.getDeclaredField(fieldName).getInt(null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return -1;
- }
-
- JSONObject getColorStateList(ColorStateList colorList)
- {
- JSONObject json = new JSONObject();
- try
- {
- json.put("EMPTY_STATE_SET", colorList.getColorForState(EMPTY_STATE_SET, 0));
- json.put("WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(WINDOW_FOCUSED_STATE_SET, 0));
- json.put("SELECTED_STATE_SET", colorList.getColorForState(SELECTED_STATE_SET, 0));
- json.put("SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_STATE_SET, 0));
- json.put("FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_STATE_SET, 0));
- json.put("FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("ENABLED_STATE_SET", colorList.getColorForState(ENABLED_STATE_SET, 0));
- json.put("ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("ENABLED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_STATE_SET, 0));
- json.put("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_STATE_SET, 0));
- json.put("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_STATE_SET, 0));
- json.put("ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_STATE_SET", colorList.getColorForState(PRESSED_STATE_SET, 0));
- json.put("PRESSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_STATE_SET, 0));
- json.put("PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_STATE_SET, 0));
- json.put("PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, 0));
- json.put("PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET", colorList.getColorForState(PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, 0));
- } catch (JSONException e) {
- e.printStackTrace();
- }
-
- return json;
- }
-
- final int [] DrawableStates ={android.R.attr.state_active, android.R.attr.state_checked
- , android.R.attr.state_enabled, android.R.attr.state_focused
- , android.R.attr.state_pressed, android.R.attr.state_selected
- , android.R.attr.state_window_focused, 16908288, 16843597, 16843518, 16843547};
- final String[] DrawableStatesLabels = {"active", "checked", "enabled", "focused", "pressed", "selected", "window_focused", "background", "multiline", "activated", "accelerated"};
- final String[] DisableDrawableStatesLabels = {"inactive", "unchecked", "disabled", "not_focused", "no_pressed", "unselected", "window_not_focused", "background", "multiline", "activated", "accelerated"};
-
- String getFileName(String file, String[] states)
- {
- for (String state: states)
- file+="__"+state;
- return file;
- }
-
- String getStatesName(String[] states)
- {
- String statesName="";
- for (String state: states)
- {
- if (statesName.length()>0)
- statesName+="__";
- statesName += state;
- }
- return statesName;
- }
-
- void addDrawableItemIfNotExists(JSONObject json, ArrayList<Integer> list, Drawable item, String[] states, String filename)
- {
- for (Integer it : list)
- {
- if (it.equals(item.hashCode()))
- return;
- }
- list.add(item.hashCode());
- try {
- json.put(getStatesName(states), getDrawable(item, getFileName(filename, states), null));
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
-
- void addSolution(String filename, JSONObject json, int c, Drawable drawable, ArrayList<Integer> drawableList, int u)
- {
- int pos=0;
- int states[] = new int[c];
- String [] statesText = new String[c];
-
- for (int n= 0;u > 0;++n, u>>= 1)
- if ((u & 1) > 0)
- {
- statesText[pos]=DrawableStatesLabels[n];
- states[pos++]=DrawableStates[n];
- }
- drawable.setState(states);
- addDrawableItemIfNotExists(json, drawableList, drawable.getCurrent(), statesText, filename);
- }
-
- int bitCount(int u)
- {
- int n;
- for (n= 0;u > 0;++n, u&= (u - 1));
- return n;
- }
-
- JSONObject getStatesList(int [] states) throws JSONException
- {
- JSONObject json = new JSONObject();
- for (int s : states)
- {
- boolean found=false;
- for (int d = 0;d<DrawableStates.length;d++)
- {
- if (s==DrawableStates[d])
- {
- json.put(DrawableStatesLabels[d], true);
- found=true;
- break;
- }
- else if (s==-DrawableStates[d])
- {
- json.put(DrawableStatesLabels[d], false);
-
- found=true;
- break;
- }
- }
- if (!found)
- {
- json.put("unhandled_state_"+s,s>0);
- }
- }
- return json;
- }
-
- String getStatesName(int [] states)
- {
- String statesName="";
- for (int s : states)
- {
- boolean found=false;
- for (int d = 0;d<DrawableStates.length;d++)
- {
- if (s==DrawableStates[d])
- {
- if (statesName.length()>0)
- statesName+="__";
- statesName+=DrawableStatesLabels[d];
- found=true;
- break;
- }
- else if (s==-DrawableStates[d])
- {
- if (statesName.length()>0)
- statesName+="__";
- statesName+=DisableDrawableStatesLabels[d];
- found=true;
- break;
- }
- }
- if (!found)
- {
- if (statesName.length()>0)
- statesName+=";";
- statesName+=s;
- }
- }
- if (statesName.length()>0)
- return statesName;
- return "empty";
- }
-
- private JSONObject getLayerDrawable(Object drawable, String filename)
- {
- JSONObject json = new JSONObject();
- LayerDrawable layers = (LayerDrawable) drawable;
- final int nr=layers.getNumberOfLayers();
- try {
- JSONArray array =new JSONArray();
- for (int i = 0; i < nr; i++)
- {
- int id = layers.getId(i);
- if (id == -1)
- id = i;
- JSONObject layerJsonObject=getDrawable(layers.getDrawable(i), filename+"__"+id, null);
- layerJsonObject.put("id", id);
- array.put(layerJsonObject);
- }
- json.put("type", "layer");
- Rect padding = new Rect();
- if (layers.getPadding(padding))
- json.put("padding", getJsonRect(padding));
- json.put("layers", array);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getStateListDrawable(Object drawable, String filename)
- {
- JSONObject json = new JSONObject();
- try {
- StateListDrawable stateList = (StateListDrawable) drawable;
- final int numStates = (Integer) StateListDrawable.class.getMethod("getStateCount").invoke(stateList);
- JSONArray array =new JSONArray();
- for (int i = 0; i < numStates; i++)
- {
- JSONObject stateJson = new JSONObject();
- final Drawable d = (Drawable) StateListDrawable.class.getMethod("getStateDrawable", Integer.TYPE).invoke(stateList, i);
- final int [] states = (int[]) StateListDrawable.class.getMethod("getStateSet", Integer.TYPE).invoke(stateList, i);
- if (states != null)
- stateJson.put("states", getStatesList(states));
- stateJson.put("drawable", getDrawable(d, filename+"__" + (states != null ? getStatesName(states) : ("state_pos_" + i)), null));
- array.put(stateJson);
- }
- json.put("type", "stateslist");
- Rect padding = new Rect();
- if (stateList.getPadding(padding))
- json.put("padding", getJsonRect(padding));
- json.put("stateslist", array);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getGradientDrawable(GradientDrawable drawable) {
- JSONObject json = new JSONObject();
- try {
- json.put("type", "gradient");
- Object obj=drawable.getConstantState();
- Class<?> gradientStateClass=obj.getClass();
- json.put("shape",gradientStateClass.getField("mShape").getInt(obj));
- json.put("gradient",gradientStateClass.getField("mGradient").getInt(obj));
- GradientDrawable.Orientation orientation=(Orientation) gradientStateClass.getField("mOrientation").get(obj);
- json.put("orientation",orientation.name());
- int [] intArray=(int[]) gradientStateClass.getField("mGradientColors").get(obj);
- if (intArray != null)
- json.put("colors",getJsonArray(intArray, 0, intArray.length));
- json.put("positions",getJsonArray((float[]) gradientStateClass.getField("mPositions").get(obj)));
- json.put("strokeWidth",gradientStateClass.getField("mStrokeWidth").getInt(obj));
- json.put("strokeDashWidth",gradientStateClass.getField("mStrokeDashWidth").getFloat(obj));
- json.put("strokeDashGap",gradientStateClass.getField("mStrokeDashGap").getFloat(obj));
- json.put("radius",gradientStateClass.getField("mRadius").getFloat(obj));
- float [] floatArray=(float[]) gradientStateClass.getField("mRadiusArray").get(obj);
- if (floatArray!=null)
- json.put("radiusArray",getJsonArray(floatArray));
- Rect rc= (Rect) gradientStateClass.getField("mPadding").get(obj);
- if (rc!=null)
- json.put("padding",getJsonRect(rc));
- json.put("width",gradientStateClass.getField("mWidth").getInt(obj));
- json.put("height",gradientStateClass.getField("mHeight").getInt(obj));
- json.put("innerRadiusRatio",gradientStateClass.getField("mInnerRadiusRatio").getFloat(obj));
- json.put("thicknessRatio",gradientStateClass.getField("mThicknessRatio").getFloat(obj));
- json.put("innerRadius",gradientStateClass.getField("mInnerRadius").getInt(obj));
- json.put("thickness",gradientStateClass.getField("mThickness").getInt(obj));
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getRotateDrawable(RotateDrawable drawable, String filename) {
- JSONObject json = new JSONObject();
- try {
- json.put("type", "rotate");
- Object obj = drawable.getConstantState();
- Class<?> rotateStateClass = obj.getClass();
- if (Build.VERSION.SDK_INT < 23)
- json.put("drawable", getDrawable(getAccessibleField(rotateStateClass, "mDrawable").get(obj), filename, null));
- else
- json.put("drawable", getDrawable(drawable.getClass().getMethod("getDrawable").invoke(drawable), filename, null));
- json.put("pivotX", getAccessibleField(rotateStateClass, "mPivotX").getFloat(obj));
- json.put("pivotXRel", getAccessibleField(rotateStateClass, "mPivotXRel").getBoolean(obj));
- json.put("pivotY", getAccessibleField(rotateStateClass, "mPivotY").getFloat(obj));
- json.put("pivotYRel", getAccessibleField(rotateStateClass, "mPivotYRel").getBoolean(obj));
- json.put("fromDegrees", getAccessibleField(rotateStateClass, "mFromDegrees").getFloat(obj));
- json.put("toDegrees", getAccessibleField(rotateStateClass, "mToDegrees").getFloat(obj));
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getAnimationDrawable(AnimationDrawable drawable, String filename) {
- JSONObject json = new JSONObject();
- try {
- json.put("type", "animation");
- json.put("oneshot", drawable.isOneShot());
- final int count = drawable.getNumberOfFrames();
- JSONArray frames = new JSONArray();
- for (int i = 0; i < count; ++i)
- {
- JSONObject frame = new JSONObject();
- frame.put("duration", drawable.getDuration(i));
- frame.put("drawable", getDrawable(drawable.getFrame(i), filename+"__"+i, null));
- frames.put(frame);
- }
- json.put("frames", frames);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getJsonRect(Rect rect) throws JSONException
- {
- JSONObject jsonRect = new JSONObject();
- jsonRect.put("left", rect.left);
- jsonRect.put("top", rect.top);
- jsonRect.put("right", rect.right);
- jsonRect.put("bottom", rect.bottom);
- return jsonRect;
-
- }
-
- private JSONArray getJsonArray(int[] array, int pos, int len)
- {
- JSONArray a = new JSONArray();
- final int l = pos+len;
- for (int i=pos; i<l;i++)
- a.put(array[i]);
- return a;
- }
-
- private JSONArray getJsonArray(float[] array) throws JSONException
- {
- JSONArray a = new JSONArray();
- if (array != null)
- for (float val: array)
- a.put(val);
- return a;
- }
-
- private JSONObject getJsonChunkInfo(int[] chunkData) throws JSONException
- {
- JSONObject jsonRect = new JSONObject();
- if (chunkData == null)
- return jsonRect;
-
- jsonRect.put("xdivs", getJsonArray(chunkData, 3, chunkData[0]));
- jsonRect.put("ydivs", getJsonArray(chunkData, 3 + chunkData[0], chunkData[1]));
- jsonRect.put("colors", getJsonArray(chunkData, 3 + chunkData[0] + chunkData[1], chunkData[2]));
- return jsonRect;
- }
-
- private JSONObject findPatchesMarings(Drawable d) throws JSONException, NoSuchFieldException, IllegalAccessException
- {
- NinePatch np;
- Field f = tryGetAccessibleField(NinePatchDrawable.class, "mNinePatch");
- if (f != null) {
- np = (NinePatch) f.get(d);
- } else {
- Object state = getAccessibleField(NinePatchDrawable.class, "mNinePatchState").get(d);
- np = (NinePatch) getAccessibleField(state.getClass(), "mNinePatch").get(state);
- }
- return getJsonChunkInfo(extractNativeChunkInfo20(getAccessibleField(np.getClass(), "mNativeChunk").getLong(np)));
- }
-
- class DrawableCache
- {
- public DrawableCache(JSONObject json, Object drawable)
- {
- object = json;
- this.drawable = drawable;
- }
- JSONObject object;
- Object drawable;
- }
- private HashMap<String, DrawableCache> m_drawableCache = new HashMap<String, DrawableCache>();
-
- private JSONObject getRippleDrawable(Object drawable, String filename, Rect padding)
- {
- JSONObject json = getLayerDrawable(drawable, filename);
- JSONObject ripple = new JSONObject();
- try {
- final Object mState = getAccessibleField(rippleDrawableClass, "mState").get(drawable);
- ripple.put("mask", getDrawable((Drawable)getAccessibleField(rippleDrawableClass, "mMask").get(drawable), filename, padding));
- ripple.put("maxRadius", getAccessibleField(mState.getClass(), "mMaxRadius").getInt(mState));
- ripple.put("color", getColorStateList((ColorStateList)getAccessibleField(mState.getClass(), "mColor").get(mState)));
- json.put("ripple", ripple);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private HashMap<Long, Long> getStateTransitions(Object sa) throws Exception
- {
- HashMap<Long, Long> transitions = new HashMap<Long, Long>();
- final int sz = getAccessibleField(sa.getClass(), "mSize").getInt(sa);
- long[] keys = (long[]) getAccessibleField(sa.getClass(), "mKeys").get(sa);
- long[] values = (long[]) getAccessibleField(sa.getClass(), "mValues").get(sa);
- for (int i = 0; i < sz; i++) {
- transitions.put(keys[i], values[i]);
- }
- return transitions;
- }
-
- private HashMap<Integer, Integer> getStateIds(Object sa) throws Exception
- {
- HashMap<Integer, Integer> states = new HashMap<Integer, Integer>();
- final int sz = getAccessibleField(sa.getClass(), "mSize").getInt(sa);
- int[] keys = (int[]) getAccessibleField(sa.getClass(), "mKeys").get(sa);
- int[] values = (int[]) getAccessibleField(sa.getClass(), "mValues").get(sa);
- for (int i = 0; i < sz; i++) {
- states.put(keys[i], values[i]);
- }
- return states;
- }
-
- private int findStateIndex(int id, HashMap<Integer, Integer> stateIds)
- {
- for (Map.Entry<Integer, Integer> s : stateIds.entrySet()) {
- if (id == s.getValue())
- return s.getKey();
- }
- return -1;
- }
-
- private JSONObject getAnimatedStateListDrawable(Object drawable, String filename)
- {
- JSONObject json = getStateListDrawable(drawable, filename);
- try {
- Object state = getAccessibleField(animatedStateListDrawableClass, "mState").get(drawable);
-
- HashMap<Integer, Integer> stateIds = getStateIds(getAccessibleField(state.getClass(), "mStateIds").get(state));
- HashMap<Long, Long> transitions = getStateTransitions(getAccessibleField(state.getClass(), "mTransitions").get(state));
-
- for (Map.Entry<Long, Long> t : transitions.entrySet()) {
- final int toState = findStateIndex(t.getKey().intValue(), stateIds);
- final int fromState = findStateIndex((int) (t.getKey() >> 32), stateIds);
-
- JSONObject transition = new JSONObject();
- transition.put("from", fromState);
- transition.put("to", toState);
- transition.put("reverse", (t.getValue() >> 32) != 0);
-
- JSONArray stateslist = json.getJSONArray("stateslist");
- JSONObject stateobj = stateslist.getJSONObject(t.getValue().intValue());
- stateobj.put("transition", transition);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject getVPath(Object path) throws Exception
- {
- JSONObject json = new JSONObject();
- final Class<?> pathClass = path.getClass();
- json.put("type", "path");
- json.put("name", tryGetAccessibleField(pathClass, "mPathName").get(path));
- Object[] mNodes = (Object[]) tryGetAccessibleField(pathClass, "mNodes").get(path);
- JSONArray nodes = new JSONArray();
- for (Object n: mNodes) {
- JSONObject node = new JSONObject();
- node.put("type", String.valueOf(getAccessibleField(n.getClass(), "mType").getChar(n)));
- node.put("params", getJsonArray((float[])getAccessibleField(n.getClass(), "mParams").get(n)));
- nodes.put(node);
- }
- json.put("nodes", nodes);
- json.put("isClip", (Boolean) pathClass.getMethod("isClipPath").invoke(path));
-
- if (tryGetAccessibleField(pathClass, "mStrokeColor") == null)
- return json; // not VFullPath
-
- json.put("strokeColor", getAccessibleField(pathClass, "mStrokeColor").getInt(path));
- json.put("strokeWidth", getAccessibleField(pathClass, "mStrokeWidth").getFloat(path));
- json.put("fillColor", getAccessibleField(pathClass, "mFillColor").getInt(path));
- json.put("strokeAlpha", getAccessibleField(pathClass, "mStrokeAlpha").getFloat(path));
- json.put("fillRule", getAccessibleField(pathClass, "mFillRule").getInt(path));
- json.put("fillAlpha", getAccessibleField(pathClass, "mFillAlpha").getFloat(path));
- json.put("trimPathStart", getAccessibleField(pathClass, "mTrimPathStart").getFloat(path));
- json.put("trimPathEnd", getAccessibleField(pathClass, "mTrimPathEnd").getFloat(path));
- json.put("trimPathOffset", getAccessibleField(pathClass, "mTrimPathOffset").getFloat(path));
- json.put("strokeLineCap", (Paint.Cap) getAccessibleField(pathClass, "mStrokeLineCap").get(path));
- json.put("strokeLineJoin", (Paint.Join) getAccessibleField(pathClass, "mStrokeLineJoin").get(path));
- json.put("strokeMiterlimit", getAccessibleField(pathClass, "mStrokeMiterlimit").getFloat(path));
- return json;
- }
-
- @SuppressWarnings("unchecked")
- private JSONObject getVGroup(Object group) throws Exception
- {
- JSONObject json = new JSONObject();
- json.put("type", "group");
- final Class<?> groupClass = group.getClass();
- json.put("name", getAccessibleField(groupClass, "mGroupName").get(group));
- json.put("rotate", getAccessibleField(groupClass, "mRotate").getFloat(group));
- json.put("pivotX", getAccessibleField(groupClass, "mPivotX").getFloat(group));
- json.put("pivotY", getAccessibleField(groupClass, "mPivotY").getFloat(group));
- json.put("scaleX", getAccessibleField(groupClass, "mScaleX").getFloat(group));
- json.put("scaleY", getAccessibleField(groupClass, "mScaleY").getFloat(group));
- json.put("translateX", getAccessibleField(groupClass, "mTranslateX").getFloat(group));
- json.put("translateY", getAccessibleField(groupClass, "mTranslateY").getFloat(group));
-
- ArrayList<Object> mChildren = (ArrayList<Object>) getAccessibleField(groupClass, "mChildren").get(group);
- JSONArray children = new JSONArray();
- for (Object c: mChildren) {
- if (groupClass.isInstance(c))
- children.put(getVGroup(c));
- else
- children.put(getVPath(c));
- }
- json.put("children", children);
- return json;
- }
-
- private JSONObject getVectorDrawable(Object drawable, String filename, Rect padding)
- {
- JSONObject json = new JSONObject();
- try {
- json.put("type", "vector");
- final Object state = getAccessibleField(vectorDrawableClass, "mVectorState").get(drawable);
- final Class<?> stateClass = state.getClass();
- final ColorStateList mTint = (ColorStateList) getAccessibleField(stateClass, "mTint").get(state);
- if (mTint != null) {
- json.put("tintList", getColorStateList(mTint));
- json.put("tintMode", (PorterDuff.Mode) getAccessibleField(stateClass, "mTintMode").get(state));
- }
- final Object mVPathRenderer = getAccessibleField(stateClass, "mVPathRenderer").get(state);
- final Class<?> VPathRendererClass = mVPathRenderer.getClass();
- json.put("baseWidth", getAccessibleField(VPathRendererClass, "mBaseWidth").getFloat(mVPathRenderer));
- json.put("baseHeight", getAccessibleField(VPathRendererClass, "mBaseHeight").getFloat(mVPathRenderer));
- json.put("viewportWidth", getAccessibleField(VPathRendererClass, "mViewportWidth").getFloat(mVPathRenderer));
- json.put("viewportHeight", getAccessibleField(VPathRendererClass, "mViewportHeight").getFloat(mVPathRenderer));
- json.put("rootAlpha", getAccessibleField(VPathRendererClass, "mRootAlpha").getInt(mVPathRenderer));
- json.put("rootName", getAccessibleField(VPathRendererClass, "mRootName").get(mVPathRenderer));
- json.put("rootGroup", getVGroup(getAccessibleField(mVPathRenderer.getClass(), "mRootGroup").get(mVPathRenderer)));
- } catch(Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- public JSONObject getDrawable(Object drawable, String filename, Rect padding)
- {
- if (drawable == null || m_minimal)
- return null;
-
- DrawableCache dc = m_drawableCache.get(filename);
- if (dc != null)
- {
- if (dc.drawable.equals(drawable))
- return dc.object;
- else
- Log.e(QtNative.QtTAG, "Different drawable objects points to the same file name \"" + filename +"\"");
- }
- JSONObject json = new JSONObject();
- Bitmap bmp = null;
- if (drawable instanceof Bitmap)
- bmp = (Bitmap) drawable;
- else
- {
- if (drawable instanceof BitmapDrawable) {
- BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;
- bmp = bitmapDrawable.getBitmap();
- try {
- json.put("gravity", bitmapDrawable.getGravity());
- json.put("tileModeX", bitmapDrawable.getTileModeX());
- json.put("tileModeY", bitmapDrawable.getTileModeY());
- json.put("antialias", (Boolean) BitmapDrawable.class.getMethod("hasAntiAlias").invoke(bitmapDrawable));
- json.put("mipMap", (Boolean) BitmapDrawable.class.getMethod("hasMipMap").invoke(bitmapDrawable));
- json.put("tintMode", (PorterDuff.Mode) BitmapDrawable.class.getMethod("getTintMode").invoke(bitmapDrawable));
- ColorStateList tintList = (ColorStateList) BitmapDrawable.class.getMethod("getTint").invoke(bitmapDrawable);
- if (tintList != null)
- json.put("tintList", getColorStateList(tintList));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- else
- {
-
- if (rippleDrawableClass != null && rippleDrawableClass.isInstance(drawable))
- return getRippleDrawable(drawable, filename, padding);
-
- if (animatedStateListDrawableClass != null && animatedStateListDrawableClass.isInstance(drawable))
- return getAnimatedStateListDrawable(drawable, filename);
-
- if (vectorDrawableClass != null && vectorDrawableClass.isInstance(drawable))
- return getVectorDrawable(drawable, filename, padding);
-
- if (drawable instanceof ScaleDrawable)
- {
- return getDrawable(((ScaleDrawable)drawable).getDrawable(), filename, null);
- }
- if (drawable instanceof LayerDrawable)
- {
- return getLayerDrawable(drawable, filename);
- }
- if (drawable instanceof StateListDrawable)
- {
- return getStateListDrawable(drawable, filename);
- }
- if (drawable instanceof GradientDrawable)
- {
- return getGradientDrawable((GradientDrawable) drawable);
- }
- if (drawable instanceof RotateDrawable)
- {
- return getRotateDrawable((RotateDrawable) drawable, filename);
- }
- if (drawable instanceof AnimationDrawable)
- {
- return getAnimationDrawable((AnimationDrawable) drawable, filename);
- }
- if (drawable instanceof ClipDrawable)
- {
- try {
- json.put("type", "clipDrawable");
- Drawable.ConstantState dcs = ((ClipDrawable)drawable).getConstantState();
- json.put("drawable", getDrawable(getAccessibleField(dcs.getClass(), "mDrawable").get(dcs), filename, null));
- if (null != padding)
- json.put("padding", getJsonRect(padding));
- else {
- Rect _padding = new Rect();
- if (((Drawable) drawable).getPadding(_padding))
- json.put("padding", getJsonRect(_padding));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
- if (drawable instanceof ColorDrawable)
- {
- bmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
- Drawable d = (Drawable) drawable;
- d.setBounds(0, 0, 1, 1);
- d.draw(new Canvas(bmp));
- try {
- json.put("type", "color");
- json.put("color", bmp.getPixel(0, 0));
- if (null != padding)
- json.put("padding", getJsonRect(padding));
- else {
- Rect _padding = new Rect();
- if (d.getPadding(_padding))
- json.put("padding", getJsonRect(_padding));
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return json;
- }
- if (drawable instanceof InsetDrawable)
- {
- try {
- InsetDrawable d = (InsetDrawable)drawable;
- Object mInsetStateObject = getAccessibleField(InsetDrawable.class, "mState").get(d);
- Rect _padding = new Rect();
- boolean hasPadding = d.getPadding(_padding);
- return getDrawable(getAccessibleField(mInsetStateObject.getClass(), "mDrawable").get(mInsetStateObject), filename, hasPadding ? _padding : null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- else
- {
- Drawable d = (Drawable) drawable;
- int w=d.getIntrinsicWidth();
- int h=d.getIntrinsicHeight();
- d.setLevel(10000);
- if (w<1 || h< 1)
- {
- w=100;
- h=100;
- }
- bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);
- d.setBounds(0, 0, w, h);
- d.draw(new Canvas(bmp));
- if (drawable instanceof NinePatchDrawable)
- {
- NinePatchDrawable npd = (NinePatchDrawable) drawable;
- try {
- json.put("type", "9patch");
- json.put("drawable", getDrawable(bmp, filename, null));
- if (padding != null)
- json.put("padding", getJsonRect(padding));
- else {
- Rect _padding = new Rect();
- if (npd.getPadding(_padding))
- json.put("padding", getJsonRect(_padding));
- }
-
- json.put("chunkInfo", findPatchesMarings(d));
- return json;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
- FileOutputStream out;
- try {
- filename = m_extractPath+filename+".png";
- out = new FileOutputStream(filename);
- bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- json.put("type", "image");
- json.put("path", filename);
- json.put("width", bmp.getWidth());
- json.put("height", bmp.getHeight());
- m_drawableCache.put(filename, new DrawableCache(json, drawable));
-// MinistroActivity.nativeChmode(filename, 0644);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return json;
- }
-
- public void extractViewInformations(String styleName, int styleId, JSONObject json, String qtClassName, AttributeSet attribSet)
- {
- try {
- int[] viewAttrs;
- viewAttrs = (int[]) styleableClass.getDeclaredField("View").get(null);
- TypedArray a =m_theme.obtainStyledAttributes(attribSet, viewAttrs, styleId, 0);
-
- if (null != qtClassName)
- json.put("qtClass", qtClassName);
- json.put("defaultBackgroundColor", defaultBackgroundColor);
- json.put("defaultTextColorPrimary", defaultTextColor);
- final int N = a.getIndexCount();
- for (int i = 0; i < N; i++) {
- int attr = a.getIndex(i);
- if (attr == View_background)
- json.put("View_background", getDrawable(a.getDrawable(attr), styleName + "_View_background", null));
- else if (attr == View_padding)
- json.put("View_padding", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_paddingLeft)
- json.put("View_paddingLeft", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_paddingTop)
- json.put("View_paddingTop", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_paddingRight)
- json.put("View_paddingRight", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_paddingBottom)
- json.put("View_paddingBottom", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_scrollX)
- json.put("View_paddingBottom", a.getDimensionPixelOffset(attr, 0));
- else if (attr == View_scrollY)
- json.put("View_scrollY", a.getDimensionPixelOffset(attr, 0));
- else if (attr == View_id)
- json.put("View_id", a.getResourceId(attr, -1));
- else if (attr == View_tag)
- json.put("View_tag", a.getText(attr));
- else if (attr == View_fitsSystemWindows)
- json.put("View_fitsSystemWindows", a.getBoolean(attr, false));
- else if (attr == View_focusable)
- json.put("View_focusable", a.getBoolean(attr, false));
- else if (attr == View_focusableInTouchMode)
- json.put("View_focusableInTouchMode", a.getBoolean(attr, false));
- else if (attr == View_clickable)
- json.put("View_clickable", a.getBoolean(attr, false));
- else if (attr == View_longClickable)
- json.put("View_longClickable", a.getBoolean(attr, false));
- else if (attr == View_saveEnabled)
- json.put("View_saveEnabled", a.getBoolean(attr, true));
- else if (attr == View_duplicateParentState)
- json.put("View_duplicateParentState", a.getBoolean(attr, false));
- else if (attr == View_visibility)
- json.put("View_visibility", a.getInt(attr, 0));
- else if (attr == View_drawingCacheQuality)
- json.put("View_drawingCacheQuality", a.getInt(attr, 0));
- else if (attr == View_drawingCacheQuality)
- json.put("View_contentDescription", a.getString(attr));
- else if (attr == View_soundEffectsEnabled)
- json.put("View_soundEffectsEnabled", a.getBoolean(attr, true));
- else if (attr == View_hapticFeedbackEnabled)
- json.put("View_hapticFeedbackEnabled", a.getBoolean(attr, true));
- else if (attr == View_scrollbars)
- json.put("View_scrollbars", a.getInt(attr, 0));
- else if (attr == View_fadingEdge)
- json.put("View_fadingEdge", a.getInt(attr, 0));
- else if (attr == View_scrollbarStyle)
- json.put("View_scrollbarStyle", a.getInt(attr, 0));
- else if (attr == View_scrollbarFadeDuration)
- json.put("View_scrollbarFadeDuration", a.getInt(attr, 0));
- else if (attr == View_scrollbarDefaultDelayBeforeFade)
- json.put("View_scrollbarDefaultDelayBeforeFade", a.getInt(attr, 0));
- else if (attr == View_scrollbarSize)
- json.put("View_scrollbarSize", a.getDimensionPixelSize(attr, -1));
- else if (attr == View_scrollbarThumbHorizontal)
- json.put("View_scrollbarThumbHorizontal", getDrawable(a.getDrawable(attr), styleName + "_View_scrollbarThumbHorizontal", null));
- else if (attr == View_scrollbarThumbVertical)
- json.put("View_scrollbarThumbVertical", getDrawable(a.getDrawable(attr), styleName + "_View_scrollbarThumbVertical", null));
- else if (attr == View_scrollbarTrackHorizontal)
- json.put("View_scrollbarTrackHorizontal", getDrawable(a.getDrawable(attr), styleName + "_View_scrollbarTrackHorizontal", null));
- else if (attr == View_scrollbarTrackVertical)
- json.put("View_scrollbarTrackVertical", getDrawable(a.getDrawable(attr), styleName + "_View_scrollbarTrackVertical", null));
- else if (attr == View_isScrollContainer)
- json.put("View_isScrollContainer", a.getBoolean(attr, false));
- else if (attr == View_keepScreenOn)
- json.put("View_keepScreenOn", a.getBoolean(attr, false));
- else if (attr == View_filterTouchesWhenObscured)
- json.put("View_filterTouchesWhenObscured", a.getBoolean(attr, false));
- else if (attr == View_nextFocusLeft)
- json.put("View_nextFocusLeft", a.getResourceId(attr, -1));
- else if (attr == View_nextFocusRight)
- json.put("View_nextFocusRight", a.getResourceId(attr, -1));
- else if (attr == View_nextFocusUp)
- json.put("View_nextFocusUp", a.getResourceId(attr, -1));
- else if (attr == View_nextFocusDown)
- json.put("View_nextFocusDown", a.getResourceId(attr, -1));
- else if (attr == View_minWidth)
- json.put("View_minWidth", a.getDimensionPixelSize(attr, 0));
- else if (attr == View_minHeight)
- json.put("View_minHeight", a.getDimensionPixelSize(attr, 0));
- else if (attr == View_onClick)
- json.put("View_onClick", a.getString(attr));
- else if (attr == View_overScrollMode)
- json.put("View_overScrollMode", a.getInt(attr, 1));
- else if (attr == View_paddingStart)
- json.put("View_paddingStart", a.getDimensionPixelSize(attr, 0));
- else if (attr == View_paddingEnd)
- json.put("View_paddingEnd", a.getDimensionPixelSize(attr, 0));
- }
- a.recycle();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public JSONObject extractTextAppearance(int styleId)
- {
- JSONObject json = new JSONObject();
- try
- {
- TypedArray a = m_theme.obtainStyledAttributes(styleId, (int[]) styleableClass.getDeclaredField("TextAppearance").get(null));
- int n = a.getIndexCount();
- for (int i = 0; i < n; i++)
- {
- int attr = a.getIndex(i);
- if (attr == TextAppearance_textColorHighlight)
- json.put("TextAppearance_textColorHighlight", a.getColor(attr, 0));
- else if (attr == TextAppearance_textColor)
- json.put("TextAppearance_textColor", getColorStateList(a.getColorStateList(attr)));
- else if (attr == TextAppearance_textColorHint)
- json.put("TextAppearance_textColorHint", getColorStateList(a.getColorStateList(attr)));
- else if (attr == TextAppearance_textColorLink)
- json.put("TextAppearance_textColorLink", getColorStateList(a.getColorStateList(attr)));
- else if (attr == TextAppearance_textSize)
- json.put("TextAppearance_textSize", a.getDimensionPixelSize(attr, 15));
- else if (attr == TextAppearance_typeface)
- json.put("TextAppearance_typeface", a.getInt(attr, -1));
- else if (attr == TextAppearance_textStyle)
- json.put("TextAppearance_textStyle", a.getInt(attr, -1));
- else if (attr == TextAppearance_textAllCaps)
- json.put("TextAppearance_textAllCaps", a.getBoolean(attr, false));
- }
- a.recycle();
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- return json;
- }
-
- public JSONObject extractTextAppearanceInformations(String styleName, String qtClass, AttributeSet attribSet, int textAppearance)
- {
- JSONObject json = new JSONObject();
- try
- {
- int textColorHighlight = 0; //
- ColorStateList textColor = null; //
- ColorStateList textColorHint = null; //
- ColorStateList textColorLink = null; //
- int textSize = 15; //
- int typefaceIndex = -1; //
- int styleIndex = -1;
- boolean allCaps = false;
-
- Class<?> attrClass= Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- extractViewInformations(styleName, styleId, json, qtClass, attribSet);
-
- int[] textViewAttrs=(int[]) styleableClass.getDeclaredField("TextView").get(null);
- TypedArray a =m_theme.obtainStyledAttributes(null, textViewAttrs, styleId, 0);
-
- TypedArray appearance = null;
- if (-1==textAppearance)
- textAppearance = a.getResourceId(styleableClass.getDeclaredField("TextView_textAppearance").getInt(null), -1);
-
- if (textAppearance != -1)
- appearance = m_theme.obtainStyledAttributes(textAppearance, (int[]) styleableClass.getDeclaredField("TextAppearance").get(null));
-
- if (appearance != null)
- {
- int n = appearance.getIndexCount();
- for (int i = 0; i < n; i++)
- {
- int attr = appearance.getIndex(i);
- if (attr == TextAppearance_textColorHighlight)
- textColorHighlight = appearance.getColor(attr, textColorHighlight);
- else if (attr == TextAppearance_textColor)
- textColor = appearance.getColorStateList(attr);
- else if (attr == TextAppearance_textColorHint)
- textColorHint = appearance.getColorStateList(attr);
- else if (attr == TextAppearance_textColorLink)
- textColorLink = appearance.getColorStateList(attr);
- else if (attr == TextAppearance_textSize)
- textSize = appearance.getDimensionPixelSize(attr, textSize);
- else if (attr == TextAppearance_typeface)
- typefaceIndex = appearance.getInt(attr, -1);
- else if (attr == TextAppearance_textStyle)
- styleIndex = appearance.getInt(attr, -1);
- else if (attr == TextAppearance_textAllCaps)
- allCaps = appearance.getBoolean(attr, false);
- }
- appearance.recycle();
- }
-
- int n = a.getIndexCount();
-
- for (int i = 0; i < n; i++) {
- int attr = a.getIndex(i);
-
- if (attr == TextView_editable)
- json.put("TextView_editable", a.getBoolean(attr, false));
- else if (attr == TextView_inputMethod)
- json.put("TextView_inputMethod", a.getText(attr));
- else if (attr == TextView_numeric)
- json.put("TextView_numeric", a.getInt(attr, 0));
- else if (attr == TextView_digits)
- json.put("TextView_digits", a.getText(attr));
- else if (attr == TextView_phoneNumber)
- json.put("TextView_phoneNumber", a.getBoolean(attr, false));
- else if (attr == TextView_autoText)
- json.put("TextView_autoText", a.getBoolean(attr, false));
- else if (attr == TextView_capitalize)
- json.put("TextView_capitalize", a.getInt(attr, -1));
- else if (attr == TextView_bufferType)
- json.put("TextView_bufferType", a.getInt(attr, 0));
- else if (attr == TextView_selectAllOnFocus)
- json.put("TextView_selectAllOnFocus", a.getBoolean(attr, false));
- else if (attr == TextView_autoLink)
- json.put("TextView_autoLink", a.getInt(attr, 0));
- else if (attr == TextView_linksClickable)
- json.put("TextView_linksClickable", a.getBoolean(attr, true));
- else if (attr == TextView_linksClickable)
- json.put("TextView_linksClickable", a.getBoolean(attr, true));
- else if (attr == TextView_drawableLeft)
- json.put("TextView_drawableLeft", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableLeft", null));
- else if (attr == TextView_drawableTop)
- json.put("TextView_drawableTop", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableTop", null));
- else if (attr == TextView_drawableRight)
- json.put("TextView_drawableRight", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableRight", null));
- else if (attr == TextView_drawableBottom)
- json.put("TextView_drawableBottom", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableBottom", null));
- else if (attr == TextView_drawableStart)
- json.put("TextView_drawableStart", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableStart", null));
- else if (attr == TextView_drawableEnd)
- json.put("TextView_drawableEnd", getDrawable(a.getDrawable(attr), styleName + "_TextView_drawableEnd", null));
- else if (attr == TextView_drawablePadding)
- json.put("TextView_drawablePadding", a.getDimensionPixelSize(attr, 0));
- else if (attr == TextView_textCursorDrawable) {
- try {
- json.put("TextView_textCursorDrawable", getDrawable(a.getDrawable(attr), styleName + "_TextView_textCursorDrawable", null));
- } catch (Exception e_) {
- try {
- json.put("TextView_textCursorDrawable", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textCursorDrawable", null));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }else if (attr == TextView_maxLines)
- json.put("TextView_maxLines", a.getInt(attr, -1));
- else if (attr == TextView_maxHeight)
- json.put("TextView_maxHeight", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_lines)
- json.put("TextView_lines", a.getInt(attr, -1));
- else if (attr == TextView_height)
- json.put("TextView_height", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_minLines)
- json.put("TextView_minLines", a.getInt(attr, -1));
- else if (attr == TextView_minHeight)
- json.put("TextView_minHeight", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_maxEms)
- json.put("TextView_maxEms", a.getInt(attr, -1));
- else if (attr == TextView_maxWidth)
- json.put("TextView_maxWidth", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_ems)
- json.put("TextView_ems", a.getInt(attr, -1));
- else if (attr == TextView_width)
- json.put("TextView_width", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_minEms)
- json.put("TextView_minEms", a.getInt(attr, -1));
- else if (attr == TextView_minWidth)
- json.put("TextView_minWidth", a.getDimensionPixelSize(attr, -1));
- else if (attr == TextView_gravity)
- json.put("TextView_gravity", a.getInt(attr, -1));
- else if (attr == TextView_hint)
- json.put("TextView_hint", a.getText(attr));
- else if (attr == TextView_text)
- json.put("TextView_text", a.getText(attr));
- else if (attr == TextView_scrollHorizontally)
- json.put("TextView_scrollHorizontally", a.getBoolean(attr, false));
- else if (attr == TextView_singleLine)
- json.put("TextView_singleLine", a.getBoolean(attr, false));
- else if (attr == TextView_ellipsize)
- json.put("TextView_ellipsize", a.getInt(attr, -1));
- else if (attr == TextView_marqueeRepeatLimit)
- json.put("TextView_marqueeRepeatLimit", a.getInt(attr, 3));
- else if (attr == TextView_includeFontPadding)
- json.put("TextView_includeFontPadding", a.getBoolean(attr, true));
- else if (attr == TextView_cursorVisible)
- json.put("TextView_cursorVisible", a.getBoolean(attr, true));
- else if (attr == TextView_maxLength)
- json.put("TextView_maxLength", a.getInt(attr, -1));
- else if (attr == TextView_textScaleX)
- json.put("TextView_textScaleX", a.getFloat(attr, 1.0f));
- else if (attr == TextView_freezesText)
- json.put("TextView_freezesText", a.getBoolean(attr, false));
- else if (attr == TextView_shadowColor)
- json.put("TextView_shadowColor", a.getInt(attr, 0));
- else if (attr == TextView_shadowDx)
- json.put("TextView_shadowDx", a.getFloat(attr, 0));
- else if (attr == TextView_shadowDy)
- json.put("TextView_shadowDy", a.getFloat(attr, 0));
- else if (attr == TextView_shadowRadius)
- json.put("TextView_shadowRadius", a.getFloat(attr, 0));
- else if (attr == TextView_enabled)
- json.put("TextView_enabled", a.getBoolean(attr,true));
- else if (attr == TextView_textColorHighlight)
- textColorHighlight = a.getColor(attr, textColorHighlight);
- else if (attr == TextView_textColor)
- textColor = a.getColorStateList(attr);
- else if (attr == TextView_textColorHint)
- textColorHint = a.getColorStateList(attr);
- else if (attr == TextView_textColorLink)
- textColorLink = a.getColorStateList(attr);
- else if (attr == TextView_textSize)
- textSize = a.getDimensionPixelSize(attr, textSize);
- else if (attr == TextView_typeface)
- typefaceIndex = a.getInt(attr, typefaceIndex);
- else if (attr == TextView_textStyle)
- styleIndex = a.getInt(attr, styleIndex);
- else if (attr == TextView_password)
- json.put("TextView_password", a.getBoolean(attr,false));
- else if (attr == TextView_lineSpacingExtra)
- json.put("TextView_lineSpacingExtra", a.getDimensionPixelSize(attr, 0));
- else if (attr == TextView_lineSpacingMultiplier)
- json.put("TextView_lineSpacingMultiplier", a.getFloat(attr, 1.0f));
- else if (attr == TextView_inputType)
- json.put("TextView_inputType", a.getInt(attr, EditorInfo.TYPE_NULL));
- else if (attr == TextView_imeOptions)
- json.put("TextView_imeOptions", a.getInt(attr, EditorInfo.IME_NULL));
- else if (attr == TextView_imeActionLabel)
- json.put("TextView_imeActionLabel", a.getText(attr));
- else if (attr == TextView_imeActionId)
- json.put("TextView_imeActionId", a.getInt(attr,0));
- else if (attr == TextView_privateImeOptions)
- json.put("TextView_privateImeOptions", a.getString(attr));
- else if (attr == TextView_textSelectHandleLeft && styleName.equals("textViewStyle")) {
- try {
- json.put("TextView_textSelectHandleLeft", getDrawable(a.getDrawable(attr), styleName + "_TextView_textSelectHandleLeft", null));
- } catch (Exception _e) {
- try {
- json.put("TextView_textSelectHandleLeft", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandleLeft", null));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } else if (attr == TextView_textSelectHandleRight && styleName.equals("textViewStyle")) {
- try {
- json.put("TextView_textSelectHandleRight", getDrawable(a.getDrawable(attr), styleName + "_TextView_textSelectHandleRight", null));
- } catch (Exception _e) {
- try {
- json.put("TextView_textSelectHandleRight", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandleRight", null));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } else if (attr == TextView_textSelectHandle && styleName.equals("textViewStyle")) {
- try {
- json.put("TextView_textSelectHandle", getDrawable(a.getDrawable(attr), styleName + "_TextView_textSelectHandle", null));
- } catch (Exception _e) {
- try {
- json.put("TextView_textSelectHandle", getDrawable(m_context.getResources().getDrawable(a.getResourceId(attr, 0)), styleName + "_TextView_textSelectHandle", null));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } else if (attr == TextView_textIsSelectable)
- json.put("TextView_textIsSelectable", a.getBoolean(attr, false));
- else if (attr == TextView_textAllCaps)
- allCaps = a.getBoolean(attr, false);
- }
- a.recycle();
-
- json.put("TextAppearance_textColorHighlight",textColorHighlight);
- json.put("TextAppearance_textColor", getColorStateList(textColor));
- json.put("TextAppearance_textColorHint", getColorStateList(textColorHint));
- json.put("TextAppearance_textColorLink", getColorStateList(textColorLink));
- json.put("TextAppearance_textSize",textSize);
- json.put("TextAppearance_typeface",typefaceIndex);
- json.put("TextAppearance_textStyle",styleIndex);
- json.put("TextAppearance_textAllCaps",allCaps);
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- return json;
- }
-
- final String[] sScaleTypeArray = {
- "MATRIX",
- "FIT_XY",
- "FIT_START",
- "FIT_CENTER",
- "FIT_END",
- "CENTER",
- "CENTER_CROP",
- "CENTER_INSIDE"
- };
-
- public JSONObject extractImageViewInformations(String styleName, String qtClassName )
- {
- JSONObject json = new JSONObject();
- try
- {
- Class<?> attrClass= Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- extractViewInformations(styleName, styleId, json, qtClassName, null);
-
- int[] imageViewAttrs=(int[]) styleableClass.getDeclaredField("ImageView").get(null);
- TypedArray a =m_theme.obtainStyledAttributes(null, imageViewAttrs, styleId, 0);
- Drawable d = a.getDrawable(ImageView_src);
- if (d != null)
- json.put("ImageView_src", getDrawable(d, styleName + "_ImageView_src", null));
-
- json.put("ImageView_baselineAlignBottom", a.getBoolean(ImageView_baselineAlignBottom, false));
- json.put("ImageView_adjustViewBounds", a.getBoolean(ImageView_adjustViewBounds, false));
- json.put("ImageView_maxWidth", a.getDimensionPixelSize(ImageView_maxWidth, Integer.MAX_VALUE));
- json.put("ImageView_maxHeight", a.getDimensionPixelSize(ImageView_maxHeight, Integer.MAX_VALUE));
- int index = a.getInt(ImageView_scaleType, -1);
- if (index >= 0)
- json.put("ImageView_scaleType", sScaleTypeArray[index]);
-
- int tint = a.getInt(ImageView_tint, 0);
- if (tint != 0)
- json.put("ImageView_tint", tint);
-
-
- json.put("ImageView_cropToPadding",a.getBoolean(ImageView_cropToPadding, false));
- a.recycle();
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- return json;
-
- }
-
- void extractCompoundButton(SimpleJsonWriter jsonWriter, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- Class<?> attrClass;
- try {
- attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
- int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("CompoundButton").get(null);
-
- TypedArray a = m_theme.obtainStyledAttributes(
- null, compoundButtonAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"CompoundButton_button"));
- if (d != null)
- json.put("CompoundButton_button", getDrawable(d, styleName + "_CompoundButton_button", null));
-
- a.recycle();
- jsonWriter.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractProgressBarInfo(JSONObject json, String styleName)
- {
- Class<?> attrClass;
- try {
- attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
- int[] progressBarAttrs=(int[]) styleableClass.getDeclaredField("ProgressBar").get(null);
-
- TypedArray a = m_theme.obtainStyledAttributes(null, progressBarAttrs, styleId, 0);
- int mMinWidth = 24;
- int mMaxWidth = 48;
- int mMinHeight = 24;
- int mMaxHeight = 48;
- mMinWidth = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_minWidth"), mMinWidth);
- mMaxWidth = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_maxWidth"), mMaxWidth);
- mMinHeight = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_minHeight"), mMinHeight);
- mMaxHeight = a.getDimensionPixelSize(getField(styleableClass, "ProgressBar_maxHeight"), mMaxHeight);
-
- json.put("ProgressBar_indeterminateDuration", a.getInt(getField(styleableClass, "ProgressBar_indeterminateDuration"), 4000));
- json.put("ProgressBar_minWidth", mMinWidth);
- json.put("ProgressBar_maxWidth", mMaxWidth);
- json.put("ProgressBar_minHeight", mMinHeight);
- json.put("ProgressBar_maxHeight", mMaxHeight);
- json.put("ProgressBar_progress_id", android.R.id.progress);
- json.put("ProgressBar_secondaryProgress_id", android.R.id.secondaryProgress);
-
- Drawable d = a.getDrawable(getField(styleableClass,"ProgressBar_progressDrawable"));
- if (d != null)
- json.put("ProgressBar_progressDrawable", getDrawable(d, styleName + "_ProgressBar_progressDrawable", null));
-
- d = a.getDrawable(getField(styleableClass,"ProgressBar_indeterminateDrawable"));
- if (d != null)
- json.put("ProgressBar_indeterminateDrawable", getDrawable(d, styleName + "_ProgressBar_indeterminateDrawable", null));
-
- a.recycle();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractProgressBar(SimpleJsonWriter writer, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- try {
- extractProgressBarInfo(json, styleName);
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractAbsSeekBar(SimpleJsonWriter jsonWriter, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- extractProgressBarInfo(json, styleName);
- Class<?> attrClass;
- try {
- attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
- int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("SeekBar").get(null);
-
- TypedArray a = m_theme.obtainStyledAttributes(
- null, compoundButtonAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"SeekBar_thumb"));
- if (d != null)
- json.put("SeekBar_thumb", getDrawable(d, styleName + "_SeekBar_thumb", null));
-
- try {
- json.put("SeekBar_thumbOffset", styleableClass.getDeclaredField("SeekBar_thumbOffset").getInt(null));
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- a.recycle();
- jsonWriter.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractSwitch(SimpleJsonWriter jsonWriter, String styleName, String qtClass)
- {
- JSONObject json = new JSONObject();
- try {
- Class<?> attrClass = Class.forName("com.android.internal.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- int[] switchAttrs = (int[]) styleableClass.getDeclaredField("Switch").get(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, switchAttrs, styleId, 0);
-
- Drawable thumb = a.getDrawable(getField(styleableClass,"Switch_thumb"));
- if (thumb != null)
- json.put("Switch_thumb", getDrawable(thumb, styleName + "_Switch_thumb", null));
-
- Drawable track = a.getDrawable(getField(styleableClass,"Switch_track"));
- if (track != null)
- json.put("Switch_track", getDrawable(track, styleName + "_Switch_track", null));
-
- int textAppearance = a.getResourceId(styleableClass.getDeclaredField("Switch_switchTextAppearance").getInt(null), -1);
- json.put("Switch_switchTextAppearance", extractTextAppearance(textAppearance));
-
- json.put("Switch_textOn", a.getText(getField(styleableClass, "Switch_textOn")));
- json.put("Switch_textOff", a.getText(getField(styleableClass, "Switch_textOff")));
- json.put("Switch_switchMinWidth", a.getDimensionPixelSize(getField(styleableClass, "Switch_switchMinWidth"), 0));
- json.put("Switch_switchPadding", a.getDimensionPixelSize(getField(styleableClass, "Switch_switchPadding"), 0));
- json.put("Switch_thumbTextPadding", a.getDimensionPixelSize(getField(styleableClass, "Switch_thumbTextPadding"), 0));
-
- json.put("Switch_showText", a.getBoolean(getField(styleableClass, "Switch_showText"), true));
- json.put("Switch_splitTrack", a.getBoolean(getField(styleableClass, "Switch_splitTrack"), false));
-
- a.recycle();
- jsonWriter.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- JSONObject extractCheckedTextView(AttributeSet attribSet, String itemName)
- {
- JSONObject json = extractTextAppearanceInformations("textViewStyle", itemName, attribSet, -1);
- try {
- Class<?> attrClass= Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField("textViewStyle").getInt(null);
- int[] compoundButtonAttrs=(int[]) styleableClass.getDeclaredField("CheckedTextView").get(null);
-
- TypedArray a = m_theme.obtainStyledAttributes(attribSet, compoundButtonAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"CheckedTextView_checkMark"));
- if (d != null)
- json.put("CheckedTextView_checkMark", getDrawable(d, itemName+"_CheckedTextView_checkMark", null));
-
- a.recycle();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return json;
- }
-
- private JSONObject extractItemStyle(int resourceId, String itemName, int textAppearance) {
- try
- {
- XmlResourceParser parser = m_context.getResources().getLayout(resourceId);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG &&
- type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
-
- if (type != XmlPullParser.START_TAG) {
- return null;
- }
-
- AttributeSet attributes = Xml.asAttributeSet(parser);
- String name = parser.getName();
- if (name.equals("TextView"))
- return extractTextAppearanceInformations("textViewStyle", itemName, attributes, textAppearance);
- if (name.equals("CheckedTextView"))
- return extractCheckedTextView(attributes, itemName);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- private void extractItemsStyle(SimpleJsonWriter jsonWriter) {
- try
- {
- jsonWriter.name("simple_list_item").value(extractItemStyle(android.R.layout.simple_list_item_1, "simple_list_item", android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_list_item_checked").value(extractItemStyle(android.R.layout.simple_list_item_checked, "simple_list_item_checked", android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_list_item_multiple_choice").value(extractItemStyle(android.R.layout.simple_list_item_multiple_choice, "simple_list_item_multiple_choice", android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_list_item_single_choice").value(extractItemStyle(android.R.layout.simple_list_item_single_choice, "simple_list_item_single_choice", android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_spinner_item").value(extractItemStyle(android.R.layout.simple_spinner_item, "simple_spinner_item", -1));
- jsonWriter.name("simple_spinner_dropdown_item").value(extractItemStyle(android.R.layout.simple_spinner_dropdown_item, "simple_spinner_dropdown_item",android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_dropdown_item_1line").value(extractItemStyle(android.R.layout.simple_dropdown_item_1line, "simple_dropdown_item_1line",android.R.style.TextAppearance_Large));
- jsonWriter.name("simple_selectable_list_item").value(extractItemStyle(android.R.layout.simple_selectable_list_item, "simple_selectable_list_item",android.R.style.TextAppearance_Large));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractListView(SimpleJsonWriter writer, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- try {
- Class<?> attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- int[] styleAttrs = (int[]) styleableClass.getDeclaredField("ListView").get(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, styleAttrs, styleId, 0);
-
- Drawable divider = a.getDrawable(getField(styleableClass,"ListView_divider"));
- if (divider != null)
- json.put("ListView_divider", getDrawable(divider, styleName + "_ListView_divider", null));
-
- json.put("ListView_dividerHeight", a.getDimensionPixelSize(getField(styleableClass, "ListView_dividerHeight"), 0));
-
- a.recycle();
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractCalendar(SimpleJsonWriter writer, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- try {
- Class<?> attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- int[] styleAttrs = (int[]) styleableClass.getDeclaredField("CalendarView").get(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, styleAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"CalendarView_selectedDateVerticalBar"));
- if (d != null)
- json.put("CalendarView_selectedDateVerticalBar", getDrawable(d, styleName + "_CalendarView_selectedDateVerticalBar", null));
-
- int dateTextAppearance = a.getResourceId(styleableClass.getDeclaredField("CalendarView_dateTextAppearance").getInt(null), -1);
- json.put("CalendarView_dateTextAppearance", extractTextAppearance(dateTextAppearance));
-
- int weekDayTextAppearance = a.getResourceId(styleableClass.getDeclaredField("CalendarView_weekDayTextAppearance").getInt(null), -1);
- json.put("CalendarView_weekDayTextAppearance", extractTextAppearance(weekDayTextAppearance));
-
- json.put("CalendarView_firstDayOfWeek", a.getInt(getField(styleableClass, "CalendarView_firstDayOfWeek"), 0));
- json.put("CalendarView_focusedMonthDateColor", a.getColor(getField(styleableClass, "CalendarView_focusedMonthDateColor"), 0));
- json.put("CalendarView_selectedWeekBackgroundColor", a.getColor(getField(styleableClass, "CalendarView_selectedWeekBackgroundColor"), 0));
- json.put("CalendarView_showWeekNumber", a.getBoolean(getField(styleableClass, "CalendarView_showWeekNumber"), true));
- json.put("CalendarView_shownWeekCount", a.getInt(getField(styleableClass, "CalendarView_shownWeekCount"), 6));
- json.put("CalendarView_unfocusedMonthDateColor", a.getColor(getField(styleableClass, "CalendarView_unfocusedMonthDateColor"), 0));
- json.put("CalendarView_weekNumberColor", a.getColor(getField(styleableClass, "CalendarView_weekNumberColor"), 0));
- json.put("CalendarView_weekSeparatorLineColor", a.getColor(getField(styleableClass, "CalendarView_weekSeparatorLineColor"), 0));
-
- a.recycle();
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractToolBar(SimpleJsonWriter writer, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- try {
- Class<?> attrClass = Class.forName("com.android.internal.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- int[] styleAttrs = (int[]) styleableClass.getDeclaredField("ActionBar").get(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, styleAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"ActionBar_background"));
- if (d != null)
- json.put("ActionBar_background", getDrawable(d, styleName + "_ActionBar_background", null));
-
- d = a.getDrawable(getField(styleableClass,"ActionBar_backgroundStacked"));
- if (d != null)
- json.put("ActionBar_backgroundStacked", getDrawable(d, styleName + "_ActionBar_backgroundStacked", null));
-
- d = a.getDrawable(getField(styleableClass,"ActionBar_backgroundSplit"));
- if (d != null)
- json.put("ActionBar_backgroundSplit", getDrawable(d, styleName + "_ActionBar_backgroundSplit", null));
-
- d = a.getDrawable(getField(styleableClass,"ActionBar_divider"));
- if (d != null)
- json.put("ActionBar_divider", getDrawable(d, styleName + "_ActionBar_divider", null));
-
- json.put("ActionBar_itemPadding", a.getDimensionPixelSize(getField(styleableClass, "ActionBar_itemPadding"), 0));
-
- a.recycle();
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- void extractTabBar(SimpleJsonWriter writer, String styleName, String qtClass)
- {
- JSONObject json = extractTextAppearanceInformations(styleName, qtClass, null, -1);
- try {
- Class<?> attrClass = Class.forName("android.R$attr");
- int styleId = attrClass.getDeclaredField(styleName).getInt(null);
-
- int[] styleAttrs = (int[]) styleableClass.getDeclaredField("LinearLayout").get(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, styleAttrs, styleId, 0);
-
- Drawable d = a.getDrawable(getField(styleableClass,"LinearLayout_divider"));
- if (d != null)
- json.put("LinearLayout_divider", getDrawable(d, styleName + "_LinearLayout_divider", null));
- json.put("LinearLayout_showDividers", a.getInt(getField(styleableClass, "LinearLayout_showDividers"), 0));
- json.put("LinearLayout_dividerPadding", a.getDimensionPixelSize(getField(styleableClass, "LinearLayout_dividerPadding"), 0));
-
- a.recycle();
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void extractWindow(SimpleJsonWriter writer, String styleName) {
- JSONObject json = new JSONObject();
- try
- {
- Class<?> attrClass = Class.forName("android.R$attr");
- int[] windowAttrs = (int[]) styleableClass.getDeclaredField("Window").get(null);
-
- int backgroundId = attrClass.getDeclaredField("windowBackground").getInt(null);
- TypedArray a = m_theme.obtainStyledAttributes(null, windowAttrs, backgroundId, 0);
- Drawable background = a.getDrawable(getField(styleableClass, "Window_windowBackground"));
- if (background != null)
- json.put("Window_windowBackground", getDrawable(background, styleName + "_Window_windowBackground", null));
- a.recycle();
-
- int frameId = attrClass.getDeclaredField("windowFrame").getInt(null);
- a = m_theme.obtainStyledAttributes(null, windowAttrs, frameId, 0);
- Drawable frame = a.getDrawable(getField(styleableClass, "Window_windowFrame"));
- if (frame != null)
- json.put("Window_windowFrame", getDrawable(frame, styleName + "_Window_windowFrame", null));
- a.recycle();
-
- writer.name(styleName).value(json);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private JSONObject extractDefaultPalette()
- {
- TypedArray array = m_theme.obtainStyledAttributes(new int[]{
- android.R.attr.textAppearance
- });
- int pos = 0;
- JSONObject json = extractTextAppearance(array.getResourceId(pos++, -1));
- try {
- json.put("defaultBackgroundColor", defaultBackgroundColor);
- json.put("defaultTextColorPrimary", defaultTextColor);
- } catch (Exception e) {
- e.printStackTrace();
- }
- array.recycle();
- return json;
- }
-
- public ExtractStyle(Context context, String extractPath, boolean minimal)
- {
-// Log.i(MinistroService.TAG, "Extract " + extractPath);
- m_minimal = minimal;
- m_extractPath = extractPath + "/";
- new File(m_extractPath).mkdirs();
-// MinistroActivity.nativeChmode(m_extractPath, 0755);
- m_context = context;
- m_theme = context.getTheme();
- TypedArray array = m_theme.obtainStyledAttributes(new int[]{
- android.R.attr.colorBackground,
- android.R.attr.textColorPrimary,
- android.R.attr.textColor
- });
- defaultBackgroundColor = array.getColor(0, 0);
- int textColor = array.getColor(1, 0xFFFFFF);
- if (textColor == 0xFFFFFF)
- textColor = array.getColor(2, 0xFFFFFF);
- defaultTextColor = textColor;
- array.recycle();
-
- try
- {
- SimpleJsonWriter jsonWriter = new SimpleJsonWriter(m_extractPath+"style.json");
- jsonWriter.beginObject();
- try {
- jsonWriter.name("defaultStyle").value(extractDefaultPalette());
- extractWindow(jsonWriter, "windowStyle");
- jsonWriter.name("buttonStyle").value(extractTextAppearanceInformations("buttonStyle", "QPushButton", null, -1));
- jsonWriter.name("spinnerStyle").value(extractTextAppearanceInformations("spinnerStyle", "QComboBox", null, -1));
- extractProgressBar(jsonWriter, "progressBarStyleHorizontal", "QProgressBar");
- extractProgressBar(jsonWriter, "progressBarStyleLarge", null);
- extractProgressBar(jsonWriter, "progressBarStyleSmall", null);
- extractProgressBar(jsonWriter, "progressBarStyle", null);
- extractAbsSeekBar(jsonWriter, "seekBarStyle", "QSlider");
- extractSwitch(jsonWriter, "switchStyle", null);
- extractCompoundButton(jsonWriter, "checkboxStyle", "QCheckBox");
- jsonWriter.name("editTextStyle").value(extractTextAppearanceInformations("editTextStyle", "QLineEdit", null, -1));
- extractCompoundButton(jsonWriter, "radioButtonStyle", "QRadioButton");
- jsonWriter.name("textViewStyle").value(extractTextAppearanceInformations("textViewStyle", "QWidget", null, -1));
- jsonWriter.name("scrollViewStyle").value(extractTextAppearanceInformations("scrollViewStyle", "QAbstractScrollArea", null, -1));
- extractListView(jsonWriter, "listViewStyle", "QListView");
- jsonWriter.name("listSeparatorTextViewStyle").value(extractTextAppearanceInformations("listSeparatorTextViewStyle", null, null, -1));
- extractItemsStyle(jsonWriter);
- extractCompoundButton(jsonWriter, "buttonStyleToggle", null);
- extractCalendar(jsonWriter, "calendarViewStyle", "QCalendarWidget");
- extractToolBar(jsonWriter, "actionBarStyle", "QToolBar");
- jsonWriter.name("actionButtonStyle").value(extractTextAppearanceInformations("actionButtonStyle", "QToolButton", null, -1));
- jsonWriter.name("actionBarTabTextStyle").value(extractTextAppearanceInformations("actionBarTabTextStyle", null, null, -1));
- jsonWriter.name("actionBarTabStyle").value(extractTextAppearanceInformations("actionBarTabStyle", null, null, -1));
- jsonWriter.name("actionOverflowButtonStyle").value(extractImageViewInformations("actionOverflowButtonStyle", null));
- extractTabBar(jsonWriter, "actionBarTabBarStyle", "QTabBar");
- } catch (Exception e) {
- e.printStackTrace();
- }
- jsonWriter.endObject();
- jsonWriter.close();
-// MinistroActivity.nativeChmode(m_extractPath+"style.json", 0644);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
deleted file mode 100644
index a7c5802c43..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
+++ /dev/null
@@ -1,1305 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.Rect;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.text.method.MetaKeyKeyListener;
-import android.util.Base64;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-import android.view.ViewTreeObserver;
-import android.widget.ImageView;
-import android.widget.PopupMenu;
-import android.hardware.display.DisplayManager;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import org.qtproject.qt5.android.accessibility.QtAccessibilityDelegate;
-
-public class QtActivityDelegate
-{
- private Activity m_activity = null;
- private Method m_super_dispatchKeyEvent = null;
- private Method m_super_onRestoreInstanceState = null;
- private Method m_super_onRetainNonConfigurationInstance = null;
- private Method m_super_onSaveInstanceState = null;
- private Method m_super_onKeyDown = null;
- private Method m_super_onKeyUp = null;
- private Method m_super_onConfigurationChanged = null;
- private Method m_super_onActivityResult = null;
- private Method m_super_dispatchGenericMotionEvent = null;
- private Method m_super_onWindowFocusChanged = null;
-
- private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
- private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
- private static final String MAIN_LIBRARY_KEY = "main.library";
- private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
- private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
- private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
- private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level";
- private static final String EXTRACT_STYLE_KEY = "extract.android.style";
- private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
-
- public static final int SYSTEM_UI_VISIBILITY_NORMAL = 0;
- public static final int SYSTEM_UI_VISIBILITY_FULLSCREEN = 1;
- public static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2;
-
- private static String m_environmentVariables = null;
- private static String m_applicationParameters = null;
-
- private int m_currentRotation = -1; // undefined
- private int m_nativeOrientation = Configuration.ORIENTATION_UNDEFINED;
-
- private String m_mainLib;
- private long m_metaState;
- private int m_lastChar = 0;
- private int m_softInputMode = 0;
- private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
- private boolean m_started = false;
- private HashMap<Integer, QtSurface> m_surfaces = null;
- private HashMap<Integer, View> m_nativeViews = null;
- private QtLayout m_layout = null;
- private ImageView m_splashScreen = null;
- private boolean m_splashScreenSticky = false;
- private QtEditText m_editText = null;
- private InputMethodManager m_imm = null;
- private boolean m_quitApp = true;
- private View m_dummyView = null;
- private boolean m_keyboardIsVisible = false;
- public boolean m_backKeyPressedSent = false;
- private long m_showHideTimeStamp = System.nanoTime();
- private int m_portraitKeyboardHeight = 0;
- private int m_landscapeKeyboardHeight = 0;
- private int m_probeKeyboardHeightDelay = 50; // ms
- private CursorHandle m_cursorHandle;
- private CursorHandle m_leftSelectionHandle;
- private CursorHandle m_rightSelectionHandle;
- private EditPopupMenu m_editPopupMenu;
-
-
- public void setSystemUiVisibility(int systemUiVisibility)
- {
- if (m_systemUiVisibility == systemUiVisibility)
- return;
-
- m_systemUiVisibility = systemUiVisibility;
-
- int systemUiVisibilityFlags = 0;
- switch (m_systemUiVisibility) {
- case SYSTEM_UI_VISIBILITY_NORMAL:
- m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_VISIBLE;
- break;
- case SYSTEM_UI_VISIBILITY_FULLSCREEN:
- m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.INVISIBLE;
- break;
- case SYSTEM_UI_VISIBILITY_TRANSLUCENT:
- m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_VISIBLE;
- break;
- };
-
- m_activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags);
-
- m_layout.requestLayout();
- }
-
- public void updateFullScreen()
- {
- if (m_systemUiVisibility == SYSTEM_UI_VISIBILITY_FULLSCREEN) {
- m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
- setSystemUiVisibility(SYSTEM_UI_VISIBILITY_FULLSCREEN);
- }
- }
-
- // input method hints - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
- private final int ImhHiddenText = 0x1;
- private final int ImhSensitiveData = 0x2;
- private final int ImhNoAutoUppercase = 0x4;
- private final int ImhPreferNumbers = 0x8;
- private final int ImhPreferUppercase = 0x10;
- private final int ImhPreferLowercase = 0x20;
- private final int ImhNoPredictiveText = 0x40;
-
- private final int ImhDate = 0x80;
- private final int ImhTime = 0x100;
-
- private final int ImhPreferLatin = 0x200;
-
- private final int ImhMultiLine = 0x400;
-
- private final int ImhDigitsOnly = 0x10000;
- private final int ImhFormattedNumbersOnly = 0x20000;
- private final int ImhUppercaseOnly = 0x40000;
- private final int ImhLowercaseOnly = 0x80000;
- private final int ImhDialableCharactersOnly = 0x100000;
- private final int ImhEmailCharactersOnly = 0x200000;
- private final int ImhUrlCharactersOnly = 0x400000;
- private final int ImhLatinOnly = 0x800000;
-
- // enter key type - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
- private final int EnterKeyDefault = 0;
- private final int EnterKeyReturn = 1;
- private final int EnterKeyDone = 2;
- private final int EnterKeyGo = 3;
- private final int EnterKeySend = 4;
- private final int EnterKeySearch = 5;
- private final int EnterKeyNext = 6;
- private final int EnterKeyPrevious = 7;
-
- // application state
- public static final int ApplicationSuspended = 0x0;
- public static final int ApplicationHidden = 0x1;
- public static final int ApplicationInactive = 0x2;
- public static final int ApplicationActive = 0x4;
-
-
- public boolean setKeyboardVisibility(boolean visibility, long timeStamp)
- {
- if (m_showHideTimeStamp > timeStamp)
- return false;
- m_showHideTimeStamp = timeStamp;
-
- if (m_keyboardIsVisible == visibility)
- return false;
- m_keyboardIsVisible = visibility;
- QtNative.keyboardVisibilityChanged(m_keyboardIsVisible);
-
- if (visibility == false)
- updateFullScreen(); // Hiding the keyboard clears the immersive mode, so we need to set it again.
-
- return true;
- }
- public void resetSoftwareKeyboard()
- {
- if (m_imm == null)
- return;
- m_editText.postDelayed(new Runnable() {
- @Override
- public void run() {
- m_imm.restartInput(m_editText);
- m_editText.m_optionsChanged = false;
- }
- }, 5);
- }
-
- public void showSoftwareKeyboard(final int x, final int y, final int width, final int height, final int inputHints, final int enterKeyType)
- {
- if (m_imm == null)
- return;
-
- DisplayMetrics metrics = new DisplayMetrics();
- m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- // If the screen is in portrait mode than we estimate that keyboard height will not be higher than 2/5 of the screen.
- // else than we estimate that keyboard height will not be higher than 2/3 of the screen
- final int visibleHeight;
- if (metrics.widthPixels < metrics.heightPixels)
- visibleHeight = m_portraitKeyboardHeight != 0 ? m_portraitKeyboardHeight : metrics.heightPixels * 3 / 5;
- else
- visibleHeight = m_landscapeKeyboardHeight != 0 ? m_landscapeKeyboardHeight : metrics.heightPixels / 3;
-
- if (m_softInputMode != 0) {
- m_activity.getWindow().setSoftInputMode(m_softInputMode);
- final boolean softInputIsHidden = (m_softInputMode & WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) != 0;
- if (softInputIsHidden)
- return;
- } else {
- if (height > visibleHeight)
- m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- else
- m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- int initialCapsMode = 0;
-
- int imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
-
- switch (enterKeyType) {
- case EnterKeyReturn:
- imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
- break;
- case EnterKeyGo:
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
- break;
- case EnterKeySend:
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEND;
- break;
- case EnterKeySearch:
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH;
- break;
- case EnterKeyNext:
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
- break;
- case EnterKeyPrevious:
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS;
- break;
- }
-
- int inputType = android.text.InputType.TYPE_CLASS_TEXT;
-
- if ((inputHints & (ImhPreferNumbers | ImhDigitsOnly | ImhFormattedNumbersOnly)) != 0) {
- inputType = android.text.InputType.TYPE_CLASS_NUMBER;
- if ((inputHints & ImhFormattedNumbersOnly) != 0) {
- inputType |= (android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL
- | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
- }
-
- if ((inputHints & ImhHiddenText) != 0)
- inputType |= 0x10 /* TYPE_NUMBER_VARIATION_PASSWORD */;
- } else if ((inputHints & ImhDialableCharactersOnly) != 0) {
- inputType = android.text.InputType.TYPE_CLASS_PHONE;
- } else if ((inputHints & (ImhDate | ImhTime)) != 0) {
- inputType = android.text.InputType.TYPE_CLASS_DATETIME;
- if ((inputHints & (ImhDate | ImhTime)) != (ImhDate | ImhTime)) {
- if ((inputHints & ImhDate) != 0)
- inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_DATE;
- if ((inputHints & ImhTime) != 0)
- inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_TIME;
- } // else { TYPE_DATETIME_VARIATION_NORMAL(0) }
- } else { // CLASS_TEXT
- if ((inputHints & (ImhEmailCharactersOnly | ImhUrlCharactersOnly)) != 0) {
- if ((inputHints & ImhUrlCharactersOnly) != 0) {
- inputType |= android.text.InputType.TYPE_TEXT_VARIATION_URI;
-
- if (enterKeyType == 0) // not explicitly overridden
- imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
- } else if ((inputHints & ImhEmailCharactersOnly) != 0) {
- inputType |= android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
- }
- } else if ((inputHints & ImhHiddenText) != 0) {
- inputType |= android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
- } else if ((inputHints & ImhSensitiveData) != 0 ||
- ((inputHints & ImhNoPredictiveText) != 0 &&
- System.getenv("QT_ANDROID_ENABLE_WORKAROUND_TO_DISABLE_PREDICTIVE_TEXT") != null)) {
- inputType |= android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
- }
-
- if ((inputHints & ImhMultiLine) != 0)
- inputType |= android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE;
-
- if ((inputHints & ImhUppercaseOnly) != 0) {
- initialCapsMode |= android.text.TextUtils.CAP_MODE_CHARACTERS;
- inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
- } else if ((inputHints & ImhLowercaseOnly) == 0 && (inputHints & ImhNoAutoUppercase) == 0) {
- initialCapsMode |= android.text.TextUtils.CAP_MODE_SENTENCES;
- inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
- }
-
- if ((inputHints & ImhNoPredictiveText) != 0 || (inputHints & ImhSensitiveData) != 0
- || (inputHints & ImhHiddenText) != 0) {
- inputType |= android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
- }
- }
-
- if (enterKeyType == 0 && (inputHints & ImhMultiLine) != 0)
- imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
-
- m_editText.setInitialCapsMode(initialCapsMode);
- m_editText.setImeOptions(imeOptions);
- m_editText.setInputType(inputType);
-
- m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(width, height, x, y), false);
- m_editText.requestFocus();
- m_editText.postDelayed(new Runnable() {
- @Override
- public void run() {
- m_imm.showSoftInput(m_editText, 0, new ResultReceiver(new Handler()) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- switch (resultCode) {
- case InputMethodManager.RESULT_SHOWN:
- QtNativeInputConnection.updateCursorPosition();
- //FALLTHROUGH
- case InputMethodManager.RESULT_UNCHANGED_SHOWN:
- setKeyboardVisibility(true, System.nanoTime());
- if (m_softInputMode == 0) {
- // probe for real keyboard height
- m_layout.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (!m_keyboardIsVisible)
- return;
- DisplayMetrics metrics = new DisplayMetrics();
- m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- Rect r = new Rect();
- m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
- if (metrics.heightPixels != r.bottom) {
- if (metrics.widthPixels > metrics.heightPixels) { // landscape
- if (m_landscapeKeyboardHeight != r.bottom) {
- m_landscapeKeyboardHeight = r.bottom;
- showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
- }
- } else {
- if (m_portraitKeyboardHeight != r.bottom) {
- m_portraitKeyboardHeight = r.bottom;
- showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
- }
- }
- } else {
- // no luck ?
- // maybe the delay was too short, so let's make it longer
- if (m_probeKeyboardHeightDelay < 1000)
- m_probeKeyboardHeightDelay *= 2;
- }
- }
- }, m_probeKeyboardHeightDelay);
- }
- break;
- case InputMethodManager.RESULT_HIDDEN:
- case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
- setKeyboardVisibility(false, System.nanoTime());
- break;
- }
- }
- });
- if (m_editText.m_optionsChanged) {
- m_imm.restartInput(m_editText);
- m_editText.m_optionsChanged = false;
- }
- }
- }, 15);
- }
-
- public void hideSoftwareKeyboard()
- {
- if (m_imm == null)
- return;
- m_imm.hideSoftInputFromWindow(m_editText.getWindowToken(), 0, new ResultReceiver(new Handler()) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- switch (resultCode) {
- case InputMethodManager.RESULT_SHOWN:
- case InputMethodManager.RESULT_UNCHANGED_SHOWN:
- setKeyboardVisibility(true, System.nanoTime());
- break;
- case InputMethodManager.RESULT_HIDDEN:
- case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
- setKeyboardVisibility(false, System.nanoTime());
- break;
- }
- }
- });
- }
-
- String getAppIconSize(Activity a)
- {
- int size = a.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
- if (size < 36 || size > 512) { // check size sanity
- DisplayMetrics metrics = new DisplayMetrics();
- a.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- size = metrics.densityDpi / 10 * 3;
- if (size < 36)
- size = 36;
-
- if (size > 512)
- size = 512;
- }
- return "\tQT_ANDROID_APP_ICON_SIZE=" + size;
- }
-
- public void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
- {
- if (m_imm == null)
- return;
-
- m_imm.updateSelection(m_editText, selStart, selEnd, candidatesStart, candidatesEnd);
- }
-
- // Values coming from QAndroidInputContext::CursorHandleShowMode
- private static final int CursorHandleNotShown = 0;
- private static final int CursorHandleShowNormal = 1;
- private static final int CursorHandleShowSelection = 2;
- private static final int CursorHandleShowEdit = 0x100;
-
- /* called from the C++ code when the position of the cursor or selection handles needs to
- be adjusted.
- mode is one of QAndroidInputContext::CursorHandleShowMode
- */
- public void updateHandles(int mode, int editX, int editY, int editButtons, int x1, int y1, int x2, int y2, boolean rtl)
- {
- switch (mode & 0xff)
- {
- case CursorHandleNotShown:
- if (m_cursorHandle != null) {
- m_cursorHandle.hide();
- m_cursorHandle = null;
- }
- if (m_rightSelectionHandle != null) {
- m_rightSelectionHandle.hide();
- m_leftSelectionHandle.hide();
- m_rightSelectionHandle = null;
- m_leftSelectionHandle = null;
- }
- if (m_editPopupMenu != null)
- m_editPopupMenu.hide();
- break;
-
- case CursorHandleShowNormal:
- if (m_cursorHandle == null) {
- m_cursorHandle = new CursorHandle(m_activity, m_layout, QtNative.IdCursorHandle,
- android.R.attr.textSelectHandle, false);
- }
- m_cursorHandle.setPosition(x1, y1);
- if (m_rightSelectionHandle != null) {
- m_rightSelectionHandle.hide();
- m_leftSelectionHandle.hide();
- m_rightSelectionHandle = null;
- m_leftSelectionHandle = null;
- }
- break;
-
- case CursorHandleShowSelection:
- if (m_rightSelectionHandle == null) {
- m_leftSelectionHandle = new CursorHandle(m_activity, m_layout, QtNative.IdLeftHandle,
- !rtl ? android.R.attr.textSelectHandleLeft :
- android.R.attr.textSelectHandleRight,
- rtl);
- m_rightSelectionHandle = new CursorHandle(m_activity, m_layout, QtNative.IdRightHandle,
- !rtl ? android.R.attr.textSelectHandleRight :
- android.R.attr.textSelectHandleLeft,
- rtl);
- }
- m_leftSelectionHandle.setPosition(x1,y1);
- m_rightSelectionHandle.setPosition(x2,y2);
- if (m_cursorHandle != null) {
- m_cursorHandle.hide();
- m_cursorHandle = null;
- }
- mode |= CursorHandleShowEdit;
- break;
- }
-
- if (QtNative.hasClipboardText())
- editButtons |= EditContextView.PASTE_BUTTON;
- else
- editButtons &= ~EditContextView.PASTE_BUTTON;
-
- if ((mode & CursorHandleShowEdit) == CursorHandleShowEdit && editButtons != 0) {
- m_editPopupMenu.setPosition(editX, editY, editButtons, m_cursorHandle, m_leftSelectionHandle,
- m_rightSelectionHandle);
- } else {
- if (m_editPopupMenu != null)
- m_editPopupMenu.hide();
- }
- }
-
- public boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams)
- {
- /// check parameters integrity
- if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY)
- || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY)
- || !loaderParams.containsKey(ENVIRONMENT_VARIABLES_KEY)) {
- return false;
- }
-
- m_activity = activity;
- setActionBarVisibility(false);
- QtNative.setActivity(m_activity, this);
- QtNative.setClassLoader(classLoader);
- if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) {
- for (String className: loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY)) {
- if (className.length() == 0)
- continue;
-
- try {
- Class<?> initClass = classLoader.loadClass(className);
- Object staticInitDataObject = initClass.newInstance(); // create an instance
- try {
- Method m = initClass.getMethod("setActivity", Activity.class, Object.class);
- m.invoke(staticInitDataObject, m_activity, this);
- } catch (Exception e) {
- }
-
- try {
- Method m = initClass.getMethod("setContext", Context.class);
- m.invoke(staticInitDataObject, (Context)m_activity);
- } catch (Exception e) {
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY));
- ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY);
- String nativeLibsDir = QtNativeLibrariesDir.nativeLibrariesDir(m_activity);
- QtNative.loadBundledLibraries(libraries, nativeLibsDir);
- m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY);
- // older apps provide the main library as the last bundled library; look for this if the main library isn't provided
- if (null == m_mainLib && libraries.size() > 0) {
- m_mainLib = libraries.get(libraries.size() - 1);
- libraries.remove(libraries.size() - 1);
- }
-
- if (loaderParams.containsKey(EXTRACT_STYLE_KEY)) {
- String path = loaderParams.getString(EXTRACT_STYLE_KEY);
- new ExtractStyle(m_activity, path, loaderParams.containsKey(EXTRACT_STYLE_MINIMAL_KEY) &&
- loaderParams.getBoolean(EXTRACT_STYLE_MINIMAL_KEY));
- }
-
- try {
- m_super_dispatchKeyEvent = m_activity.getClass().getMethod("super_dispatchKeyEvent", KeyEvent.class);
- m_super_onRestoreInstanceState = m_activity.getClass().getMethod("super_onRestoreInstanceState", Bundle.class);
- m_super_onRetainNonConfigurationInstance = m_activity.getClass().getMethod("super_onRetainNonConfigurationInstance");
- m_super_onSaveInstanceState = m_activity.getClass().getMethod("super_onSaveInstanceState", Bundle.class);
- m_super_onKeyDown = m_activity.getClass().getMethod("super_onKeyDown", Integer.TYPE, KeyEvent.class);
- m_super_onKeyUp = m_activity.getClass().getMethod("super_onKeyUp", Integer.TYPE, KeyEvent.class);
- m_super_onConfigurationChanged = m_activity.getClass().getMethod("super_onConfigurationChanged", Configuration.class);
- m_super_onActivityResult = m_activity.getClass().getMethod("super_onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class);
- m_super_onWindowFocusChanged = m_activity.getClass().getMethod("super_onWindowFocusChanged", Boolean.TYPE);
- m_super_dispatchGenericMotionEvent = m_activity.getClass().getMethod("super_dispatchGenericMotionEvent", MotionEvent.class);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
-
- int necessitasApiLevel = 1;
- if (loaderParams.containsKey(NECESSITAS_API_LEVEL_KEY))
- necessitasApiLevel = loaderParams.getInt(NECESSITAS_API_LEVEL_KEY);
-
- m_environmentVariables = loaderParams.getString(ENVIRONMENT_VARIABLES_KEY);
- String additionalEnvironmentVariables = "QT_ANDROID_FONTS_MONOSPACE=Droid Sans Mono;Droid Sans;Droid Sans Fallback"
- + "\tQT_ANDROID_FONTS_SERIF=Droid Serif"
- + "\tNECESSITAS_API_LEVEL=" + necessitasApiLevel
- + "\tHOME=" + m_activity.getFilesDir().getAbsolutePath()
- + "\tTMPDIR=" + m_activity.getFilesDir().getAbsolutePath();
-
- additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback";
-
- additionalEnvironmentVariables += getAppIconSize(activity);
-
- if (m_environmentVariables != null && m_environmentVariables.length() > 0)
- m_environmentVariables = additionalEnvironmentVariables + "\t" + m_environmentVariables;
- else
- m_environmentVariables = additionalEnvironmentVariables;
-
- if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY))
- m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY);
- else
- m_applicationParameters = "";
-
- try {
- m_softInputMode = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), 0).softInputMode;
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) { }
-
- @Override
- public void onDisplayChanged(int displayId) {
- m_currentRotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
- QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation);
- }
-
- @Override
- public void onDisplayRemoved(int displayId) { }
- };
-
- try {
- DisplayManager displayManager = (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE);
- displayManager.registerDisplayListener(displayListener, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
- return m_mainLib != null;
- }
-
- public boolean startApplication()
- {
- // start application
- try {
-
- Bundle extras = m_activity.getIntent().getExtras();
- if (extras != null) {
- try {
- // do NOT remove !!!!
- final String dc = "--Added-by-androiddeployqt--/debugger.command";
- new BufferedReader(new InputStreamReader(m_activity.getAssets().open(dc))).readLine();
- // do NOT remove !!!!
- // The previous lines are needed to check if the debug mode is enabled.
- // We are not allowed to use extraenvvars or extraappparams in a non debuggable environment.
-
- if (extras.containsKey("extraenvvars")) {
- try {
- m_environmentVariables += "\t" + new String(Base64.decode(extras.getString("extraenvvars"), Base64.DEFAULT), "UTF-8");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if (extras.containsKey("extraappparams")) {
- try {
- m_applicationParameters += "\t" + new String(Base64.decode(extras.getString("extraappparams"), Base64.DEFAULT), "UTF-8");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } catch (Exception e) {
- // This is not an error, so keep it silent
- // e.printStackTrace();
- }
- } // extras != null
-
- if (null == m_surfaces)
- onCreate(null);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public void onTerminate()
- {
- QtNative.terminateQt();
- QtNative.m_qtThread.exit();
- }
-
- public void onCreate(Bundle savedInstanceState)
- {
- m_quitApp = true;
- Runnable startApplication = null;
- if (null == savedInstanceState) {
- startApplication = new Runnable() {
- @Override
- public void run() {
- try {
- QtNative.startApplication(m_applicationParameters, m_environmentVariables, m_mainLib);
- m_started = true;
- } catch (Exception e) {
- e.printStackTrace();
- m_activity.finish();
- }
- }
- };
- }
- m_layout = new QtLayout(m_activity, startApplication);
-
- int orientation = m_activity.getResources().getConfiguration().orientation;
-
- try {
- ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
-
- String splashScreenKey = "android.app.splash_screen_drawable_"
- + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
- if (!info.metaData.containsKey(splashScreenKey))
- splashScreenKey = "android.app.splash_screen_drawable";
-
- if (info.metaData.containsKey(splashScreenKey)) {
- m_splashScreenSticky = info.metaData.containsKey("android.app.splash_screen_sticky") && info.metaData.getBoolean("android.app.splash_screen_sticky");
- int id = info.metaData.getInt(splashScreenKey);
- m_splashScreen = new ImageView(m_activity);
- m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(id));
- m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
- m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- m_layout.addView(m_splashScreen);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- m_editText = new QtEditText(m_activity, this);
- m_imm = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE);
- m_surfaces = new HashMap<Integer, QtSurface>();
- m_nativeViews = new HashMap<Integer, View>();
- m_activity.registerForContextMenu(m_layout);
- m_activity.setContentView(m_layout,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
- boolean rot90 = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
- boolean currentlyLandscape = (orientation == Configuration.ORIENTATION_LANDSCAPE);
- if ((currentlyLandscape && !rot90) || (!currentlyLandscape && rot90))
- m_nativeOrientation = Configuration.ORIENTATION_LANDSCAPE;
- else
- m_nativeOrientation = Configuration.ORIENTATION_PORTRAIT;
-
- QtNative.handleOrientationChanged(rotation, m_nativeOrientation);
- m_currentRotation = rotation;
-
- m_layout.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- if (!m_keyboardIsVisible)
- return true;
-
- Rect r = new Rect();
- m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
- DisplayMetrics metrics = new DisplayMetrics();
- m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- final int kbHeight = metrics.heightPixels - r.bottom;
- final int[] location = new int[2];
- m_layout.getLocationOnScreen(location);
- QtNative.keyboardGeometryChanged(location[0], r.bottom - location[1],
- r.width(), kbHeight);
- return true;
- }
- });
- m_editPopupMenu = new EditPopupMenu(m_activity, m_layout);
- }
-
- public void hideSplashScreen()
- {
- hideSplashScreen(0);
- }
-
- public void hideSplashScreen(final int duration)
- {
- if (m_splashScreen == null)
- return;
-
- if (duration <= 0) {
- m_layout.removeView(m_splashScreen);
- m_splashScreen = null;
- return;
- }
-
- final Animation fadeOut = new AlphaAnimation(1, 0);
- fadeOut.setInterpolator(new AccelerateInterpolator());
- fadeOut.setDuration(duration);
-
- fadeOut.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) { hideSplashScreen(0); }
-
- @Override
- public void onAnimationRepeat(Animation animation) {}
-
- @Override
- public void onAnimationStart(Animation animation) {}
- });
-
- m_splashScreen.startAnimation(fadeOut);
- }
-
-
- public void initializeAccessibility()
- {
- new QtAccessibilityDelegate(m_activity, m_layout, this);
- }
-
- public void onWindowFocusChanged(boolean hasFocus) {
- try {
- m_super_onWindowFocusChanged.invoke(m_activity, hasFocus);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (hasFocus)
- updateFullScreen();
- }
-
- public void onConfigurationChanged(Configuration configuration)
- {
- try {
- m_super_onConfigurationChanged.invoke(m_activity, configuration);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void onDestroy()
- {
- if (m_quitApp) {
- QtNative.terminateQt();
- QtNative.setActivity(null, null);
- QtNative.m_qtThread.exit();
- System.exit(0);
- }
- }
-
- public void onPause()
- {
- if (Build.VERSION.SDK_INT < 24 || !m_activity.isInMultiWindowMode())
- QtNative.setApplicationState(ApplicationInactive);
- }
-
- public void onResume()
- {
- QtNative.setApplicationState(ApplicationActive);
- if (m_started) {
- QtNative.updateWindow();
- updateFullScreen(); // Suspending the app clears the immersive mode, so we need to set it again.
- }
- }
-
- public void onNewIntent(Intent data)
- {
- QtNative.onNewIntent(data);
- }
-
- public void onActivityResult(int requestCode, int resultCode, Intent data)
- {
- try {
- m_super_onActivityResult.invoke(m_activity, requestCode, resultCode, data);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- QtNative.onActivityResult(requestCode, resultCode, data);
- }
-
-
- public void onStop()
- {
- QtNative.setApplicationState(ApplicationSuspended);
- }
-
- public Object onRetainNonConfigurationInstance()
- {
- try {
- m_super_onRetainNonConfigurationInstance.invoke(m_activity);
- } catch (Exception e) {
- e.printStackTrace();
- }
- m_quitApp = false;
- return true;
- }
-
- public void onSaveInstanceState(Bundle outState) {
- try {
- m_super_onSaveInstanceState.invoke(m_activity, outState);
- } catch (Exception e) {
- e.printStackTrace();
- }
- outState.putInt("SystemUiVisibility", m_systemUiVisibility);
- outState.putBoolean("Started", m_started);
- // It should never
- }
-
- public void onRestoreInstanceState(Bundle savedInstanceState)
- {
- try {
- m_super_onRestoreInstanceState.invoke(m_activity, savedInstanceState);
- } catch (Exception e) {
- e.printStackTrace();
- }
- m_started = savedInstanceState.getBoolean("Started");
- // FIXME restore all surfaces
-
- }
-
- public boolean onKeyDown(int keyCode, KeyEvent event)
- {
- if (!m_started)
- return false;
-
- m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event);
- int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState) | event.getMetaState());
- int lc = c;
- m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState);
-
- if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
- c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
- int composed = KeyEvent.getDeadChar(m_lastChar, c);
- c = composed;
- }
-
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
- || keyCode == KeyEvent.KEYCODE_MUTE)
- && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
- return false;
- }
-
- m_lastChar = lc;
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- m_backKeyPressedSent = !m_keyboardIsVisible;
- if (!m_backKeyPressedSent)
- return true;
- }
- QtNative.keyDown(keyCode, c, event.getMetaState(), event.getRepeatCount() > 0);
-
- return true;
- }
-
- public boolean onKeyUp(int keyCode, KeyEvent event)
- {
- if (!m_started)
- return false;
-
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
- || keyCode == KeyEvent.KEYCODE_MUTE)
- && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
- return false;
- }
-
- if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) {
- hideSoftwareKeyboard();
- setKeyboardVisibility(false, System.nanoTime());
- return true;
- }
-
- m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event);
- QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState(), event.getRepeatCount() > 0);
- return true;
- }
-
- public boolean dispatchKeyEvent(KeyEvent event)
- {
- if (m_started
- && event.getAction() == KeyEvent.ACTION_MULTIPLE
- && event.getCharacters() != null
- && event.getCharacters().length() == 1
- && event.getKeyCode() == 0) {
- QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0);
- QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0);
- }
-
- if (QtNative.dispatchKeyEvent(event))
- return true;
-
- try {
- return (Boolean) m_super_dispatchKeyEvent.invoke(m_activity, event);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return false;
- }
-
- private boolean m_optionsMenuIsVisible = false;
- public boolean onCreateOptionsMenu(Menu menu)
- {
- menu.clear();
- return true;
- }
- public boolean onPrepareOptionsMenu(Menu menu)
- {
- m_optionsMenuIsVisible = true;
- boolean res = QtNative.onPrepareOptionsMenu(menu);
- setActionBarVisibility(res && menu.size() > 0);
- return res;
- }
-
- public boolean onOptionsItemSelected(MenuItem item)
- {
- return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked());
- }
-
- public void onOptionsMenuClosed(Menu menu)
- {
- m_optionsMenuIsVisible = false;
- QtNative.onOptionsMenuClosed(menu);
- }
-
- public void resetOptionsMenu()
- {
- m_activity.invalidateOptionsMenu();
- }
-
- private boolean m_contextMenuVisible = false;
- public void onCreateContextMenu(ContextMenu menu,
- View v,
- ContextMenuInfo menuInfo)
- {
- menu.clearHeader();
- QtNative.onCreateContextMenu(menu);
- m_contextMenuVisible = true;
- }
-
- public void onCreatePopupMenu(Menu menu)
- {
- QtNative.fillContextMenu(menu);
- m_contextMenuVisible = true;
- }
-
- public void onContextMenuClosed(Menu menu)
- {
- if (!m_contextMenuVisible)
- return;
- m_contextMenuVisible = false;
- QtNative.onContextMenuClosed(menu);
- }
-
- public boolean onContextItemSelected(MenuItem item)
- {
- m_contextMenuVisible = false;
- return QtNative.onContextItemSelected(item.getItemId(), item.isChecked());
- }
-
- public void openContextMenu(final int x, final int y, final int w, final int h)
- {
- m_layout.postDelayed(new Runnable() {
- @Override
- public void run() {
- m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(w, h, x, y), false);
- PopupMenu popup = new PopupMenu(m_activity, m_editText);
- QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
- popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- return QtActivityDelegate.this.onContextItemSelected(menuItem);
- }
- });
- popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
- @Override
- public void onDismiss(PopupMenu popupMenu) {
- QtActivityDelegate.this.onContextMenuClosed(popupMenu.getMenu());
- }
- });
- popup.show();
- }
- }, 100);
- }
-
- public void closeContextMenu()
- {
- m_activity.closeContextMenu();
- }
-
- private void setActionBarVisibility(boolean visible)
- {
- if (m_activity.getActionBar() == null)
- return;
- if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible)
- m_activity.getActionBar().hide();
- else
- m_activity.getActionBar().show();
- }
-
- public void insertNativeView(int id, View view, int x, int y, int w, int h) {
- if (m_dummyView != null) {
- m_layout.removeView(m_dummyView);
- m_dummyView = null;
- }
-
- if (m_nativeViews.containsKey(id))
- m_layout.removeView(m_nativeViews.remove(id));
-
- if (w < 0 || h < 0) {
- view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- } else {
- view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
- }
-
- view.setId(id);
- m_layout.addView(view);
- m_nativeViews.put(id, view);
- }
-
- public void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth) {
- if (m_surfaces.size() == 0) {
- TypedValue attr = new TypedValue();
- m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground, attr, true);
- if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT && attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(attr.data));
- } else {
- m_activity.getWindow().setBackgroundDrawable(m_activity.getResources().getDrawable(attr.resourceId));
- }
- if (m_dummyView != null) {
- m_layout.removeView(m_dummyView);
- m_dummyView = null;
- }
- }
-
- if (m_surfaces.containsKey(id))
- m_layout.removeView(m_surfaces.remove(id));
-
- QtSurface surface = new QtSurface(m_activity, id, onTop, imageDepth);
- if (w < 0 || h < 0) {
- surface.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- } else {
- surface.setLayoutParams( new QtLayout.LayoutParams(w, h, x, y));
- }
-
- // Native views are always inserted in the end of the stack (i.e., on top).
- // All other views are stacked based on the order they are created.
- final int surfaceCount = getSurfaceCount();
- m_layout.addView(surface, surfaceCount);
-
- m_surfaces.put(id, surface);
- if (!m_splashScreenSticky)
- hideSplashScreen();
- }
-
- public void setSurfaceGeometry(int id, int x, int y, int w, int h) {
- if (m_surfaces.containsKey(id)) {
- QtSurface surface = m_surfaces.get(id);
- surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
- } else if (m_nativeViews.containsKey(id)) {
- View view = m_nativeViews.get(id);
- view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
- } else {
- Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
- return;
- }
- }
-
- public void destroySurface(int id) {
- View view = null;
-
- if (m_surfaces.containsKey(id)) {
- view = m_surfaces.remove(id);
- } else if (m_nativeViews.containsKey(id)) {
- view = m_nativeViews.remove(id);
- } else {
- Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
- }
-
- if (view == null)
- return;
-
- // Keep last frame in stack until it is replaced to get correct
- // shutdown transition
- if (m_surfaces.size() == 0 && m_nativeViews.size() == 0) {
- m_dummyView = view;
- } else {
- m_layout.removeView(view);
- }
- }
-
- public int getSurfaceCount()
- {
- return m_surfaces.size();
- }
-
- public void bringChildToFront(int id)
- {
- View view = m_surfaces.get(id);
- if (view != null) {
- final int surfaceCount = getSurfaceCount();
- if (surfaceCount > 0)
- m_layout.moveChild(view, surfaceCount - 1);
- return;
- }
-
- view = m_nativeViews.get(id);
- if (view != null)
- m_layout.moveChild(view, -1);
- }
-
- public void bringChildToBack(int id)
- {
- View view = m_surfaces.get(id);
- if (view != null) {
- m_layout.moveChild(view, 0);
- return;
- }
-
- view = m_nativeViews.get(id);
- if (view != null) {
- final int index = getSurfaceCount();
- m_layout.moveChild(view, index);
- }
- }
-
- public boolean dispatchGenericMotionEvent (MotionEvent ev)
- {
- if (m_started && QtNative.dispatchGenericMotionEvent(ev))
- return true;
-
- try {
- return (Boolean) m_super_dispatchGenericMotionEvent.invoke(m_activity, ev);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return false;
- }
-
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
- {
- QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java
deleted file mode 100644
index e6da5482ca..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.content.Context;
-import android.text.InputType;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-public class QtEditText extends View
-{
- int m_initialCapsMode = 0;
- int m_imeOptions = 0;
- int m_inputType = InputType.TYPE_CLASS_TEXT;
- boolean m_optionsChanged = false;
- QtActivityDelegate m_activityDelegate;
-
- public void setImeOptions(int m_imeOptions)
- {
- if (m_imeOptions == this.m_imeOptions)
- return;
- this.m_imeOptions = m_imeOptions;
- m_optionsChanged = true;
- }
-
- public void setInitialCapsMode(int m_initialCapsMode)
- {
- if (m_initialCapsMode == this.m_initialCapsMode)
- return;
- this.m_initialCapsMode = m_initialCapsMode;
- m_optionsChanged = true;
- }
-
-
- public void setInputType(int m_inputType)
- {
- if (m_inputType == this.m_inputType)
- return;
- this.m_inputType = m_inputType;
- m_optionsChanged = true;
- }
-
- public QtEditText(Context context, QtActivityDelegate activityDelegate)
- {
- super(context);
- setFocusable(true);
- setFocusableInTouchMode(true);
- m_activityDelegate = activityDelegate;
- }
- public QtActivityDelegate getActivityDelegate()
- {
- return m_activityDelegate;
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs)
- {
- outAttrs.inputType = m_inputType;
- outAttrs.imeOptions = m_imeOptions;
- outAttrs.initialCapsMode = m_initialCapsMode;
- outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
- return new QtInputConnection(this);
- }
-
-// // DEBUG CODE
-// @Override
-// protected void onDraw(Canvas canvas) {
-// canvas.drawARGB(127, 255, 0, 255);
-// super.onDraw(canvas);
-// }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java b/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java
deleted file mode 100644
index a3bbff4e1a..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
- ** Contact: https://www.qt.io/licensing/
- **
- ** This file is part of the Android port of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:LGPL$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and The Qt Company. For licensing terms
- ** and conditions see https://www.qt.io/terms-conditions. For further
- ** information use the contact form at https://www.qt.io/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 3 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.LGPL3 included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU Lesser General Public License version 3 requirements
- ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
- **
- ** GNU General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU
- ** General Public License version 2.0 or (at your option) the GNU General
- ** Public license version 3 or any later version approved by the KDE Free
- ** Qt Foundation. The licenses are as published by the Free Software
- ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
- ** included in the packaging of this file. Please review the following
- ** information to ensure the GNU General Public License requirements will
- ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
- ** https://www.gnu.org/licenses/gpl-3.0.html.
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
-
-
-package org.qtproject.qt5.android;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.text.ClipboardManager;
-import android.text.Html;
-import android.text.Spanned;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-
-class QtNativeDialogHelper
-{
- static native void dialogResult(long handler, int buttonID);
-}
-
-class ButtonStruct implements View.OnClickListener
-{
- ButtonStruct(QtMessageDialogHelper dialog, int id, String text)
- {
- m_dialog = dialog;
- m_id = id;
- m_text = Html.fromHtml(text);
- }
- QtMessageDialogHelper m_dialog;
- private int m_id;
- Spanned m_text;
-
- @Override
- public void onClick(View view) {
- QtNativeDialogHelper.dialogResult(m_dialog.handler(), m_id);
- }
-}
-
-public class QtMessageDialogHelper
-{
-
- public QtMessageDialogHelper(Activity activity)
- {
- m_activity = activity;
- }
-
-
- public void setIcon(int icon)
- {
- m_icon = icon;
-
- }
-
- private Drawable getIconDrawable()
- {
- if (m_icon == 0)
- return null;
-
- try {
- TypedValue typedValue = new TypedValue();
- m_theme.resolveAttribute(android.R.attr.alertDialogIcon, typedValue, true);
- return m_activity.getResources().getDrawable(typedValue.resourceId);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- // Information, Warning, Critical, Question
- switch (m_icon)
- {
- case 1: // Information
- try {
- return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_info);
- } catch (Exception e) {
- e.printStackTrace();
- }
- break;
- case 2: // Warning
-// try {
-// return Class.forName("android.R$drawable").getDeclaredField("stat_sys_warning").getInt(null);
-// } catch (Exception e) {
-// e.printStackTrace();
-// }
-// break;
- case 3: // Critical
- try {
- return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_alert);
- } catch (Exception e) {
- e.printStackTrace();
- }
- break;
- case 4: // Question
- try {
- return m_activity.getResources().getDrawable(android.R.drawable.ic_menu_help);
- } catch (Exception e) {
- e.printStackTrace();
- }
- break;
- }
- return null;
- }
-
- public void setTile(String title)
- {
- m_title = Html.fromHtml(title);
- }
-
- public void setText(String text)
- {
- m_text = Html.fromHtml(text);
- }
-
- public void setInformativeText(String informativeText)
- {
- m_informativeText = Html.fromHtml(informativeText);
- }
-
- public void setDetailedText(String text)
- {
- m_detailedText = Html.fromHtml(text);
- }
-
- public void addButton(int id, String text)
- {
- if (m_buttonsList == null)
- m_buttonsList = new ArrayList<ButtonStruct>();
- m_buttonsList.add(new ButtonStruct(this, id, text));
- }
-
- private Drawable getStyledDrawable(String drawable) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException
- {
- int[] attrs = {Class.forName("android.R$attr").getDeclaredField(drawable).getInt(null)};
- final TypedArray a = m_theme.obtainStyledAttributes(attrs);
- Drawable d = a.getDrawable(0);
- a.recycle();
- return d;
- }
-
-
- public void show(long handler)
- {
- m_handler = handler;
- m_activity.runOnUiThread( new Runnable() {
- @Override
- public void run() {
- if (m_dialog != null && m_dialog.isShowing())
- m_dialog.dismiss();
-
- m_dialog = new AlertDialog.Builder(m_activity).create();
- m_theme = m_dialog.getWindow().getContext().getTheme();
-
- if (m_title != null)
- m_dialog.setTitle(m_title);
- m_dialog.setOnCancelListener( new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- QtNativeDialogHelper.dialogResult(handler(), -1);
- }
- });
- m_dialog.setCancelable(m_buttonsList == null);
- m_dialog.setCanceledOnTouchOutside(m_buttonsList == null);
- m_dialog.setIcon(getIconDrawable());
- ScrollView scrollView = new ScrollView(m_activity);
- RelativeLayout dialogLayout = new RelativeLayout(m_activity);
- int id = 1;
- View lastView = null;
- View.OnLongClickListener copyText = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- TextView tv = (TextView)view;
- if (tv != null) {
- ClipboardManager cm = (android.text.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setText(tv.getText());
- }
- return true;
- }
- };
- if (m_text != null)
- {
- TextView view = new TextView(m_activity);
- view.setId(id++);
- view.setOnLongClickListener(copyText);
- view.setLongClickable(true);
-
- view.setText(m_text);
- view.setTextAppearance(m_activity, android.R.style.TextAppearance_Medium);
-
- RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- layout.setMargins(16, 8, 16, 8);
- layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- dialogLayout.addView(view, layout);
- lastView = view;
- }
-
- if (m_informativeText != null)
- {
- TextView view= new TextView(m_activity);
- view.setId(id++);
- view.setOnLongClickListener(copyText);
- view.setLongClickable(true);
-
- view.setText(m_informativeText);
- view.setTextAppearance(m_activity, android.R.style.TextAppearance_Medium);
-
- RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- layout.setMargins(16, 8, 16, 8);
- if (lastView != null)
- layout.addRule(RelativeLayout.BELOW, lastView.getId());
- else
- layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- dialogLayout.addView(view, layout);
- lastView = view;
- }
-
- if (m_detailedText != null)
- {
- TextView view= new TextView(m_activity);
- view.setId(id++);
- view.setOnLongClickListener(copyText);
- view.setLongClickable(true);
-
- view.setText(m_detailedText);
- view.setTextAppearance(m_activity, android.R.style.TextAppearance_Small);
-
- RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- layout.setMargins(16, 8, 16, 8);
- if (lastView != null)
- layout.addRule(RelativeLayout.BELOW, lastView.getId());
- else
- layout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- dialogLayout.addView(view, layout);
- lastView = view;
- }
-
- if (m_buttonsList != null)
- {
- LinearLayout buttonsLayout = new LinearLayout(m_activity);
- buttonsLayout.setOrientation(LinearLayout.HORIZONTAL);
- buttonsLayout.setId(id++);
- boolean firstButton = true;
- for (ButtonStruct button: m_buttonsList)
- {
- Button bv;
- try {
- bv = new Button(m_activity, null, Class.forName("android.R$attr").getDeclaredField("borderlessButtonStyle").getInt(null));
- } catch (Exception e) {
- bv = new Button(m_activity);
- e.printStackTrace();
- }
-
- bv.setText(button.m_text);
- bv.setOnClickListener(button);
- if (!firstButton) // first button
- {
- LinearLayout.LayoutParams layout = null;
- View spacer = new View(m_activity);
- try {
- layout = new LinearLayout.LayoutParams(1, RelativeLayout.LayoutParams.MATCH_PARENT);
- spacer.setBackgroundDrawable(getStyledDrawable("dividerVertical"));
- buttonsLayout.addView(spacer, layout);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- LinearLayout.LayoutParams layout = null;
- layout = new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT, 1.0f);
- buttonsLayout.addView(bv, layout);
- firstButton = false;
- }
-
- try {
- View horizontalDevider = new View(m_activity);
- horizontalDevider.setId(id++);
- horizontalDevider.setBackgroundDrawable(getStyledDrawable("dividerHorizontal"));
- RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 1);
- relativeParams.setMargins(0, 10, 0, 0);
- if (lastView != null) {
- relativeParams.addRule(RelativeLayout.BELOW, lastView.getId());
- }
- else
- relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- dialogLayout.addView(horizontalDevider, relativeParams);
- lastView = horizontalDevider;
- } catch (Exception e) {
- e.printStackTrace();
- }
- RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- if (lastView != null) {
- relativeParams.addRule(RelativeLayout.BELOW, lastView.getId());
- }
- else
- relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- relativeParams.setMargins(2, 0, 2, 0);
- dialogLayout.addView(buttonsLayout, relativeParams);
- }
- scrollView.addView(dialogLayout);
- m_dialog.setView(scrollView);
- m_dialog.show();
- }
- });
- }
-
- public void hide()
- {
- m_activity.runOnUiThread( new Runnable() {
- @Override
- public void run() {
- if (m_dialog != null && m_dialog.isShowing())
- m_dialog.dismiss();
- reset();
- }
- });
- }
-
- public long handler()
- {
- return m_handler;
- }
-
- public void reset()
- {
- m_icon = 0;
- m_title = null;
- m_text = null;
- m_informativeText = null;
- m_detailedText = null;
- m_buttonsList = null;
- m_dialog = null;
- m_handler = 0;
- }
-
- private Activity m_activity;
- private int m_icon = 0;
- private Spanned m_title, m_text, m_informativeText, m_detailedText;
- private ArrayList<ButtonStruct> m_buttonsList;
- private AlertDialog m_dialog;
- private long m_handler = 0;
- private Resources.Theme m_theme;
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
deleted file mode 100644
index 066352cedd..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
+++ /dev/null
@@ -1,1276 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-import java.io.IOException;
-
-import android.app.Activity;
-import android.app.Service;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ActivityInfo;
-import android.content.UriPermission;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.content.ClipboardManager;
-import android.content.ClipboardManager.OnPrimaryClipChangedListener;
-import android.content.ClipData;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.InputDevice;
-import android.database.Cursor;
-
-import java.lang.reflect.Method;
-import java.security.KeyStore;
-import java.security.cert.X509Certificate;
-import java.util.Iterator;
-import java.util.List;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-
-public class QtNative
-{
- private static Activity m_activity = null;
- private static boolean m_activityPaused = false;
- private static Service m_service = null;
- private static QtActivityDelegate m_activityDelegate = null;
- private static QtServiceDelegate m_serviceDelegate = null;
- public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
-
- public static final String QtTAG = "Qt JAVA"; // string used for Log.x
- private static ArrayList<Runnable> m_lostActions = new ArrayList<Runnable>(); // a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
- private static boolean m_started = false;
- private static int m_displayMetricsScreenWidthPixels = 0;
- private static int m_displayMetricsScreenHeightPixels = 0;
- private static int m_displayMetricsAvailableLeftPixels = 0;
- private static int m_displayMetricsAvailableTopPixels = 0;
- private static int m_displayMetricsAvailableWidthPixels = 0;
- private static int m_displayMetricsAvailableHeightPixels = 0;
- private static double m_displayMetricsXDpi = .0;
- private static double m_displayMetricsYDpi = .0;
- private static double m_displayMetricsScaledDensity = 1.0;
- private static double m_displayMetricsDensity = 1.0;
- private static int m_oldx, m_oldy;
- private static final int m_moveThreshold = 0;
- private static ClipboardManager m_clipboardManager = null;
- private static Method m_checkSelfPermissionMethod = null;
- private static Boolean m_tabletEventSupported = null;
- private static boolean m_usePrimaryClip = false;
- public static QtThread m_qtThread = new QtThread();
- private static Method m_addItemMethod = null;
- private static final Runnable runPendingCppRunnablesRunnable = new Runnable() {
- @Override
- public void run() {
- runPendingCppRunnables();
- }
- };
-
- private static ClassLoader m_classLoader = null;
- public static ClassLoader classLoader()
- {
- return m_classLoader;
- }
-
- public static void setClassLoader(ClassLoader classLoader)
- {
- m_classLoader = classLoader;
- }
-
- public static Activity activity()
- {
- synchronized (m_mainActivityMutex) {
- return m_activity;
- }
- }
-
- public static Service service()
- {
- synchronized (m_mainActivityMutex) {
- return m_service;
- }
- }
-
-
- public static QtActivityDelegate activityDelegate()
- {
- synchronized (m_mainActivityMutex) {
- return m_activityDelegate;
- }
- }
-
- public static QtServiceDelegate serviceDelegate()
- {
- synchronized (m_mainActivityMutex) {
- return m_serviceDelegate;
- }
- }
-
- public static String[] getStringArray(String joinedString)
- {
- return joinedString.split(",");
- }
-
- private static Uri getUriWithValidPermission(Context context, String uri, String openMode)
- {
- try {
- Uri parsedUri = Uri.parse(uri);
- String scheme = parsedUri.getScheme();
-
- // We only want to check permissions for content Uris
- if (scheme.compareTo("content") != 0)
- return parsedUri;
-
- List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions();
- String uriStr = parsedUri.getPath();
-
- for (int i = 0; i < permissions.size(); ++i) {
- Uri iterUri = permissions.get(i).getUri();
- boolean isRightPermission = permissions.get(i).isReadPermission();
-
- if (!openMode.equals("r"))
- isRightPermission = permissions.get(i).isWritePermission();
-
- if (iterUri.getPath().equals(uriStr) && isRightPermission)
- return iterUri;
- }
-
- return null;
- } catch (SecurityException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public static boolean openURL(Context context, String url, String mime)
- {
- Uri uri = getUriWithValidPermission(context, url, "r");
-
- if (uri == null) {
- Log.e(QtTAG, "openURL(): No permissions to open Uri");
- return false;
- }
-
- try {
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (!mime.isEmpty())
- intent.setDataAndType(uri, mime);
-
- activity().startActivity(intent);
-
- return true;
- } catch (IllegalArgumentException e) {
- Log.e(QtTAG, "openURL(): Invalid Uri");
- return false;
- } catch (UnsupportedOperationException e) {
- Log.e(QtTAG, "openURL(): Unsupported operation for given Uri");
- return false;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public static int openFdForContentUrl(Context context, String contentUrl, String openMode)
- {
- Uri uri = getUriWithValidPermission(context, contentUrl, openMode);
- int error = -1;
-
- if (uri == null) {
- Log.e(QtTAG, "openFdForContentUrl(): No permissions to open Uri");
- return error;
- }
-
- try {
- ContentResolver resolver = context.getContentResolver();
- ParcelFileDescriptor fdDesc = resolver.openFileDescriptor(uri, openMode);
- return fdDesc.detachFd();
- } catch (FileNotFoundException e) {
- return error;
- } catch (IllegalArgumentException e) {
- Log.e(QtTAG, "openFdForContentUrl(): Invalid Uri");
- return error;
- }
- }
-
- public static long getSize(Context context, String contentUrl)
- {
- Uri uri = getUriWithValidPermission(context, contentUrl, "r");
- long size = -1;
-
- if (uri == null) {
- Log.e(QtTAG, "getSize(): No permissions to open Uri");
- return size;
- }
-
- try {
- ContentResolver resolver = context.getContentResolver();
- Cursor cur = resolver.query(uri, null, null, null, null);
- if (cur != null) {
- if (cur.moveToFirst())
- size = cur.getLong(5); // size column
- cur.close();
- }
- return size;
- } catch (IllegalArgumentException e) {
- Log.e(QtTAG, "getSize(): Invalid Uri");
- return size;
- } catch (UnsupportedOperationException e) {
- Log.e(QtTAG, "getSize(): Unsupported operation for given Uri");
- return size;
- }
- }
-
- public static boolean checkFileExists(Context context, String contentUrl)
- {
- Uri uri = getUriWithValidPermission(context, contentUrl, "r");
- boolean exists = false;
-
- if (uri == null) {
- Log.e(QtTAG, "checkFileExists(): No permissions to open Uri");
- return exists;
- }
-
- try {
- ContentResolver resolver = context.getContentResolver();
- Cursor cur = resolver.query(uri, null, null, null, null);
- if (cur != null) {
- exists = true;
- cur.close();
- }
- return exists;
- } catch (IllegalArgumentException e) {
- Log.e(QtTAG, "checkFileExists(): Invalid Uri");
- return exists;
- } catch (UnsupportedOperationException e) {
- Log.e(QtTAG, "checkFileExists(): Unsupported operation for given Uri");
- return false;
- }
- }
-
- // this method loads full path libs
- public static void loadQtLibraries(final ArrayList<String> libraries)
- {
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- if (libraries == null)
- return;
- for (String libName : libraries) {
- try {
- File f = new File(libName);
- if (f.exists())
- System.load(libName);
- else
- Log.i(QtTAG, "Can't find '" + libName + "'");
- } catch (SecurityException e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- }
- }
- }
- });
- }
-
- // this method loads bundled libs by name.
- public static void loadBundledLibraries(final ArrayList<String> libraries, final String nativeLibraryDir)
- {
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- if (libraries == null)
- return;
-
- for (String libName : libraries) {
- try {
- String libNameTemplate = "lib" + libName + ".so";
- File f = new File(nativeLibraryDir + libNameTemplate);
- if (!f.exists()) {
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
- try {
- ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
- PackageManager.GET_META_DATA);
- String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
- if (info.metaData.containsKey("android.app.system_libs_prefix"))
- systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
- f = new File(systemLibraryDir + libNameTemplate);
- } catch (Exception e) {
-
- }
- }
- if (f.exists())
- System.load(f.getAbsolutePath());
- else
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- }
- }
- }
- });
- }
-
- public static String loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)
- {
- final String[] res = new String[1];
- res[0] = null;
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- try {
- String mainLibNameTemplate = "lib" + mainLibrary + ".so";
- File f = new File(nativeLibraryDir + mainLibNameTemplate);
- if (!f.exists()) {
- try {
- ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
- PackageManager.GET_META_DATA);
- String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
- if (info.metaData.containsKey("android.app.system_libs_prefix"))
- systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
- f = new File(systemLibraryDir + mainLibNameTemplate);
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
- }
- if (!f.exists())
- return;
- System.load(f.getAbsolutePath());
- res[0] = f.getAbsolutePath();
- } catch (Exception e) {
- Log.e(QtTAG, "Can't load '" + mainLibrary + "'", e);
- }
- }
- });
- return res[0];
- }
-
- public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
- {
- synchronized (m_mainActivityMutex) {
- m_activity = qtMainActivity;
- m_activityDelegate = qtActivityDelegate;
- }
- }
-
- public static void setService(Service qtMainService, QtServiceDelegate qtServiceDelegate)
- {
- synchronized (m_mainActivityMutex) {
- m_service = qtMainService;
- m_serviceDelegate = qtServiceDelegate;
- }
- }
-
- public static void setApplicationState(int state)
- {
- synchronized (m_mainActivityMutex) {
- switch (state) {
- case QtActivityDelegate.ApplicationActive:
- m_activityPaused = false;
- Iterator<Runnable> itr = m_lostActions.iterator();
- while (itr.hasNext())
- runAction(itr.next());
- m_lostActions.clear();
- break;
- default:
- m_activityPaused = true;
- break;
- }
- }
- updateApplicationState(state);
- }
-
- private static void runAction(Runnable action)
- {
- synchronized (m_mainActivityMutex) {
- final Looper mainLooper = Looper.getMainLooper();
- final Handler handler = new Handler(mainLooper);
- final boolean actionIsQueued = !m_activityPaused && m_activity != null && mainLooper != null && handler.post(action);
- if (!actionIsQueued)
- m_lostActions.add(action);
- }
- }
-
- private static void runPendingCppRunnablesOnAndroidThread()
- {
- synchronized (m_mainActivityMutex) {
- if (m_activity != null) {
- if (!m_activityPaused)
- m_activity.runOnUiThread(runPendingCppRunnablesRunnable);
- else
- runAction(runPendingCppRunnablesRunnable);
- } else {
- final Looper mainLooper = Looper.getMainLooper();
- final Thread looperThread = mainLooper.getThread();
- if (looperThread.equals(Thread.currentThread())) {
- runPendingCppRunnablesRunnable.run();
- } else {
- final Handler handler = new Handler(mainLooper);
- handler.post(runPendingCppRunnablesRunnable);
- }
- }
- }
- }
-
- private static void setViewVisibility(final View view, final boolean visible)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- view.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- });
- }
-
- public static boolean startApplication(String params, final String environment, String mainLib) throws Exception
- {
- if (params == null)
- params = "-platform\tandroid";
-
- final boolean[] res = new boolean[1];
- res[0] = false;
- synchronized (m_mainActivityMutex) {
- if (params.length() > 0 && !params.startsWith("\t"))
- params = "\t" + params;
- final String qtParams = mainLib + params;
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- res[0] = startQtAndroidPlugin(qtParams, environment);
- setDisplayMetrics(m_displayMetricsScreenWidthPixels,
- m_displayMetricsScreenHeightPixels,
- m_displayMetricsAvailableLeftPixels,
- m_displayMetricsAvailableTopPixels,
- m_displayMetricsAvailableWidthPixels,
- m_displayMetricsAvailableHeightPixels,
- m_displayMetricsXDpi,
- m_displayMetricsYDpi,
- m_displayMetricsScaledDensity,
- m_displayMetricsDensity);
- }
- });
- m_qtThread.post(new Runnable() {
- @Override
- public void run() {
- startQtApplication();
- }
- });
- waitForServiceSetup();
- m_started = true;
- }
- return res[0];
- }
-
- public static void setApplicationDisplayMetrics(int screenWidthPixels,
- int screenHeightPixels,
- int availableLeftPixels,
- int availableTopPixels,
- int availableWidthPixels,
- int availableHeightPixels,
- double XDpi,
- double YDpi,
- double scaledDensity,
- double density)
- {
- /* Fix buggy dpi report */
- if (XDpi < android.util.DisplayMetrics.DENSITY_LOW)
- XDpi = android.util.DisplayMetrics.DENSITY_LOW;
- if (YDpi < android.util.DisplayMetrics.DENSITY_LOW)
- YDpi = android.util.DisplayMetrics.DENSITY_LOW;
-
- synchronized (m_mainActivityMutex) {
- if (m_started) {
- setDisplayMetrics(screenWidthPixels,
- screenHeightPixels,
- availableLeftPixels,
- availableTopPixels,
- availableWidthPixels,
- availableHeightPixels,
- XDpi,
- YDpi,
- scaledDensity,
- density);
- } else {
- m_displayMetricsScreenWidthPixels = screenWidthPixels;
- m_displayMetricsScreenHeightPixels = screenHeightPixels;
- m_displayMetricsAvailableLeftPixels = availableLeftPixels;
- m_displayMetricsAvailableTopPixels = availableTopPixels;
- m_displayMetricsAvailableWidthPixels = availableWidthPixels;
- m_displayMetricsAvailableHeightPixels = availableHeightPixels;
- m_displayMetricsXDpi = XDpi;
- m_displayMetricsYDpi = YDpi;
- m_displayMetricsScaledDensity = scaledDensity;
- m_displayMetricsDensity = density;
- }
- }
- }
-
-
-
- // application methods
- public static native boolean startQtAndroidPlugin(String params, String env);
- public static native void startQtApplication();
- public static native void waitForServiceSetup();
- public static native void quitQtCoreApplication();
- public static native void quitQtAndroidPlugin();
- public static native void terminateQt();
- // application methods
-
- private static void quitApp()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- quitQtAndroidPlugin();
- if (m_activity != null)
- m_activity.finish();
- if (m_service != null)
- m_service.stopSelf();
- }
- });
- }
-
- //@ANDROID-9
- static private int getAction(int index, MotionEvent event)
- {
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_MOVE) {
- int hsz = event.getHistorySize();
- if (hsz > 0) {
- float x = event.getX(index);
- float y = event.getY(index);
- for (int h = 0; h < hsz; ++h) {
- if ( event.getHistoricalX(index, h) != x ||
- event.getHistoricalY(index, h) != y )
- return 1;
- }
- return 2;
- }
- return 1;
- }
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN && index == event.getActionIndex()) {
- return 0;
- } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP && index == event.getActionIndex()) {
- return 3;
- }
- return 2;
- }
- //@ANDROID-9
-
- static public void sendTouchEvent(MotionEvent event, int id)
- {
- int pointerType = 0;
-
- if (m_tabletEventSupported == null)
- m_tabletEventSupported = isTabletEventSupported();
-
- switch (event.getToolType(0)) {
- case MotionEvent.TOOL_TYPE_STYLUS:
- pointerType = 1; // QTabletEvent::Pen
- break;
- case MotionEvent.TOOL_TYPE_ERASER:
- pointerType = 3; // QTabletEvent::Eraser
- break;
- }
-
- if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
- sendMouseEvent(event, id);
- } else if (m_tabletEventSupported && pointerType != 0) {
- tabletEvent(id, event.getDeviceId(), event.getEventTime(), event.getAction(), pointerType,
- event.getButtonState(), event.getX(), event.getY(), event.getPressure());
- } else {
- touchBegin(id);
- for (int i = 0; i < event.getPointerCount(); ++i) {
- touchAdd(id,
- event.getPointerId(i),
- getAction(i, event),
- i == 0,
- (int)event.getX(i),
- (int)event.getY(i),
- event.getTouchMajor(i),
- event.getTouchMinor(i),
- event.getOrientation(i),
- event.getPressure(i));
- }
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- touchEnd(id, 0);
- break;
-
- case MotionEvent.ACTION_UP:
- touchEnd(id, 2);
- break;
-
- default:
- touchEnd(id, 1);
- }
- }
- }
-
- static public void sendTrackballEvent(MotionEvent event, int id)
- {
- sendMouseEvent(event,id);
- }
-
- static public boolean sendGenericMotionEvent(MotionEvent event, int id)
- {
- if (((event.getAction() & (MotionEvent.ACTION_SCROLL | MotionEvent.ACTION_HOVER_MOVE)) == 0)
- || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != InputDevice.SOURCE_CLASS_POINTER) {
- return false;
- }
-
- return sendMouseEvent(event, id);
- }
-
- static public boolean sendMouseEvent(MotionEvent event, int id)
- {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_UP:
- mouseUp(id, (int) event.getX(), (int) event.getY());
- break;
-
- case MotionEvent.ACTION_DOWN:
- mouseDown(id, (int) event.getX(), (int) event.getY());
- m_oldx = (int) event.getX();
- m_oldy = (int) event.getY();
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_MOVE:
- if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
- mouseMove(id, (int) event.getX(), (int) event.getY());
- } else {
- int dx = (int) (event.getX() - m_oldx);
- int dy = (int) (event.getY() - m_oldy);
- if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
- mouseMove(id, (int) event.getX(), (int) event.getY());
- m_oldx = (int) event.getX();
- m_oldy = (int) event.getY();
- }
- }
- break;
- case MotionEvent.ACTION_SCROLL:
- mouseWheel(id, (int) event.getX(), (int) event.getY(),
- event.getAxisValue(MotionEvent.AXIS_HSCROLL), event.getAxisValue(MotionEvent.AXIS_VSCROLL));
- break;
- default:
- return false;
- }
- return true;
- }
-
- public static Context getContext() {
- if (m_activity != null)
- return m_activity;
- return m_service;
- }
-
- public static int checkSelfPermission(String permission)
- {
- int perm = PackageManager.PERMISSION_DENIED;
- synchronized (m_mainActivityMutex) {
- Context context = getContext();
- try {
- if (m_checkSelfPermissionMethod == null)
- m_checkSelfPermissionMethod = Context.class.getMethod("checkSelfPermission", String.class);
- perm = (Integer)m_checkSelfPermissionMethod.invoke(context, permission);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- return perm;
- }
-
- private static void updateSelection(final int selStart,
- final int selEnd,
- final int candidatesStart,
- final int candidatesEnd)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.updateSelection(selStart, selEnd, candidatesStart, candidatesEnd);
- }
- });
- }
-
- private static void updateHandles(final int mode,
- final int editX,
- final int editY,
- final int editButtons,
- final int x1,
- final int y1,
- final int x2,
- final int y2,
- final boolean rtl)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- m_activityDelegate.updateHandles(mode, editX, editY, editButtons, x1, y1, x2, y2, rtl);
- }
- });
- }
-
- private static void showSoftwareKeyboard(final int x,
- final int y,
- final int width,
- final int height,
- final int inputHints,
- final int enterKeyType)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
- }
- });
- }
-
- private static void resetSoftwareKeyboard()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.resetSoftwareKeyboard();
- }
- });
- }
-
- private static void hideSoftwareKeyboard()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.hideSoftwareKeyboard();
- }
- });
- }
-
- private static void setSystemUiVisibility(final int systemUiVisibility)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null) {
- m_activityDelegate.setSystemUiVisibility(systemUiVisibility);
- }
- updateWindow();
- }
- });
- }
-
- private static void registerClipboardManager()
- {
- if (m_service == null || m_activity != null) { // Avoid freezing if only service
- final Semaphore semaphore = new Semaphore(0);
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activity != null)
- m_clipboardManager = (android.content.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE);
- if (m_clipboardManager != null) {
- m_clipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
- public void onPrimaryClipChanged() {
- onClipboardDataChanged();
- }
- });
- }
- semaphore.release();
- }
- });
- try {
- semaphore.acquire();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private static void clearClipData()
- {
- if (Build.VERSION.SDK_INT >= 28 && m_clipboardManager != null && m_usePrimaryClip)
- m_clipboardManager.clearPrimaryClip();
- m_usePrimaryClip = false;
- }
- private static void setClipboardText(String text)
- {
- if (m_clipboardManager != null) {
- ClipData clipData = ClipData.newPlainText("text/plain", text);
- updatePrimaryClip(clipData);
- }
- }
-
- public static boolean hasClipboardText()
- {
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getText() != null)
- return true;
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- return false;
- }
-
- private static String getClipboardText()
- {
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getText() != null)
- return primaryClip.getItemAt(i).getText().toString();
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- return "";
- }
-
- private static void updatePrimaryClip(ClipData clipData)
- {
- try {
- if (m_usePrimaryClip) {
- ClipData clip = m_clipboardManager.getPrimaryClip();
- if (Build.VERSION.SDK_INT >= 26) {
- if (m_addItemMethod == null) {
- Class[] cArg = new Class[2];
- cArg[0] = ContentResolver.class;
- cArg[1] = ClipData.Item.class;
- try {
- m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg);
- } catch (Exception e) {
- }
- }
- }
- if (m_addItemMethod != null) {
- try {
- m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0));
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- clip.addItem(clipData.getItemAt(0));
- }
- m_clipboardManager.setPrimaryClip(clip);
- } else {
- m_clipboardManager.setPrimaryClip(clipData);
- m_usePrimaryClip = true;
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to set clipboard data", e);
- }
- }
-
- private static void setClipboardHtml(String text, String html)
- {
- if (m_clipboardManager != null) {
- ClipData clipData = ClipData.newHtmlText("text/html", text, html);
- updatePrimaryClip(clipData);
- }
- }
-
- public static boolean hasClipboardHtml()
- {
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getHtmlText() != null)
- return true;
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- return false;
- }
-
- private static String getClipboardHtml()
- {
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getHtmlText() != null)
- return primaryClip.getItemAt(i).getHtmlText().toString();
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- return "";
- }
-
- private static void setClipboardUri(String uriString)
- {
- if (m_clipboardManager != null) {
- ClipData clipData = ClipData.newUri(m_activity.getContentResolver(), "text/uri-list",
- Uri.parse(uriString));
- updatePrimaryClip(clipData);
- }
- }
-
- public static boolean hasClipboardUri()
- {
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getUri() != null)
- return true;
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- return false;
- }
-
- private static String[] getClipboardUris()
- {
- ArrayList<String> uris = new ArrayList<String>();
- try {
- if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
- ClipData primaryClip = m_clipboardManager.getPrimaryClip();
- for (int i = 0; i < primaryClip.getItemCount(); ++i)
- if (primaryClip.getItemAt(i).getUri() != null)
- uris.add(primaryClip.getItemAt(i).getUri().toString());
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get clipboard data", e);
- }
- String[] strings = new String[uris.size()];
- strings = uris.toArray(strings);
- return strings;
- }
-
- private static void openContextMenu(final int x, final int y, final int w, final int h)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.openContextMenu(x, y, w, h);
- }
- });
- }
-
- private static void closeContextMenu()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.closeContextMenu();
- }
- });
- }
-
- private static void resetOptionsMenu()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.resetOptionsMenu();
- }
- });
- }
-
- private static void openOptionsMenu()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activity != null)
- m_activity.openOptionsMenu();
- }
- });
- }
-
- private static byte[][] getSSLCertificates()
- {
- ArrayList<byte[]> certificateList = new ArrayList<byte[]>();
-
- try {
- TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- factory.init((KeyStore) null);
-
- for (TrustManager manager : factory.getTrustManagers()) {
- if (manager instanceof X509TrustManager) {
- X509TrustManager trustManager = (X509TrustManager) manager;
-
- for (X509Certificate certificate : trustManager.getAcceptedIssuers()) {
- byte buffer[] = certificate.getEncoded();
- certificateList.add(buffer);
- }
- }
- }
- } catch (Exception e) {
- Log.e(QtTAG, "Failed to get certificates", e);
- }
-
- byte[][] certificateArray = new byte[certificateList.size()][];
- certificateArray = certificateList.toArray(certificateArray);
- return certificateArray;
- }
-
- private static void createSurface(final int id, final boolean onTop, final int x, final int y, final int w, final int h, final int imageDepth)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.createSurface(id, onTop, x, y, w, h, imageDepth);
- }
- });
- }
-
- private static void insertNativeView(final int id, final View view, final int x, final int y, final int w, final int h)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.insertNativeView(id, view, x, y, w, h);
- }
- });
- }
-
- private static void setSurfaceGeometry(final int id, final int x, final int y, final int w, final int h)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.setSurfaceGeometry(id, x, y, w, h);
- }
- });
- }
-
- private static void bringChildToFront(final int id)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.bringChildToFront(id);
- }
- });
- }
-
- private static void bringChildToBack(final int id)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.bringChildToBack(id);
- }
- });
- }
-
- private static void destroySurface(final int id)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.destroySurface(id);
- }
- });
- }
-
- private static void initializeAccessibility()
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- m_activityDelegate.initializeAccessibility();
- }
- });
- }
-
- private static void hideSplashScreen(final int duration)
- {
- runAction(new Runnable() {
- @Override
- public void run() {
- if (m_activityDelegate != null)
- m_activityDelegate.hideSplashScreen(duration);
- }
- });
- }
-
- private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
- String [] list;
- ArrayList<String> res = new ArrayList<String>();
- try {
- list = asset.list(path);
- if (list.length > 0) {
- for (String file : list) {
- try {
- String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
- if (isDir != null && isDir.length > 0)
- file += "/";
- res.add(file);
- } catch (Exception e) {}
- }
- }
- } catch (Exception e) {}
- return res.toArray(new String[res.size()]);
- }
-
- // screen methods
- public static native void setDisplayMetrics(int screenWidthPixels,
- int screenHeightPixels,
- int availableLeftPixels,
- int availableTopPixels,
- int availableWidthPixels,
- int availableHeightPixels,
- double XDpi,
- double YDpi,
- double scaledDensity,
- double density);
- public static native void handleOrientationChanged(int newRotation, int nativeOrientation);
- // screen methods
-
- // pointer methods
- public static native void mouseDown(int winId, int x, int y);
- public static native void mouseUp(int winId, int x, int y);
- public static native void mouseMove(int winId, int x, int y);
- public static native void mouseWheel(int winId, int x, int y, float hdelta, float vdelta);
- public static native void touchBegin(int winId);
- public static native void touchAdd(int winId, int pointerId, int action, boolean primary, int x, int y, float major, float minor, float rotation, float pressure);
- public static native void touchEnd(int winId, int action);
- public static native void longPress(int winId, int x, int y);
- // pointer methods
-
- // tablet methods
- public static native boolean isTabletEventSupported();
- public static native void tabletEvent(int winId, int deviceId, long time, int action, int pointerType, int buttonState, float x, float y, float pressure);
- // tablet methods
-
- // keyboard methods
- public static native void keyDown(int key, int unicode, int modifier, boolean autoRepeat);
- public static native void keyUp(int key, int unicode, int modifier, boolean autoRepeat);
- public static native void keyboardVisibilityChanged(boolean visibility);
- public static native void keyboardGeometryChanged(int x, int y, int width, int height);
- // keyboard methods
-
- // handle methods
- public static final int IdCursorHandle = 1;
- public static final int IdLeftHandle = 2;
- public static final int IdRightHandle = 3;
- public static native void handleLocationChanged(int id, int x, int y);
- // handle methods
-
- // dispatch events methods
- public static native boolean dispatchGenericMotionEvent(MotionEvent ev);
- public static native boolean dispatchKeyEvent(KeyEvent event);
- // dispatch events methods
-
- // surface methods
- public static native void setSurface(int id, Object surface, int w, int h);
- // surface methods
-
- // window methods
- public static native void updateWindow();
- // window methods
-
- // application methods
- public static native void updateApplicationState(int state);
-
- // menu methods
- public static native boolean onPrepareOptionsMenu(Menu menu);
- public static native boolean onOptionsItemSelected(int itemId, boolean checked);
- public static native void onOptionsMenuClosed(Menu menu);
-
- public static native void onCreateContextMenu(ContextMenu menu);
- public static native void fillContextMenu(Menu menu);
- public static native boolean onContextItemSelected(int itemId, boolean checked);
- public static native void onContextMenuClosed(Menu menu);
- // menu methods
-
- // clipboard methods
- public static native void onClipboardDataChanged();
- // clipboard methods
-
- // activity methods
- public static native void onActivityResult(int requestCode, int resultCode, Intent data);
- public static native void onNewIntent(Intent data);
-
- public static native void runPendingCppRunnables();
-
- public static native void sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
-
- private static native void setNativeActivity(Activity activity);
- private static native void setNativeService(Service service);
- // activity methods
-
- // service methods
- public static native IBinder onBind(Intent intent);
- // service methods
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java
deleted file mode 100644
index 4a732f7f7f..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-
-public class QtNativeLibrariesDir {
- public static final String systemLibrariesDir = "/system/lib/";
- public static String nativeLibrariesDir(Context context)
- {
- String m_nativeLibraryDir = null;
- try {
- ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
- m_nativeLibraryDir = ai.nativeLibraryDir + "/";
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
- return m_nativeLibraryDir;
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java
deleted file mode 100644
index 4cceab50c7..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.graphics.drawable.ColorDrawable;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ResultReceiver;
-import android.text.method.MetaKeyKeyListener;
-import android.util.Base64;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-
-public class QtServiceDelegate
-{
- private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
- private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
- private static final String MAIN_LIBRARY_KEY = "main.library";
- private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
- private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
- private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
- private static final String APP_DISPLAY_METRIC_SCREEN_DESKTOP_KEY = "display.screen.desktop";
- private static final String APP_DISPLAY_METRIC_SCREEN_XDPI_KEY = "display.screen.dpi.x";
- private static final String APP_DISPLAY_METRIC_SCREEN_YDPI_KEY = "display.screen.dpi.y";
- private static final String APP_DISPLAY_METRIC_SCREEN_DENSITY_KEY = "display.screen.density";
-
- private String m_mainLib = null;
- private Service m_service = null;
- private static String m_environmentVariables = null;
- private static String m_applicationParameters = null;
-
- public boolean loadApplication(Service service, ClassLoader classLoader, Bundle loaderParams)
- {
- /// check parameters integrity
- if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY)
- || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY)) {
- return false;
- }
-
- m_service = service;
- QtNative.setService(m_service, this);
- QtNative.setClassLoader(classLoader);
-
- QtNative.setApplicationDisplayMetrics(10, 10, 0, 0, 10, 10, 120, 120, 1.0, 1.0);
-
- if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) {
- for (String className: loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY)) {
- if (className.length() == 0)
- continue;
- try {
- Class<?> initClass = classLoader.loadClass(className);
- Object staticInitDataObject = initClass.newInstance(); // create an instance
- try {
- Method m = initClass.getMethod("setService", Service.class, Object.class);
- m.invoke(staticInitDataObject, m_service, this);
- } catch (Exception e) {
- }
-
- try {
- Method m = initClass.getMethod("setContext", Context.class);
- m.invoke(staticInitDataObject, (Context)m_service);
- } catch (Exception e) {
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY));
- ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY);
- String nativeLibsDir = QtNativeLibrariesDir.nativeLibrariesDir(m_service);
- QtNative.loadBundledLibraries(libraries, nativeLibsDir);
- m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY);
- m_environmentVariables = loaderParams.getString(ENVIRONMENT_VARIABLES_KEY);
- String additionalEnvironmentVariables = "QT_ANDROID_FONTS_MONOSPACE=Droid Sans Mono;Droid Sans;Droid Sans Fallback"
- + "\tQT_ANDROID_FONTS_SERIF=Droid Serif"
- + "\tHOME=" + m_service.getFilesDir().getAbsolutePath()
- + "\tTMPDIR=" + m_service.getFilesDir().getAbsolutePath();
- if (Build.VERSION.SDK_INT < 14)
- additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Droid Sans;Droid Sans Fallback";
- else
- additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback";
-
- if (m_environmentVariables != null && m_environmentVariables.length() > 0)
- m_environmentVariables = additionalEnvironmentVariables + "\t" + m_environmentVariables;
- else
- m_environmentVariables = additionalEnvironmentVariables;
-
- if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY))
- m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY);
- else
- m_applicationParameters = "";
-
- m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
- return m_mainLib != null;
- }
-
- public boolean startApplication()
- {
- // start application
- try {
- String nativeLibraryDir = QtNativeLibrariesDir.nativeLibrariesDir(m_service);
- QtNative.startApplication(m_applicationParameters, m_environmentVariables, m_mainLib);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public void onDestroy()
- {
- QtNative.quitQtCoreApplication();
- QtNative.terminateQt();
- QtNative.setService(null, null);
- QtNative.m_qtThread.exit();
- System.exit(0);
- }
-
- public IBinder onBind(Intent intent)
- {
- synchronized (this) {
- return QtNative.onBind(intent);
- }
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java
deleted file mode 100644
index 08b5a80f7e..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-public class QtSurface extends SurfaceView implements SurfaceHolder.Callback
-{
- private GestureDetector m_gestureDetector;
- private Object m_accessibilityDelegate = null;
-
- public QtSurface(Context context, int id, boolean onTop, int imageDepth)
- {
- super(context);
- setFocusable(false);
- setFocusableInTouchMode(false);
- setZOrderMediaOverlay(onTop);
- getHolder().addCallback(this);
- if (imageDepth == 16)
- getHolder().setFormat(PixelFormat.RGB_565);
- else
- getHolder().setFormat(PixelFormat.RGBA_8888);
-
- setId(id);
- m_gestureDetector =
- new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
- public void onLongPress(MotionEvent event) {
- QtNative.longPress(getId(), (int) event.getX(), (int) event.getY());
- }
- });
- m_gestureDetector.setIsLongpressEnabled(true);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder)
- {
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
- {
- if (width < 1 || height < 1)
- return;
-
- QtNative.setSurface(getId(), holder.getSurface(), width, height);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder)
- {
- QtNative.setSurface(getId(), null, 0, 0);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event)
- {
- QtNative.sendTouchEvent(event, getId());
- m_gestureDetector.onTouchEvent(event);
- return true;
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent event)
- {
- QtNative.sendTrackballEvent(event, getId());
- return true;
- }
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event)
- {
- return QtNative.sendGenericMotionEvent(event, getId());
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtThread.java b/src/android/jar/src/org/qtproject/qt5/android/QtThread.java
deleted file mode 100644
index 975e787345..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/QtThread.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 BogDan Vatra <bogdan@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android;
-
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-public class QtThread {
- private ArrayList<Runnable> m_pendingRunnables = new ArrayList<Runnable>();
- private boolean m_exit = false;
- private Thread m_qtThread = new Thread(new Runnable() {
- @Override
- public void run() {
- while (!m_exit) {
- try {
- ArrayList<Runnable> pendingRunnables;
- synchronized (m_qtThread) {
- if (m_pendingRunnables.size() == 0)
- m_qtThread.wait();
- pendingRunnables = new ArrayList<Runnable>(m_pendingRunnables);
- m_pendingRunnables.clear();
- }
- for (Runnable runnable : pendingRunnables)
- runnable.run();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- });
-
- QtThread() {
- m_qtThread.setName("qtMainLoopThread");
- m_qtThread.start();
- }
-
- public void post(final Runnable runnable) {
- synchronized (m_qtThread) {
- m_pendingRunnables.add(runnable);
- m_qtThread.notify();
- }
- }
-
- public void run(final Runnable runnable) {
- final Semaphore sem = new Semaphore(0);
- synchronized (m_qtThread) {
- m_pendingRunnables.add(new Runnable() {
- @Override
- public void run() {
- runnable.run();
- sem.release();
- }
- });
- m_qtThread.notify();
- }
- try {
- sem.acquire();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- public void exit()
- {
- m_exit = true;
- synchronized (m_qtThread) {
- m_qtThread.notify();
- }
- try {
- m_qtThread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java
deleted file mode 100644
index a83174377d..0000000000
--- a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Android port of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-package org.qtproject.qt5.android.accessibility;
-
-import android.graphics.Rect;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-class QtNativeAccessibility
-{
- static native void setActive(boolean enable);
- static native int[] childIdListForAccessibleObject(int objectId);
- static native int parentId(int objectId);
- static native String descriptionForAccessibleObject(int objectId);
- static native Rect screenRect(int objectId);
- static native int hitTest(float x, float y);
- static native boolean clickAction(int objectId);
- static native boolean scrollForward(int objectId);
- static native boolean scrollBackward(int objectId);
-
- static native boolean populateNode(int objectId, AccessibilityNodeInfo node);
-}
diff --git a/src/android/java/CMakeLists.txt b/src/android/java/CMakeLists.txt
index 1c24bd5e82..2ff2fb5791 100644
--- a/src/android/java/CMakeLists.txt
+++ b/src/android/java/CMakeLists.txt
@@ -1,12 +1,66 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Android Java Resource files
-# special case skip regeneration
-# special case begin
set(resource_directories
- res
- src)
+ "${CMAKE_CURRENT_SOURCE_DIR}/res"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src"
+)
+
+set(java_bindings
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/org/qtproject/qt/android/bindings/QtActivity.java"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/org/qtproject/qt/android/bindings/QtService.java"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/org/qtproject/qt/android/bindings/QtApplication.java"
+)
+
+set(strings_resouces
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-de/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-el/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-es/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-et/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-fa/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-fr/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-in/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-it/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-ja/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-ms/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-nb/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-nl/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-pl/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-pt-rBR/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-ro/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-ru/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-se/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-zh-rCN/strings.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values-zh-rTW/strings.xml"
+)
+
+add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidBindings
+ SOURCES
+ ${java_bindings}
+ ${strings_resouces}
+)
+
+qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/java")
+
+qt_copy_or_install(DIRECTORY ${resource_directories}
+ DESTINATION "${destination}"
+)
+
+if(NOT QT_WILL_INSTALL)
+ qt_internal_copy_at_build_time(TARGET Qt${QtBase_VERSION_MAJOR}AndroidBindings
+ DIRECTORIES ${resource_directories}
+ DESTINATION ${destination})
+endif()
-qt_install(DIRECTORY ${resource_directories}
- DESTINATION src/android/java
- COMPONENT Devel)
-# special case end
+# To avoid Java build errors after the loader classes have been moved to the internal Jar package,
+# make this step to remove them from the build folder. This mainly useful for existing builds,
+# as clean builds wouldn't have to deal with this case.
+if ("${PROJECT_VERSION}" GREATER_EQUAL "6.7")
+ set(loader_bindings "QtLoader.java" "QtActivityLoader.java" "QtServiceLoader.java")
+ foreach(binding IN LISTS loader_bindings)
+ file(REMOVE "${destination}/src/org/qtproject/qt/android/bindings/${binding}")
+ endforeach()
+endif()
diff --git a/src/android/java/java.pro b/src/android/java/java.pro
deleted file mode 100644
index 7f0dfa8a1b..0000000000
--- a/src/android/java/java.pro
+++ /dev/null
@@ -1,23 +0,0 @@
-CONFIG += single_arch
-
-CONFIG -= qt android_install
-
-javaresources.files = \
- $$PWD/res \
- $$PWD/src
-
-javaresources.path = $$[QT_INSTALL_PREFIX]/src/android/java
-
-INSTALLS += javaresources
-
-!prefix_build:!equals(OUT_PWD, $$PWD) {
- RETURN = $$escape_expand(\\n\\t)
- equals(QMAKE_HOST.os, Windows) {
- RETURN = $$escape_expand(\\r\\n\\t)
- }
- OUT_PATH = $$shell_path($$OUT_PWD)
-
- QMAKE_POST_LINK += \
- $${QMAKE_COPY_DIR} $$shell_path($$PWD/res) $$OUT_PATH $$RETURN \
- $${QMAKE_COPY_DIR} $$shell_path($$PWD/src) $$OUT_PATH
-}
diff --git a/src/android/java/res/values-de/strings.xml b/src/android/java/res/values-de/strings.xml
index 21881ec97f..55b927ebff 100644
--- a/src/android/java/res/values-de/strings.xml
+++ b/src/android/java/res/values-de/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Ministro-Dienst wurde nicht gefunden.\nAnwendung kann nicht gestartet werden</string>
- <string name="ministro_needed_msg">Diese Anwendung benötigt den Ministro-Dienst. Möchten Sie ihn installieren?</string>
<string name="fatal_error_msg">In Ihrer Anwendung ist ein schwerwiegender Fehler aufgetreten, sie kann nicht fortgesetzt werden</string>
<string name="unsupported_android_version">Diese Android-Version wird nicht unterstützt.</string>
</resources>
diff --git a/src/android/java/res/values-el/strings.xml b/src/android/java/res/values-el/strings.xml
index 42b2b3b49d..9dd0973f2d 100644
--- a/src/android/java/res/values-el/strings.xml
+++ b/src/android/java/res/values-el/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Δεν ήταν δυνατή η εύρεση της υπηρεσίας Ministro. Δεν είναι δυνατή η εκκίνηση της εφαρμογής.</string>
- <string name="ministro_needed_msg">Η εφαρμογή απαιτεί την υπηρεσία Ministro. Να εγκατασταθεί η υπηρεσία?</string>
<string name="fatal_error_msg">Παρουσιάστηκε ένα κρίσιμο σφάλμα και η εφαρμογή δεν μπορεί να συνεχίσει.</string>
<string name="unsupported_android_version">Αυτή η έκδοση του Android δεν υποστηρίζεται.</string>
</resources>
diff --git a/src/android/java/res/values-es/strings.xml b/src/android/java/res/values-es/strings.xml
index 1da33a6444..ee991e15f5 100644
--- a/src/android/java/res/values-es/strings.xml
+++ b/src/android/java/res/values-es/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Servicio Ministro inesistente. Imposible ejecutar la aplicación.</string>
- <string name="ministro_needed_msg">Esta aplicación requiere el servicio Ministro. Instalarlo?</string>
<string name="fatal_error_msg">La aplicación ha causado un error grave y no es posible continuar.</string>
<string name="unsupported_android_version">Esta versión de Android no es compatible.</string>
</resources>
diff --git a/src/android/java/res/values-et/strings.xml b/src/android/java/res/values-et/strings.xml
index 9620fd2bc8..9c5b430961 100644
--- a/src/android/java/res/values-et/strings.xml
+++ b/src/android/java/res/values-et/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Ei suuda leida Ministro teenust.\nProgrammi ei saa käivitada.</string>
- <string name="ministro_needed_msg">See programm vajab Ministro teenust.\nKas soovite paigaldada?</string>
<string name="fatal_error_msg">Programmiga juhtus fataalne viga.\nKahjuks ei saa jätkata.</string>
<string name="unsupported_android_version">Seda Androidi versiooni ei toetata.</string>
</resources>
diff --git a/src/android/java/res/values-fa/strings.xml b/src/android/java/res/values-fa/strings.xml
index d1ee06118a..0c88c7b1a1 100644
--- a/src/android/java/res/values-fa/strings.xml
+++ b/src/android/java/res/values-fa/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">سرویس Ministro را پیدا نمی‌کند. برنامه نمی‌تواند آغاز شود.</string>
- <string name="ministro_needed_msg">این نرم‌افزار به سرویس Ministro احتیاج دارد. آیا دوست دارید آن را نصب کنید؟</string>
<string name="fatal_error_msg">خطایی اساسی در برنامه‌تان رخ داد و اجرای برنامه نمی‌تواند ادامه یابد.</string>
<string name="unsupported_android_version">این نسخه از Android پشتیبانی نمی شود</string>
</resources>
diff --git a/src/android/java/res/values-fr/strings.xml b/src/android/java/res/values-fr/strings.xml
index ce8e5f5af1..c3717f377d 100644
--- a/src/android/java/res/values-fr/strings.xml
+++ b/src/android/java/res/values-fr/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Le service Ministro est introuvable.\nL\'application ne peut pas démarrer.</string>
- <string name="ministro_needed_msg">Cette application requiert le service Ministro. Voulez-vous l\'installer?</string>
<string name="fatal_error_msg">Votre application a rencontré une erreur fatale et ne peut pas continuer.</string>
<string name="unsupported_android_version">Cette version d\'Android n\'est pas supportée.</string>
</resources>
diff --git a/src/android/java/res/values-in/strings.xml b/src/android/java/res/values-in/strings.xml
index b25f568ee9..3499dd3ef1 100644
--- a/src/android/java/res/values-in/strings.xml
+++ b/src/android/java/res/values-in/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Layanan Ministro tidak bisa ditemukan.\nAplikasi tidak bisa dimulai.</string>
- <string name="ministro_needed_msg">Aplikasi ini membutuhkan layanan Ministro. Apakah Anda ingin menginstalnya?</string>
<string name="fatal_error_msg">Aplikasi Anda mengalami kesalahan fatal dan tidak dapat melanjutkan.</string>
<string name="unsupported_android_version">Versi Android ini tidak didukung.</string>
</resources>
diff --git a/src/android/java/res/values-it/strings.xml b/src/android/java/res/values-it/strings.xml
index 9ba5fe2b1c..f93fa1cac7 100644
--- a/src/android/java/res/values-it/strings.xml
+++ b/src/android/java/res/values-it/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Servizio Ministro inesistente. Impossibile eseguire \nl\'applicazione.</string>
- <string name="ministro_needed_msg">Questa applicazione richiede il servizio Ministro.Installarlo?</string>
<string name="fatal_error_msg">L\'applicazione ha provocato un errore grave e non puo\' continuare.</string>
<string name="unsupported_android_version">Questa versione di Android non è supportata.</string>
</resources>
diff --git a/src/android/java/res/values-ja/strings.xml b/src/android/java/res/values-ja/strings.xml
index 40da7dce48..f8888b407c 100644
--- a/src/android/java/res/values-ja/strings.xml
+++ b/src/android/java/res/values-ja/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Ministroサービスが見つかりません。\nアプリケーションが起動できません。</string>
- <string name="ministro_needed_msg">このアプリケーションにはMinistroサービスが必要です。 インストールしてもよろしいですか?</string>
<string name="fatal_error_msg">アプリケーションで致命的なエラーが発生したため続行できません。</string>
<string name="unsupported_android_version">このバージョンのAndroidはサポートされていません。</string>
</resources>
diff --git a/src/android/java/res/values-ms/strings.xml b/src/android/java/res/values-ms/strings.xml
index bd27890eb2..bbf90de357 100644
--- a/src/android/java/res/values-ms/strings.xml
+++ b/src/android/java/res/values-ms/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Tidak jumpa servis Ministro.\nAplikasi tidak boleh dimulakan.</string>
- <string name="ministro_needed_msg">Aplikasi ini memerlukan servis Ministro. Adakah anda ingin pasang servis itu?</string>
<string name="fatal_error_msg">Aplikasi anda menemui ralat muat dan tidak boleh diteruskan.</string>
<string name="unsupported_android_version">Versi Android ini tidak disokong.</string>
</resources>
diff --git a/src/android/java/res/values-nb/strings.xml b/src/android/java/res/values-nb/strings.xml
index 53529b7f52..a66e997853 100644
--- a/src/android/java/res/values-nb/strings.xml
+++ b/src/android/java/res/values-nb/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Kan ikke finne tjenesten Ministro. Applikasjonen kan ikke starte.</string>
- <string name="ministro_needed_msg">Denne applikasjonen krever tjenesten Ministro. Vil du installere denne?</string>
<string name="fatal_error_msg">Applikasjonen fikk en kritisk feil og kan ikke fortsette</string>
<string name="unsupported_android_version">Denne versjonen av Android støttes ikke.</string>
</resources>
diff --git a/src/android/java/res/values-nl/strings.xml b/src/android/java/res/values-nl/strings.xml
index 7e54587f61..9f4d166035 100644
--- a/src/android/java/res/values-nl/strings.xml
+++ b/src/android/java/res/values-nl/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">De Ministro service is niet gevonden.\nDe applicatie kan niet starten.</string>
- <string name="ministro_needed_msg">Deze applicatie maakt gebruik van de Ministro service. Wilt u deze installeren?</string>
<string name="fatal_error_msg">Er is een fatale fout in de applicatie opgetreden. De applicatie kan niet verder gaan.</string>
<string name="unsupported_android_version">Deze versie van Android wordt niet ondersteund.</string>
</resources>
diff --git a/src/android/java/res/values-pl/strings.xml b/src/android/java/res/values-pl/strings.xml
index e7feb01392..abbd2f6394 100644
--- a/src/android/java/res/values-pl/strings.xml
+++ b/src/android/java/res/values-pl/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Usługa Ministro nie została znaleziona.\nAplikacja nie może zostać uruchomiona.</string>
- <string name="ministro_needed_msg">Aplikacja wymaga usługi Ministro. Czy chcesz ją zainstalować?</string>
<string name="fatal_error_msg">Wystąpił błąd krytyczny. Aplikacja zostanie zamknięta.</string>
<string name="unsupported_android_version">Ta wersja Androida nie jest obsługiwana.</string>
</resources>
diff --git a/src/android/java/res/values-pt-rBR/strings.xml b/src/android/java/res/values-pt-rBR/strings.xml
index 4bac77c784..8a07b7e68a 100644
--- a/src/android/java/res/values-pt-rBR/strings.xml
+++ b/src/android/java/res/values-pt-rBR/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Não foi possível encontrar o serviço Ministro.\nA aplicação não pode iniciar.</string>
- <string name="ministro_needed_msg">Essa aplicação requer o serviço Ministro. Gostaria de instalá-lo?</string>
<string name="fatal_error_msg">Sua aplicação encontrou um erro fatal e não pode continuar.</string>
<string name="unsupported_android_version">Esta versão do Android não é suportada.</string>
</resources>
diff --git a/src/android/java/res/values-ro/strings.xml b/src/android/java/res/values-ro/strings.xml
index d55c5b5c38..033f743cf4 100644
--- a/src/android/java/res/values-ro/strings.xml
+++ b/src/android/java/res/values-ro/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Serviciul Ministro nu poate fi găsit.\nAplicaţia nu poate porni.</string>
- <string name="ministro_needed_msg">Această aplicaţie necesită serviciul Ministro.\nDoriţi să-l instalaţi?</string>
<string name="fatal_error_msg">Aplicaţia dumneavoastră a întâmpinat o eroare fatală şi nu poate continua.</string>
<string name="unsupported_android_version">Această versiune de Android nu este suportată.</string>
</resources>
diff --git a/src/android/java/res/values-ru/strings.xml b/src/android/java/res/values-ru/strings.xml
index ec853d22f9..73344c8f9b 100644
--- a/src/android/java/res/values-ru/strings.xml
+++ b/src/android/java/res/values-ru/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Сервис Ministro не найден.\nПриложение нельзя запустить.</string>
- <string name="ministro_needed_msg">Этому приложению необходим сервис Ministro. Вы хотите его установить?</string>
<string name="fatal_error_msg">Ваше приложение столкнулось с фатальной ошибкой и не может более работать.</string>
<string name="unsupported_android_version">Эта версия Android не поддерживается.</string>
</resources>
diff --git a/src/android/java/res/values-se/strings.xml b/src/android/java/res/values-se/strings.xml
index 2a1e8284ce..f0927eee1f 100644
--- a/src/android/java/res/values-se/strings.xml
+++ b/src/android/java/res/values-se/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">Ministro servise nije pronađen. Aplikacija ne može biti pokrenuta.</string>
- <string name="ministro_needed_msg">Ova aplikacija zahteva Ministro servis. Želite li da ga instalirate?</string>
<string name="fatal_error_msg">Vaša aplikacija je naišla na fatalnu grešku i ne može nastaviti sa radom.</string>
<string name="unsupported_android_version">Ova verzija Android-a nije podržana.</string>
</resources>
diff --git a/src/android/java/res/values-zh-rCN/strings.xml b/src/android/java/res/values-zh-rCN/strings.xml
index 58cdd89946..d63fb0a1f2 100644
--- a/src/android/java/res/values-zh-rCN/strings.xml
+++ b/src/android/java/res/values-zh-rCN/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">无法找到Ministro服务。\n应用程序无法启动。</string>
- <string name="ministro_needed_msg">此应用程序需要Ministro服务。您想安装它吗?</string>
<string name="fatal_error_msg">您的应用程序遇到一个致命错误导致它无法继续。</string>
<string name="unsupported_android_version">这个版本的安卓系统不被支持。</string>
</resources>
diff --git a/src/android/java/res/values-zh-rTW/strings.xml b/src/android/java/res/values-zh-rTW/strings.xml
index 81d2bebdee..e6dcaac27a 100644
--- a/src/android/java/res/values-zh-rTW/strings.xml
+++ b/src/android/java/res/values-zh-rTW/strings.xml
@@ -1,7 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="ministro_not_found_msg">無法找到Ministro服務。\n應用程序無法啟動。</string>
- <string name="ministro_needed_msg">此應用程序需要Ministro服務。您想安裝它嗎?</string>
<string name="fatal_error_msg">您的應用程序遇到一個致命錯誤導致它無法繼續。</string>
<string name="unsupported_android_version">這個版本的安卓系統不被支持。</string>
</resources>
diff --git a/src/android/java/res/values/strings.xml b/src/android/java/res/values/strings.xml
index 0110948dcf..42077b07db 100644
--- a/src/android/java/res/values/strings.xml
+++ b/src/android/java/res/values/strings.xml
@@ -1,8 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<!-- %%INSERT_STRINGS -->
- <string name="ministro_not_found_msg">Can\'t find Ministro service.\nThe application can\'t start.</string>
- <string name="ministro_needed_msg">This application requires Ministro service. Would you like to install it?</string>
<string name="fatal_error_msg">Your application encountered a fatal error and cannot continue.</string>
<string name="unsupported_android_version">This version of Android is not supported.</string>
</resources>
diff --git a/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl b/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl
deleted file mode 100644
index bbd8116d96..0000000000
--- a/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- Copyright (c) 2011-2013, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-package org.kde.necessitas.ministro;
-
-import org.kde.necessitas.ministro.IMinistroCallback;
-
-interface IMinistro
-{
-/**
-* Check/download required libs to run the application
-*
-* param callback - interface used by Minsitro service to notify the client when the loader is ready
-* param parameters
-* parameters fields:
-* * Key Name Key type Explanations
-* "sources" StringArray Sources list from where Ministro will download the libs. Make sure you are using ONLY secure locations.
-* "repository" String Overwrites the default Ministro repository. Possible values: default, stable, testing and unstable
-* "required.modules" StringArray Required modules by your application
-* "application.title" String Application name, used to show more informations to user
-* "qt.provider" String Qt libs provider, currently only "necessitas" is supported.
-* "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 3 !
-* "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://qt-project.org/doc/qt-4.8/qtglobal.html#QT_VERSION)!
-*/
- void requestLoader(in IMinistroCallback callback, in Bundle parameters);
-}
diff --git a/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl b/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl
deleted file mode 100644
index f19caa69e0..0000000000
--- a/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- Copyright (c) 2011-2013, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.kde.necessitas.ministro;
-
-oneway interface IMinistroCallback {
-/**
-* This method is called by the Ministro service back into the application which
-* implements this interface.
-*
-* param in - loaderParams
-* loaderParams fields:
-* * Key Name Key type Explanations
-* * "error.code" Integer See below
-* * "error.message" String Missing if no error, otherwise will contain the error message translated into phone language where available.
-* * "dex.path" String The list of jar/apk files containing classes and resources, needed to be passed to application DexClassLoader
-* * "lib.path" String The list of directories containing native libraries; may be missing, needed to be passed to application DexClassLoader
-* * "loader.class.name" String Loader class name.
-*
-* "error.code" field possible errors:
-* - 0 no error.
-* - 1 incompatible Ministro version. Ministro needs to be upgraded.
-* - 2 not all modules could be satisfy.
-* - 3 invalid parameters
-* - 4 invalid qt version
-* - 5 download canceled
-*
-* The parameter contains additional fields which are used by the loader to start your application, so it must be passed to the loader.
-*/
-
- void loaderReady(in Bundle loaderParams);
-}
diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java
new file mode 100644
index 0000000000..ca4d2128ba
--- /dev/null
+++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+package org.qtproject.qt.android.bindings;
+
+import android.os.Bundle;
+
+import org.qtproject.qt.android.QtActivityBase;
+
+public class QtActivity extends QtActivityBase
+{
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java
new file mode 100644
index 0000000000..d1330565c1
--- /dev/null
+++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+package org.qtproject.qt.android.bindings;
+
+import org.qtproject.qt.android.QtApplicationBase;
+
+public class QtApplication extends QtApplicationBase
+{
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ }
+}
diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java
new file mode 100644
index 0000000000..6569446901
--- /dev/null
+++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+package org.qtproject.qt.android.bindings;
+
+import org.qtproject.qt.android.QtServiceBase;
+
+public class QtService extends QtServiceBase
+{
+ @Override
+ public void onCreate()
+ {
+ super.onCreate();
+ }
+}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java
deleted file mode 100644
index 86775619ec..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java
+++ /dev/null
@@ -1,990 +0,0 @@
-/*
- Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources.Theme;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.ActionMode;
-import android.view.ActionMode.Callback;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityEvent;
-
-
-public class QtActivity extends Activity
-{
- public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application,
- // the parameters must not contain any white spaces
- // and must be separated with "\t"
- // e.g "-param1\t-param2=value2\t-param3\tvalue3"
-
- public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
- // use this variable to add any environment variables to your application.
- // the env vars must be separated with "\t"
- // e.g. "ENV_VAR1=1\tENV_VAR2=2\t"
- // Currently the following vars are used by the android plugin:
- // * QT_USE_ANDROID_NATIVE_DIALOGS - 1 to use the android native dialogs.
-
- public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use.
- // The name of the theme must be the same with any theme from
- // http://developer.android.com/reference/android/R.style.html
- // The most used themes are:
- // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme
- // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black
- // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light
- // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo
- // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light
- // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault
- // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light
-
- public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme.
-
- private QtActivityLoader m_loader;
- public QtActivity()
- {
- m_loader = new QtActivityLoader(this);
- QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"};
- QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light";
- }
-
-
- /////////////////////////// forward all notifications ////////////////////////////
- /////////////////////////// Super class calls ////////////////////////////////////
- /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE //////////////////////////
- //////////////////////////////////////////////////////////////////////////////////
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event);
- else
- return super.dispatchKeyEvent(event);
- }
- public boolean super_dispatchKeyEvent(KeyEvent event)
- {
- return super.dispatchKeyEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event);
- else
- return super.dispatchPopulateAccessibilityEvent(event);
- }
- public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
- {
- return super.dispatchPopulateAccessibilityEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev);
- else
- return super.dispatchTouchEvent(ev);
- }
- public boolean super_dispatchTouchEvent(MotionEvent event)
- {
- return super.dispatchTouchEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean dispatchTrackballEvent(MotionEvent ev)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev);
- else
- return super.dispatchTrackballEvent(ev);
- }
- public boolean super_dispatchTrackballEvent(MotionEvent event)
- {
- return super.dispatchTrackballEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data)
- {
-
- if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) {
- QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data);
- return;
- }
- if (requestCode == QtLoader.MINISTRO_INSTALL_REQUEST_CODE)
- m_loader.startApp(false);
- super.onActivityResult(requestCode, resultCode, data);
- }
- public void super_onActivityResult(int requestCode, int resultCode, Intent data)
- {
- super.onActivityResult(requestCode, resultCode, data);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onApplyThemeResource(Theme theme, int resid, boolean first)
- {
- if (!QtApplication.invokeDelegate(theme, resid, first).invoked)
- super.onApplyThemeResource(theme, resid, first);
- }
- public void super_onApplyThemeResource(Theme theme, int resid, boolean first)
- {
- super.onApplyThemeResource(theme, resid, first);
- }
- //---------------------------------------------------------------------------
-
-
- @Override
- protected void onChildTitleChanged(Activity childActivity, CharSequence title)
- {
- if (!QtApplication.invokeDelegate(childActivity, title).invoked)
- super.onChildTitleChanged(childActivity, title);
- }
- public void super_onChildTitleChanged(Activity childActivity, CharSequence title)
- {
- super.onChildTitleChanged(childActivity, title);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onConfigurationChanged(Configuration newConfig)
- {
- if (!QtApplication.invokeDelegate(newConfig).invoked)
- super.onConfigurationChanged(newConfig);
- }
- public void super_onConfigurationChanged(Configuration newConfig)
- {
- super.onConfigurationChanged(newConfig);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onContentChanged()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onContentChanged();
- }
- public void super_onContentChanged()
- {
- super.onContentChanged();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onContextItemSelected(MenuItem item)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onContextItemSelected(item);
- }
- public boolean super_onContextItemSelected(MenuItem item)
- {
- return super.onContextItemSelected(item);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onContextMenuClosed(Menu menu)
- {
- if (!QtApplication.invokeDelegate(menu).invoked)
- super.onContextMenuClosed(menu);
- }
- public void super_onContextMenuClosed(Menu menu)
- {
- super.onContextMenuClosed(menu);
- }
- //---------------------------------------------------------------------------
-
- protected void onCreateHook(Bundle savedInstanceState) {
- m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS;
- m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES;
- m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES;
- m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME;
- m_loader.onCreate(savedInstanceState);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- onCreateHook(savedInstanceState);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
- {
- if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked)
- super.onCreateContextMenu(menu, v, menuInfo);
- }
- public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
- {
- super.onCreateContextMenu(menu, v, menuInfo);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public CharSequence onCreateDescription()
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate();
- if (res.invoked)
- return (CharSequence)res.methodReturns;
- else
- return super.onCreateDescription();
- }
- public CharSequence super_onCreateDescription()
- {
- return super.onCreateDescription();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected Dialog onCreateDialog(int id)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(id);
- if (res.invoked)
- return (Dialog)res.methodReturns;
- else
- return super.onCreateDialog(id);
- }
- public Dialog super_onCreateDialog(int id)
- {
- return super.onCreateDialog(id);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onCreateOptionsMenu(menu);
- }
- public boolean super_onCreateOptionsMenu(Menu menu)
- {
- return super.onCreateOptionsMenu(menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onCreatePanelMenu(int featureId, Menu menu)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onCreatePanelMenu(featureId, menu);
- }
- public boolean super_onCreatePanelMenu(int featureId, Menu menu)
- {
- return super.onCreatePanelMenu(featureId, menu);
- }
- //---------------------------------------------------------------------------
-
-
- @Override
- public View onCreatePanelView(int featureId)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId);
- if (res.invoked)
- return (View)res.methodReturns;
- else
- return super.onCreatePanelView(featureId);
- }
- public View super_onCreatePanelView(int featureId)
- {
- return super.onCreatePanelView(featureId);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onCreateThumbnail(outBitmap, canvas);
- }
- public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas)
- {
- return super.onCreateThumbnail(outBitmap, canvas);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs);
- if (res.invoked)
- return (View)res.methodReturns;
- else
- return super.onCreateView(name, context, attrs);
- }
- public View super_onCreateView(String name, Context context, AttributeSet attrs)
- {
- return super.onCreateView(name, context, attrs);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onDestroy()
- {
- super.onDestroy();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event);
- else
- return super.onKeyDown(keyCode, event);
- }
- public boolean super_onKeyDown(int keyCode, KeyEvent event)
- {
- return super.onKeyDown(keyCode, event);
- }
- //---------------------------------------------------------------------------
-
-
- @Override
- public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event);
- else
- return super.onKeyMultiple(keyCode, repeatCount, event);
- }
- public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
- {
- return super.onKeyMultiple(keyCode, repeatCount, event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onKeyUp != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event);
- else
- return super.onKeyUp(keyCode, event);
- }
- public boolean super_onKeyUp(int keyCode, KeyEvent event)
- {
- return super.onKeyUp(keyCode, event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onLowMemory()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onLowMemory();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onMenuItemSelected(featureId, item);
- }
- public boolean super_onMenuItemSelected(int featureId, MenuItem item)
- {
- return super.onMenuItemSelected(featureId, item);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onMenuOpened(int featureId, Menu menu)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onMenuOpened(featureId, menu);
- }
- public boolean super_onMenuOpened(int featureId, Menu menu)
- {
- return super.onMenuOpened(featureId, menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onNewIntent(Intent intent)
- {
- if (!QtApplication.invokeDelegate(intent).invoked)
- super.onNewIntent(intent);
- }
- public void super_onNewIntent(Intent intent)
- {
- super.onNewIntent(intent);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onOptionsItemSelected(item);
- }
- public boolean super_onOptionsItemSelected(MenuItem item)
- {
- return super.onOptionsItemSelected(item);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onOptionsMenuClosed(Menu menu)
- {
- if (!QtApplication.invokeDelegate(menu).invoked)
- super.onOptionsMenuClosed(menu);
- }
- public void super_onOptionsMenuClosed(Menu menu)
- {
- super.onOptionsMenuClosed(menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onPanelClosed(int featureId, Menu menu)
- {
- if (!QtApplication.invokeDelegate(featureId, menu).invoked)
- super.onPanelClosed(featureId, menu);
- }
- public void super_onPanelClosed(int featureId, Menu menu)
- {
- super.onPanelClosed(featureId, menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onPause()
- {
- super.onPause();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState)
- {
- super.onPostCreate(savedInstanceState);
- QtApplication.invokeDelegate(savedInstanceState);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onPostResume()
- {
- super.onPostResume();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog)
- {
- if (!QtApplication.invokeDelegate(id, dialog).invoked)
- super.onPrepareDialog(id, dialog);
- }
- public void super_onPrepareDialog(int id, Dialog dialog)
- {
- super.onPrepareDialog(id, dialog);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onPrepareOptionsMenu(menu);
- }
- public boolean super_onPrepareOptionsMenu(Menu menu)
- {
- return super.onPrepareOptionsMenu(menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onPreparePanel(int featureId, View view, Menu menu)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu);
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onPreparePanel(featureId, view, menu);
- }
- public boolean super_onPreparePanel(int featureId, View view, Menu menu)
- {
- return super.onPreparePanel(featureId, view, menu);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onRestart()
- {
- super.onRestart();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState)
- {
- if (!QtApplication.invokeDelegate(savedInstanceState).invoked)
- super.onRestoreInstanceState(savedInstanceState);
- }
- public void super_onRestoreInstanceState(Bundle savedInstanceState)
- {
- super.onRestoreInstanceState(savedInstanceState);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onResume()
- {
- super.onResume();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public Object onRetainNonConfigurationInstance()
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate();
- if (res.invoked)
- return res.methodReturns;
- else
- return super.onRetainNonConfigurationInstance();
- }
- public Object super_onRetainNonConfigurationInstance()
- {
- return super.onRetainNonConfigurationInstance();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onSaveInstanceState(Bundle outState)
- {
- if (!QtApplication.invokeDelegate(outState).invoked)
- super.onSaveInstanceState(outState);
- }
- public void super_onSaveInstanceState(Bundle outState)
- {
- super.onSaveInstanceState(outState);
-
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onSearchRequested()
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate();
- if (res.invoked)
- return (Boolean)res.methodReturns;
- else
- return super.onSearchRequested();
- }
- public boolean super_onSearchRequested()
- {
- return super.onSearchRequested();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onStart()
- {
- super.onStart();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onStop()
- {
- super.onStop();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onTitleChanged(CharSequence title, int color)
- {
- if (!QtApplication.invokeDelegate(title, color).invoked)
- super.onTitleChanged(title, color);
- }
- public void super_onTitleChanged(CharSequence title, int color)
- {
- super.onTitleChanged(title, color);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onTouchEvent(MotionEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event);
- else
- return super.onTouchEvent(event);
- }
- public boolean super_onTouchEvent(MotionEvent event)
- {
- return super.onTouchEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onTrackballEvent(MotionEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event);
- else
- return super.onTrackballEvent(event);
- }
- public boolean super_onTrackballEvent(MotionEvent event)
- {
- return super.onTrackballEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onUserInteraction()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onUserInteraction();
- }
- public void super_onUserInteraction()
- {
- super.onUserInteraction();
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onUserLeaveHint()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onUserLeaveHint();
- }
- public void super_onUserLeaveHint()
- {
- super.onUserLeaveHint();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onWindowAttributesChanged(LayoutParams params)
- {
- if (!QtApplication.invokeDelegate(params).invoked)
- super.onWindowAttributesChanged(params);
- }
- public void super_onWindowAttributesChanged(LayoutParams params)
- {
- super.onWindowAttributesChanged(params);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus)
- {
- if (!QtApplication.invokeDelegate(hasFocus).invoked)
- super.onWindowFocusChanged(hasFocus);
- }
- public void super_onWindowFocusChanged(boolean hasFocus)
- {
- super.onWindowFocusChanged(hasFocus);
- }
- //---------------------------------------------------------------------------
-
- //////////////// Activity API 5 /////////////
-//@ANDROID-5
- @Override
- public void onAttachedToWindow()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onAttachedToWindow();
- }
- public void super_onAttachedToWindow()
- {
- super.onAttachedToWindow();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onBackPressed()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onBackPressed();
- }
- public void super_onBackPressed()
- {
- super.onBackPressed();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onDetachedFromWindow()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onDetachedFromWindow();
- }
- public void super_onDetachedFromWindow()
- {
- super.onDetachedFromWindow();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event);
- else
- return super.onKeyLongPress(keyCode, event);
- }
- public boolean super_onKeyLongPress(int keyCode, KeyEvent event)
- {
- return super.onKeyLongPress(keyCode, event);
- }
- //---------------------------------------------------------------------------
-//@ANDROID-5
-
-//////////////// Activity API 8 /////////////
-//@ANDROID-8
-@Override
- protected Dialog onCreateDialog(int id, Bundle args)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args);
- if (res.invoked)
- return (Dialog)res.methodReturns;
- else
- return super.onCreateDialog(id, args);
- }
- public Dialog super_onCreateDialog(int id, Bundle args)
- {
- return super.onCreateDialog(id, args);
- }
- //---------------------------------------------------------------------------
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog, Bundle args)
- {
- if (!QtApplication.invokeDelegate(id, dialog, args).invoked)
- super.onPrepareDialog(id, dialog, args);
- }
- public void super_onPrepareDialog(int id, Dialog dialog, Bundle args)
- {
- super.onPrepareDialog(id, dialog, args);
- }
- //---------------------------------------------------------------------------
-//@ANDROID-8
- //////////////// Activity API 11 /////////////
-
-//@ANDROID-11
- @Override
- public boolean dispatchKeyShortcutEvent(KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event);
- else
- return super.dispatchKeyShortcutEvent(event);
- }
- public boolean super_dispatchKeyShortcutEvent(KeyEvent event)
- {
- return super.dispatchKeyShortcutEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onActionModeFinished(ActionMode mode)
- {
- if (!QtApplication.invokeDelegate(mode).invoked)
- super.onActionModeFinished(mode);
- }
- public void super_onActionModeFinished(ActionMode mode)
- {
- super.onActionModeFinished(mode);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onActionModeStarted(ActionMode mode)
- {
- if (!QtApplication.invokeDelegate(mode).invoked)
- super.onActionModeStarted(mode);
- }
- public void super_onActionModeStarted(ActionMode mode)
- {
- super.onActionModeStarted(mode);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onAttachFragment(Fragment fragment)
- {
- if (!QtApplication.invokeDelegate(fragment).invoked)
- super.onAttachFragment(fragment);
- }
- public void super_onAttachFragment(Fragment fragment)
- {
- super.onAttachFragment(fragment);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs);
- if (res.invoked)
- return (View)res.methodReturns;
- else
- return super.onCreateView(parent, name, context, attrs);
- }
- public View super_onCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return super.onCreateView(parent, name, context, attrs);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onKeyShortcut(int keyCode, KeyEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode,event);
- else
- return super.onKeyShortcut(keyCode, event);
- }
- public boolean super_onKeyShortcut(int keyCode, KeyEvent event)
- {
- return super.onKeyShortcut(keyCode, event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public ActionMode onWindowStartingActionMode(Callback callback)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback);
- if (res.invoked)
- return (ActionMode)res.methodReturns;
- else
- return super.onWindowStartingActionMode(callback);
- }
- public ActionMode super_onWindowStartingActionMode(Callback callback)
- {
- return super.onWindowStartingActionMode(callback);
- }
- //---------------------------------------------------------------------------
-//@ANDROID-11
- //////////////// Activity API 12 /////////////
-
-//@ANDROID-12
- @Override
- public boolean dispatchGenericMotionEvent(MotionEvent ev)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev);
- else
- return super.dispatchGenericMotionEvent(ev);
- }
- public boolean super_dispatchGenericMotionEvent(MotionEvent event)
- {
- return super.dispatchGenericMotionEvent(event);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null)
- return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event);
- else
- return super.onGenericMotionEvent(event);
- }
- public boolean super_onGenericMotionEvent(MotionEvent event)
- {
- return super.onGenericMotionEvent(event);
- }
- //---------------------------------------------------------------------------
-//@ANDROID-12
-
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
- {
- if (QtApplication.m_delegateObject != null && QtApplication.onRequestPermissionsResult != null) {
- QtApplication.invokeDelegateMethod(QtApplication.onRequestPermissionsResult, requestCode , permissions, grantResults);
- return;
- }
- }
-}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java
deleted file mode 100644
index c3074c353e..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt-project.org/legal
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and Digia. For licensing terms and
- conditions see http://qt.digia.com/licensing. For further information
- use the contact form at http://qt.digia.com/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.Window;
-
-
-import java.lang.reflect.Field;
-
-public class QtActivityLoader extends QtLoader {
- QtActivity m_activity;
-
- QtActivityLoader(QtActivity activity)
- {
- super(activity, QtActivity.class);
- m_activity = activity;
- }
- @Override
- protected void downloadUpgradeMinistro(String msg) {
- AlertDialog.Builder downloadDialog = new AlertDialog.Builder(m_activity);
- downloadDialog.setMessage(msg);
- downloadDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- try {
- Uri uri = Uri.parse("market://details?id=org.kde.necessitas.ministro");
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- m_activity.startActivityForResult(intent, MINISTRO_INSTALL_REQUEST_CODE);
- } catch (Exception e) {
- e.printStackTrace();
- ministroNotFound();
- }
- }
- });
-
- downloadDialog.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- m_activity.finish();
- }
- });
- downloadDialog.show();
- }
-
- @Override
- protected String loaderClassName() {
- return "org.qtproject.qt5.android.QtActivityDelegate";
- }
-
- @Override
- protected Class<?> contextClassName() {
- return android.app.Activity.class;
- }
-
- @Override
- protected void finish() {
- m_activity.finish();
- }
-
- @Override
- protected String getTitle() {
- return (String) m_activity.getTitle();
- }
-
- @Override
- protected void runOnUiThread(Runnable run) {
- m_activity.runOnUiThread(run);
- }
-
- @Override
- Intent getIntent() {
- return m_activity.getIntent();
- }
-
- public void onCreate(Bundle savedInstanceState) {
- try {
- m_contextInfo = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
- int theme = ((ActivityInfo)m_contextInfo).getThemeResource();
- for (Field f : Class.forName("android.R$style").getDeclaredFields()) {
- if (f.getInt(null) == theme) {
- QT_ANDROID_THEMES = new String[] {f.getName()};
- QT_ANDROID_DEFAULT_THEME = f.getName();
- break;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- finish();
- return;
- }
-
- try {
- m_activity.setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null));
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- m_activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
-
- if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) {
- QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState);
- return;
- }
-
- m_displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
-
- ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME
- + "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t";
-
- if (null == m_activity.getLastNonConfigurationInstance()) {
- if (m_contextInfo.metaData.containsKey("android.app.background_running")
- && m_contextInfo.metaData.getBoolean("android.app.background_running")) {
- ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t";
- } else {
- ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t";
- }
-
- if (m_contextInfo.metaData.containsKey("android.app.auto_screen_scale_factor")
- && m_contextInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) {
- ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t";
- }
-
- startApp(true);
- }
- }
-}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java
deleted file mode 100644
index afc0432bdd..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.app.Application;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-public class QtApplication extends Application
-{
- public final static String QtTAG = "Qt";
- public static Object m_delegateObject = null;
- public static HashMap<String, ArrayList<Method>> m_delegateMethods= new HashMap<String, ArrayList<Method>>();
- public static Method dispatchKeyEvent = null;
- public static Method dispatchPopulateAccessibilityEvent = null;
- public static Method dispatchTouchEvent = null;
- public static Method dispatchTrackballEvent = null;
- public static Method onKeyDown = null;
- public static Method onKeyMultiple = null;
- public static Method onKeyUp = null;
- public static Method onTouchEvent = null;
- public static Method onTrackballEvent = null;
- public static Method onActivityResult = null;
- public static Method onCreate = null;
- public static Method onKeyLongPress = null;
- public static Method dispatchKeyShortcutEvent = null;
- public static Method onKeyShortcut = null;
- public static Method dispatchGenericMotionEvent = null;
- public static Method onGenericMotionEvent = null;
- public static Method onRequestPermissionsResult = null;
- private static String activityClassName;
- public static void setQtContextDelegate(Class<?> clazz, Object listener)
- {
- m_delegateObject = listener;
- activityClassName = clazz.getCanonicalName();
-
- ArrayList<Method> delegateMethods = new ArrayList<Method>();
- for (Method m : listener.getClass().getMethods()) {
- if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt5.android"))
- delegateMethods.add(m);
- }
-
- ArrayList<Field> applicationFields = new ArrayList<Field>();
- for (Field f : QtApplication.class.getFields()) {
- if (f.getDeclaringClass().getName().equals(QtApplication.class.getName()))
- applicationFields.add(f);
- }
-
- for (Method delegateMethod : delegateMethods) {
- try {
- clazz.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes());
- if (QtApplication.m_delegateMethods.containsKey(delegateMethod.getName())) {
- QtApplication.m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod);
- } else {
- ArrayList<Method> delegateSet = new ArrayList<Method>();
- delegateSet.add(delegateMethod);
- QtApplication.m_delegateMethods.put(delegateMethod.getName(), delegateSet);
- }
- for (Field applicationField:applicationFields) {
- if (applicationField.getName().equals(delegateMethod.getName())) {
- try {
- applicationField.set(null, delegateMethod);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- } catch (Exception e) {
- }
- }
- }
-
- @Override
- public void onTerminate() {
- if (m_delegateObject != null && m_delegateMethods.containsKey("onTerminate"))
- invokeDelegateMethod(m_delegateMethods.get("onTerminate").get(0));
- super.onTerminate();
- }
-
- public static class InvokeResult
- {
- public boolean invoked = false;
- public Object methodReturns = null;
- }
-
- private static int stackDeep=-1;
- public static InvokeResult invokeDelegate(Object... args)
- {
- InvokeResult result = new InvokeResult();
- if (m_delegateObject == null)
- return result;
- StackTraceElement[] elements = Thread.currentThread().getStackTrace();
- if (-1 == stackDeep) {
- for (int it=0;it<elements.length;it++)
- if (elements[it].getClassName().equals(activityClassName)) {
- stackDeep = it;
- break;
- }
- }
- if (-1 == stackDeep)
- return result;
-
- final String methodName=elements[stackDeep].getMethodName();
- if (!m_delegateMethods.containsKey(methodName))
- return result;
-
- for (Method m : m_delegateMethods.get(methodName)) {
- if (m.getParameterTypes().length == args.length) {
- result.methodReturns = invokeDelegateMethod(m, args);
- result.invoked = true;
- return result;
- }
- }
- return result;
- }
-
- public static Object invokeDelegateMethod(Method m, Object... args)
- {
- try {
- return m.invoke(m_delegateObject, args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
deleted file mode 100644
index 1e72aa3841..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- Copyright (c) 2019, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ComponentInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import org.kde.necessitas.ministro.IMinistro;
-import org.kde.necessitas.ministro.IMinistroCallback;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-import dalvik.system.DexClassLoader;
-
-public abstract class QtLoader {
-
- public final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished
- public static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file)
- public static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin
- public static final int QT_VERSION = 0x050700; // This app requires at least Qt version 5.7.0
-
- public static final String ERROR_CODE_KEY = "error.code";
- public static final String ERROR_MESSAGE_KEY = "error.message";
- public static final String DEX_PATH_KEY = "dex.path";
- public static final String LIB_PATH_KEY = "lib.path";
- public static final String LOADER_CLASS_NAME_KEY = "loader.class.name";
- public static final String NATIVE_LIBRARIES_KEY = "native.libraries";
- public static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
- public static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
- public static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
- public static final String MAIN_LIBRARY_KEY = "main.library";
- public static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
- public static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level";
- public static final String EXTRACT_STYLE_KEY = "extract.android.style";
- private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
-
- // These parameters matter in case of deploying application as system (embedded into firmware)
- public static final String SYSTEM_LIB_PATH = "/system/lib/";
- public String[] SYSTEM_APP_PATHS = {"/system/priv-app/", "/system/app/"};
-
- /// Ministro server parameter keys
- public static final String REQUIRED_MODULES_KEY = "required.modules";
- public static final String APPLICATION_TITLE_KEY = "application.title";
- public static final String MINIMUM_MINISTRO_API_KEY = "minimum.ministro.api";
- public static final String MINIMUM_QT_VERSION_KEY = "minimum.qt.version";
- public static final String SOURCES_KEY = "sources"; // needs MINISTRO_API_LEVEL >=3 !!!
- // Use this key to specify any 3rd party sources urls
- // Ministro will download these repositories into their
- // own folders, check http://community.kde.org/Necessitas/Ministro
- // for more details.
-
- public static final String REPOSITORY_KEY = "repository"; // use this key to overwrite the default ministro repsitory
- public static final String ANDROID_THEMES_KEY = "android.themes"; // themes that your application uses
-
- public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application,
- // the parameters must not contain any white spaces
- // and must be separated with "\t"
- // e.g "-param1\t-param2=value2\t-param3\tvalue3"
-
- public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
- // use this variable to add any environment variables to your application.
- // the env vars must be separated with "\t"
- // e.g. "ENV_VAR1=1\tENV_VAR2=2\t"
- // Currently the following vars are used by the android plugin:
- // * QT_USE_ANDROID_NATIVE_DIALOGS -1 to use the android native dialogs.
-
- public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use.
- // The name of the theme must be the same with any theme from
- // http://developer.android.com/reference/android/R.style.html
- // The most used themes are:
- // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme
- // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black
- // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light
- // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo
- // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light
- // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault
- // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light
-
- public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme.
-
- public static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded.
-
- public String[] m_sources = {"https://download.qt-project.org/ministro/android/qt5/qt-5.7"}; // Make sure you are using ONLY secure locations
- public String m_repository = "default"; // Overwrites the default Ministro repository
- // Possible values:
- // * default - Ministro default repository set with "Ministro configuration tool".
- // By default the stable version is used. Only this or stable repositories should
- // be used in production.
- // * stable - stable repository, only this and default repositories should be used
- // in production.
- // * testing - testing repository, DO NOT use this repository in production,
- // this repository is used to push a new release, and should be used to test your application.
- // * unstable - unstable repository, DO NOT use this repository in production,
- // this repository is used to push Qt snapshots.
- public ArrayList<String> m_qtLibs = null; // required qt libs
- public int m_displayDensity = -1;
- private ContextWrapper m_context;
- protected ComponentInfo m_contextInfo;
- private Class<?> m_delegateClass;
-
- private static ArrayList<FileOutputStream> m_fileOutputStreams = new ArrayList<FileOutputStream>();
- // List of open file streams associated with files copied during installation.
-
- QtLoader(ContextWrapper context, Class<?> clazz) {
- m_context = context;
- m_delegateClass = clazz;
- }
-
- // Implement in subclass
- protected void finish() {}
-
- protected String getTitle() {
- return "Qt";
- }
-
- protected void runOnUiThread(Runnable run) {
- run.run();
- }
- protected void downloadUpgradeMinistro(String msg)
- {
- Log.e(QtApplication.QtTAG, msg);
- }
-
- protected abstract String loaderClassName();
- protected abstract Class<?> contextClassName();
-
- Intent getIntent()
- {
- return null;
- }
- // Implement in subclass
-
- private final List<String> supportedAbis = Arrays.asList(Build.SUPPORTED_ABIS);
- private String preferredAbi = null;
-
- private ArrayList<String> prefferedAbiLibs(String []libs)
- {
- HashMap<String, ArrayList<String>> abisLibs = new HashMap<>();
- for (String lib : libs) {
- String[] archLib = lib.split(";", 2);
- if (preferredAbi != null && !archLib[0].equals(preferredAbi))
- continue;
- if (!abisLibs.containsKey(archLib[0]))
- abisLibs.put(archLib[0], new ArrayList<String>());
- abisLibs.get(archLib[0]).add(archLib[1]);
- }
-
- if (preferredAbi != null) {
- if (abisLibs.containsKey(preferredAbi)) {
- return abisLibs.get(preferredAbi);
- }
- return new ArrayList<String>();
- }
-
- for (String abi: supportedAbis) {
- if (abisLibs.containsKey(abi)) {
- preferredAbi = abi;
- return abisLibs.get(abi);
- }
- }
- return new ArrayList<String>();
- }
-
- // this function is used to load and start the loader
- private void loadApplication(Bundle loaderParams)
- {
- try {
- final int errorCode = loaderParams.getInt(ERROR_CODE_KEY);
- if (errorCode != 0) {
- if (errorCode == INCOMPATIBLE_MINISTRO_VERSION) {
- downloadUpgradeMinistro(loaderParams.getString(ERROR_MESSAGE_KEY));
- return;
- }
-
- // fatal error, show the error and quit
- AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
- errorDialog.setMessage(loaderParams.getString(ERROR_MESSAGE_KEY));
- errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- errorDialog.show();
- return;
- }
-
- // add all bundled Qt libs to loader params
- ArrayList<String> libs = new ArrayList<String>();
- if (m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id")) {
- int resourceId = m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id");
- libs.addAll(prefferedAbiLibs(m_context.getResources().getStringArray(resourceId)));
- }
-
- String libName = null;
- if (m_contextInfo.metaData.containsKey("android.app.lib_name")) {
- libName = m_contextInfo.metaData.getString("android.app.lib_name") + "_" + preferredAbi;
- loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
- }
-
- loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
- loaderParams.putInt(NECESSITAS_API_LEVEL_KEY, NECESSITAS_API_LEVEL);
-
- // load and start QtLoader class
- DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
- m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written.
- loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists)
- m_context.getClassLoader()); // parent loader
-
- Class<?> loaderClass = classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class
- Object qtLoader = loaderClass.newInstance(); // create an instance
- Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication",
- contextClassName(),
- ClassLoader.class,
- Bundle.class);
- if (!(Boolean)prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams))
- throw new Exception("");
-
- QtApplication.setQtContextDelegate(m_delegateClass, qtLoader);
-
- Method startAppMethod=qtLoader.getClass().getMethod("startApplication");
- if (!(Boolean)startAppMethod.invoke(qtLoader))
- throw new Exception("");
-
- } catch (Exception e) {
- e.printStackTrace();
- AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
- if (m_contextInfo.metaData.containsKey("android.app.fatal_error_msg"))
- errorDialog.setMessage(m_contextInfo.metaData.getString("android.app.fatal_error_msg"));
- else
- errorDialog.setMessage("Fatal error, your application can't be started.");
-
- errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- errorDialog.show();
- }
- }
-
- private ServiceConnection m_ministroConnection=new ServiceConnection() {
- private IMinistro m_service = null;
- @Override
- public void onServiceConnected(ComponentName name, IBinder service)
- {
- m_service = IMinistro.Stub.asInterface(service);
- try {
- if (m_service != null) {
- Bundle parameters = new Bundle();
- parameters.putStringArray(REQUIRED_MODULES_KEY, (String[]) m_qtLibs.toArray());
- parameters.putString(APPLICATION_TITLE_KEY, getTitle());
- parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL);
- parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION);
- parameters.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES);
- if (APPLICATION_PARAMETERS != null)
- parameters.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS);
- parameters.putStringArray(SOURCES_KEY, m_sources);
- parameters.putString(REPOSITORY_KEY, m_repository);
- if (QT_ANDROID_THEMES != null)
- parameters.putStringArray(ANDROID_THEMES_KEY, QT_ANDROID_THEMES);
- m_service.requestLoader(m_ministroCallback, parameters);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub() {
- // this function is called back by Ministro.
- @Override
- public void loaderReady(final Bundle loaderParams) throws RemoteException {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- m_context.unbindService(m_ministroConnection);
- loadApplication(loaderParams);
- }
- });
- }
- };
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- m_service = null;
- }
- };
-
- protected void ministroNotFound()
- {
- AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
-
- if (m_contextInfo.metaData.containsKey("android.app.ministro_not_found_msg"))
- errorDialog.setMessage(m_contextInfo.metaData.getString("android.app.ministro_not_found_msg"));
- else
- errorDialog.setMessage("Can't find Ministro service.\nThe application can't start.");
-
- errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- errorDialog.show();
- }
-
- public void startApp(final boolean firstStart)
- {
- try {
- if (m_contextInfo.metaData.containsKey("android.app.qt_sources_resource_id")) {
- int resourceId = m_contextInfo.metaData.getInt("android.app.qt_sources_resource_id");
- m_sources = m_context.getResources().getStringArray(resourceId);
- }
-
- if (m_contextInfo.metaData.containsKey("android.app.repository"))
- m_repository = m_contextInfo.metaData.getString("android.app.repository");
-
- if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) {
- int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id");
- m_qtLibs = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId));
- }
-
- if (m_contextInfo.metaData.containsKey("android.app.use_local_qt_libs")
- && m_contextInfo.metaData.getInt("android.app.use_local_qt_libs") == 1) {
- ArrayList<String> libraryList = new ArrayList<String>();
-
- boolean apkDeployFromSystem = false;
- String apkPath = m_context.getApplicationInfo().publicSourceDir;
- File apkFile = new File(apkPath);
- if (apkFile.exists() && Arrays.asList(SYSTEM_APP_PATHS).contains(apkFile.getParentFile().getAbsolutePath() + "/"))
- apkDeployFromSystem = true;
-
- String libsDir = null;
- String bundledLibsDir = null;
- if (apkDeployFromSystem) {
- String systemLibsPrefix = SYSTEM_LIB_PATH;
- if (m_contextInfo.metaData.containsKey("android.app.system_libs_prefix")) {
- systemLibsPrefix = m_contextInfo.metaData.getString("android.app.system_libs_prefix");
- } else {
- Log.e(QtApplication.QtTAG, "It looks like app deployed as system app. "
- + "It may be necessary to specify path to system lib directory using "
- + "android.app.system_libs_prefix metadata variable in your AndroidManifest.xml");
- Log.e(QtApplication.QtTAG, "Using " + SYSTEM_LIB_PATH + " as default path");
- }
- File systemLibraryDir = new File(systemLibsPrefix);
- if (systemLibraryDir.exists() && systemLibraryDir.isDirectory() && systemLibraryDir.list().length > 0)
- libsDir = systemLibsPrefix;
- } else {
- String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/";
- File nativeLibraryDir = new File(nativeLibraryPrefix);
- if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) {
- libsDir = nativeLibraryPrefix;
- bundledLibsDir = nativeLibraryPrefix;
- }
-
- }
-
- if (apkDeployFromSystem && libsDir == null)
- throw new Exception("");
-
-
- if (m_qtLibs != null) {
- String libPrefix = libsDir + "lib";
- for (String lib : m_qtLibs)
- libraryList.add(libPrefix + lib + ".so");
- }
-
- if (m_contextInfo.metaData.containsKey("android.app.bundle_local_qt_libs")
- && m_contextInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) {
- int resourceId = m_contextInfo.metaData.getInt("android.app.load_local_libs_resource_id");
- for (String libs : prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))) {
- for (String lib : libs.split(":")) {
- if (!lib.isEmpty())
- libraryList.add(libsDir + lib);
- }
- }
- if (bundledLibsDir != null)
- ENVIRONMENT_VARIABLES += "\tQT_BUNDLED_LIBS_PATH=" + bundledLibsDir;
- }
-
- Bundle loaderParams = new Bundle();
- loaderParams.putInt(ERROR_CODE_KEY, 0);
- loaderParams.putString(DEX_PATH_KEY, new String());
- loaderParams.putString(LOADER_CLASS_NAME_KEY, loaderClassName());
- if (m_contextInfo.metaData.containsKey("android.app.static_init_classes")) {
- loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY,
- m_contextInfo.metaData.getString("android.app.static_init_classes").split(":"));
- }
- loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
-
-
- String themePath = m_context.getApplicationInfo().dataDir + "/qt-reserved-files/android-style/";
- String stylePath = themePath + m_displayDensity + "/";
-
- String extractOption = "default";
- if (m_contextInfo.metaData.containsKey("android.app.extract_android_style")) {
- extractOption = m_contextInfo.metaData.getString("android.app.extract_android_style");
- if (!extractOption.equals("default") && !extractOption.equals("full") && !extractOption.equals("minimal") && !extractOption.equals("none")) {
- Log.e(QtApplication.QtTAG, "Invalid extract_android_style option \"" + extractOption + "\", defaulting to \"default\"");
- extractOption = "default";
- }
- }
-
- // QTBUG-69810: The extraction code will trigger compatibility warnings on Android SDK version >= 28
- // when the target SDK version is set to something lower then 28, so default to "none" and issue a warning
- // if that is the case.
- if (extractOption.equals("default")) {
- final int targetSdkVersion = m_context.getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion < 28 && Build.VERSION.SDK_INT >= 28) {
- Log.e(QtApplication.QtTAG, "extract_android_style option set to \"none\" when targetSdkVersion is less then 28");
- extractOption = "none";
- }
- }
-
- if (!(new File(stylePath)).exists() && !extractOption.equals("none")) {
- loaderParams.putString(EXTRACT_STYLE_KEY, stylePath);
- loaderParams.putBoolean(EXTRACT_STYLE_MINIMAL_KEY, extractOption.equals("minimal"));
- }
-
- if (extractOption.equals("full"))
- ENVIRONMENT_VARIABLES += "\tQT_USE_ANDROID_NATIVE_STYLE=1";
-
- ENVIRONMENT_VARIABLES += "\tMINISTRO_ANDROID_STYLE_PATH=" + stylePath
- + "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath;
-
- loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES);
-
- String appParams = null;
- if (APPLICATION_PARAMETERS != null)
- appParams = APPLICATION_PARAMETERS;
-
- Intent intent = getIntent();
- if (intent != null) {
- String parameters = intent.getStringExtra("applicationArguments");
- if (parameters != null)
- if (appParams == null)
- appParams = parameters;
- else
- appParams += '\t' + parameters;
- }
-
- if (m_contextInfo.metaData.containsKey("android.app.arguments")) {
- String parameters = m_contextInfo.metaData.getString("android.app.arguments");
- if (appParams == null)
- appParams = parameters;
- else
- appParams += '\t' + parameters;
- }
-
- if (appParams != null)
- loaderParams.putString(APPLICATION_PARAMETERS_KEY, appParams.replace(' ', '\t').trim());
-
- loadApplication(loaderParams);
- return;
- }
-
- try {
- if (!m_context.bindService(new Intent(org.kde.necessitas.ministro.IMinistro.class.getCanonicalName()),
- m_ministroConnection,
- Context.BIND_AUTO_CREATE)) {
- throw new SecurityException("");
- }
- } catch (Exception e) {
- if (firstStart) {
- String msg = "This application requires Ministro service. Would you like to install it?";
- if (m_contextInfo.metaData.containsKey("android.app.ministro_needed_msg"))
- msg = m_contextInfo.metaData.getString("android.app.ministro_needed_msg");
- downloadUpgradeMinistro(msg);
- } else {
- ministroNotFound();
- }
- }
- } catch (Exception e) {
- Log.e(QtApplication.QtTAG, "Can't create main activity", e);
- }
- }
-}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java
deleted file mode 100644
index cdd6c1efea..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.app.Service;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.IBinder;
-
-public class QtService extends Service
-{
- QtServiceLoader m_loader = new QtServiceLoader(this);
-
-
- /////////////////////////// forward all notifications ////////////////////////////
- /////////////////////////// Super class calls ////////////////////////////////////
- /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE //////////////////////////
- //////////////////////////////////////////////////////////////////////////////////
- protected void onCreateHook() {
- m_loader.onCreate();
- }
- @Override
- public void onCreate()
- {
- super.onCreate();
- onCreateHook();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onDestroy()
- {
- super.onDestroy();
- QtApplication.invokeDelegate();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public IBinder onBind(Intent intent)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent);
- if (res.invoked)
- return (IBinder)res.methodReturns;
- else
- return null;
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onConfigurationChanged(Configuration newConfig)
- {
- if (!QtApplication.invokeDelegate(newConfig).invoked)
- super.onConfigurationChanged(newConfig);
- }
- public void super_onConfigurationChanged(Configuration newConfig)
- {
- super.onConfigurationChanged(newConfig);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onLowMemory()
- {
- if (!QtApplication.invokeDelegate().invoked)
- super.onLowMemory();
- }
- //---------------------------------------------------------------------------
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent, flags, startId);
- if (res.invoked)
- return (Integer) res.methodReturns;
- else
- return super.onStartCommand(intent, flags, startId);
- }
- public int super_onStartCommand(Intent intent, int flags, int startId)
- {
- return super.onStartCommand(intent, flags, startId);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onTaskRemoved(Intent rootIntent)
- {
- if (!QtApplication.invokeDelegate(rootIntent).invoked)
- super.onTaskRemoved(rootIntent);
- }
- public void super_onTaskRemoved(Intent rootIntent)
- {
- super.onTaskRemoved(rootIntent);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public void onTrimMemory(int level)
- {
- if (!QtApplication.invokeDelegate(level).invoked)
- super.onTrimMemory(level);
- }
- public void super_onTrimMemory(int level)
- {
- super.onTrimMemory(level);
- }
- //---------------------------------------------------------------------------
-
- @Override
- public boolean onUnbind(Intent intent)
- {
- QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent);
- if (res.invoked)
- return (Boolean) res.methodReturns;
- else
- return super.onUnbind(intent);
- }
- public boolean super_onUnbind(Intent intent)
- {
- return super.onUnbind(intent);
- }
- //---------------------------------------------------------------------------
-}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java
deleted file mode 100644
index e32a4048e1..0000000000
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
- Contact: http://www.qt.io/licensing/
-
- Commercial License Usage
- Licensees holding valid commercial Qt licenses may use this file in
- accordance with the commercial license agreement provided with the
- Software or, alternatively, in accordance with the terms contained in
- a written agreement between you and The Qt Company. For licensing terms
- and conditions see http://www.qt.io/terms-conditions. For further
- information use the contact form at http://www.qt.io/contact-us.
-
- BSD License Usage
- Alternatively, this file may be used under the BSD license as follows:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package org.qtproject.qt5.android.bindings;
-
-import android.os.Bundle;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-
-public class QtServiceLoader extends QtLoader {
- QtService m_service;
- QtServiceLoader(QtService service) {
- super(service, QtService.class);
- m_service = service;
- }
-
- public void onCreate() {
- try {
- m_contextInfo = m_service.getPackageManager().getServiceInfo(new ComponentName(m_service, m_service.getClass()), PackageManager.GET_META_DATA);
- } catch (Exception e) {
- e.printStackTrace();
- m_service.stopSelf();
- return;
- }
-
- if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) {
- Bundle bundle = null;
- QtApplication.invokeDelegateMethod(QtApplication.onCreate, bundle);
- }
- startApp(true);
- }
-
- @Override
- protected void finish() {
- m_service.stopSelf();
- }
-
- @Override
- protected String loaderClassName() {
- return "org.qtproject.qt5.android.QtServiceDelegate";
- }
-
- @Override
- protected Class<?> contextClassName() {
- return android.app.Service.class;
- }
-}
diff --git a/src/android/templates/.gitignore b/src/android/templates/.gitignore
new file mode 100644
index 0000000000..90d41c1b2b
--- /dev/null
+++ b/src/android/templates/.gitignore
@@ -0,0 +1,4 @@
+.gradle/
+.settings/
+.project
+build/
diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml
index 092cf16545..21ceaacc2e 100644
--- a/src/android/templates/AndroidManifest.xml
+++ b/src/android/templates/AndroidManifest.xml
@@ -1,84 +1,51 @@
<?xml version="1.0"?>
-<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
- <!-- 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. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example"
+ android:installLocation="auto"
+ android:versionCode="-- %%INSERT_VERSION_CODE%% --"
+ android:versionName="-- %%INSERT_VERSION_NAME%% --">
<!-- %%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 -->
-
- <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
- <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true">
- <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
+ <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="-- %%INSERT_APP_NAME%% --"
+ 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"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- <!-- Application arguments -->
- <!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
- <!-- Application arguments -->
-
- <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%% --"/>
-
- <!-- 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_resource_id" android:resource="@array/load_local_libs"/>
- <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%% --"/>
- <!-- Used to specify custom system library path to run with local system libs -->
- <!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
- <!-- 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"/>
- <meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
- <!-- Messages maps -->
-
- <!-- Splash screen -->
- <!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
- then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
- use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
- are done populating your window with content. -->
- <!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
- <!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
- <!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
- <!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
- <!-- Splash screen -->
-
- <!-- Background running -->
- <!-- Warning: changing this value to true may cause unexpected crashes if the
- application still try to draw after
- "applicationStateChanged(Qt::ApplicationSuspended)"
- signal is sent! -->
- <meta-data android:name="android.app.background_running" android:value="false"/>
- <!-- Background running -->
-
- <!-- auto screen scale factor -->
- <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
- <!-- auto screen scale factor -->
-
- <!-- extract android style -->
- <!-- available android:values :
- * default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
- * full - useful QWidget & Quick Controls 1 apps
- * minimal - useful for Quick Controls 2 apps, it is much faster than "full"
- * none - useful for apps that don't use any of the above Qt modules
- -->
- <meta-data android:name="android.app.extract_android_style" android:value="default"/>
- <!-- extract android style -->
- </activity>
-
- <!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
-
+ <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%% --" />
+ </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/CMakeLists.txt b/src/android/templates/CMakeLists.txt
index 8e007b7215..94f3243c22 100644
--- a/src/android/templates/CMakeLists.txt
+++ b/src/android/templates/CMakeLists.txt
@@ -1,19 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Android template files
-# special case skip regeneration
-# special case begin
set(template_files
- AndroidManifest.xml
- build.gradle)
+ "${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/build.gradle")
set(template_directories
- res)
+ "${CMAKE_CURRENT_SOURCE_DIR}/res")
+
+add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidTemplates
+ SOURCES
+ ${template_files}
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/values/libs.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/xml/qtprovider_paths.xml"
+)
+
+qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/templates")
+
+qt_copy_or_install(FILES ${template_files}
+ DESTINATION "${destination}")
-qt_install(FILES ${template_files}
- DESTINATION src/android/templates
- COMPONENT Devel)
+qt_copy_or_install(DIRECTORY ${template_directories}
+ DESTINATION "${destination}")
-qt_install(DIRECTORY ${template_directories}
- DESTINATION src/android/templates
- COMPONENT Devel)
-# special case end
+if(NOT QT_WILL_INSTALL)
+ qt_internal_copy_at_build_time(TARGET Qt${QtBase_VERSION_MAJOR}AndroidTemplates
+ FILES ${template_files}
+ DIRECTORIES ${template_directories}
+ DESTINATION ${destination}
+ )
+endif()
diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle
index d5b3b93499..f94ffbde54 100644
--- a/src/android/templates/build.gradle
+++ b/src/android/templates/build.gradle
@@ -1,23 +1,24 @@
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.0'
+ classpath 'com.android.tools.build:gradle:7.4.1'
}
}
repositories {
google()
- jcenter()
+ mavenCentral()
}
apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ implementation 'androidx.core:core:1.10.1'
}
android {
@@ -25,7 +26,7 @@ android {
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
- * - qt5AndroidDir - holds the path to qt android files
+ * - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
@@ -34,16 +35,19 @@ android {
* Changing them manually might break the compilation!
*******************************************************/
- compileSdkVersion androidCompileSdkVersion.toInteger()
+ compileSdkVersion androidCompileSdkVersion
+ buildToolsVersion androidBuildToolsVersion
+ ndkVersion androidNdkVersion
- buildToolsVersion '28.0.3'
+ // Extract native libraries from the APK
+ packagingOptions.jniLibs.useLegacyPackaging true
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
- aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
- res.srcDirs = [qt5AndroidDir + '/res', 'res']
+ 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']
@@ -51,6 +55,15 @@ android {
}
}
+ tasks.withType(JavaCompile) {
+ options.incremental = true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
lintOptions {
abortOnError false
}
@@ -62,7 +75,8 @@ android {
defaultConfig {
resConfig "en"
- minSdkVersion = qtMinSdkVersion
- targetSdkVersion = qtTargetSdkVersion
+ minSdkVersion qtMinSdkVersion
+ targetSdkVersion qtTargetSdkVersion
+ ndk.abiFilters = qtTargetAbiList.split(",")
}
}
diff --git a/src/android/templates/doc/src/android-manifest-file-configuration.qdoc b/src/android/templates/doc/src/android-manifest-file-configuration.qdoc
new file mode 100644
index 0000000000..db0d3c7277
--- /dev/null
+++ b/src/android/templates/doc/src/android-manifest-file-configuration.qdoc
@@ -0,0 +1,287 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page android-manifest-file-configuration.html
+\title Qt Android Manifest File Configuration
+\ingroup androidplatform
+\brief Provides details on the AndroidManifest.xml configuration.
+\previouspage android-openssl-support.html
+\nextpage android-services.html
+
+The Android Manifest is an XML file necessary for any Android app. It contains
+app configuration for different settings and features that the app use, as well
+as details on the app itself, such as, package name, app name, version, etc.
+Permissions and hardware features can also be set from the manifest.
+
+Qt for Android maintains a version of \c {AndroidManifest.xml} with default
+configuration that include features, permissions and other configuration
+used by the build system which are needed for building and running Qt apps
+on Android.
+
+\section1 Qt Project to Manifest Configuration
+
+Qt defines some \l {Android: App Manifest <meta-data>}{meta-data} that is passed
+from the build systems and to \l {Deploying an Application on Android}{androiddeployqt}
+which populates the manifest with the correct values without explicitly setting
+these in the manifest file. Such \l {Android: App Manifest <meta-data>}{meta-data}
+is assigned a value in the form \c {"-- %%INSERT_VALUE%% --"}, for example:
+
+\badcode
+<manifest ...
+ android:versionCode="-- %%INSERT_VERSION_CODE%% --"
+ ...
+</manifest>
+\endcode
+
+This would be populated with the version code that is set in, for example,
+\c CMake.
+
+\section1 Qt Default Configuration
+
+Qt sets the following manifest configuration by default:
+
+\table
+\header
+ \li Section
+ \li Option
+ \li Description
+\row
+ \li {1, 5} \l {Android: App Manifest <manifest>}{<manifest>}
+ \li package
+ \li Sets the package name. The default value is \c {org.qtproject.example.app_name}.
+\row
+ \li \c {android:installLocation}
+ \li Sets the app's installation location, whether internal or external storage.
+ The default value is \c auto.
+\row
+ \li android:versionCode
+ \li Sets the internal version code. Populated from \c ANDROID_VERSION_CODE (qmake)
+ and \c QT_ANDROID_VERSION_CODE (CMake). The default value is \c 1.
+\row
+ \li android:versionName
+ \li Sets the public version name. Populated from \c ANDROID_VERSION_NAME (qmake)
+ and \c QT_ANDROID_VERSION_NAME (CMake). The default value is \c {1.0}.
+\row
+ \li \c {<supports-screens>}
+ \li Sets the screen sizes that the app supports,
+ default values are \c anyDensity, \c largeScreens,
+ \c normalScreens, and \c smallScreens.
+\row
+ \li {1, 6} \l {Android: App Manifest <application>}{<application>}
+ \li android:name
+ \li The application class name. Default value is
+ \c {org.qtproject.qt.android.bindings.QtApplication}.
+\row
+ \li android:label
+ \li The application name label. Default value is the Qt project's target name.
+\row
+ \li android:hardwareAccelerated
+ \li Sets hardware acceleration preference. The default value is \c true.
+\row
+ \li android:requestLegacyExternalStorage
+ \li Whether to use Android scoped storage. The default value is \c true.
+\row
+ \li android:allowBackup
+ \li Whether to allow the application to participate in the backup and restore
+ infrastructure. If this is set to \c false, no backup or restore of the
+ application will ever be performed. The default value is \c true.
+\row
+ \li android:fullBackupOnly
+ \li Whether or not to use Auto Backup on devices where it is available.
+ The default value is \c false.
+\row
+ \li {1, 6} \l {Android: App Manifest <activity>}{<activity>}
+ \li android:name
+ \li The activity class name. The default value is \c {org.qtproject.qt.android.bindings.QtActivity}.
+\row
+ \li android:configChanges
+ \li Lists configuration changes that the activity handles. Default value is
+ \c orientation, \c uiMode, \c screenLayout, \c screenSize,
+ \c smallestScreenSize, \c layoutDirection, \c locale, \c fontScale,
+ \c keyboard, \c keyboardHidden, \c navigation, \c mcc, \c mnc, \c density.
+\row
+ \li android:launchMode
+ \li The method used to launch the activity. The default value is \c singleTop.
+\row
+ \li android:screenOrientation
+ \li The orientation of the activity's display on the device. The default value is \c unspecified.
+\row
+ \li <intent-filter>
+ \li Specifies the types of intents that the activity can respond to. Default value is
+ \badcode
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ \endcode
+\row
+ \li android:exported
+ \li Sets whether the activity can be launched by components of other applications.
+ The default value is \c true.
+\endtable
+
+\section1 Qt Specific Meta-data
+
+In addition to the default manifest configuration that Qt sets, Qt defines
+some meta-data that is valid for Qt apps only. Such meta-data is usually
+under the \c <activity> section in the form:
+
+\badcode
+<meta-data
+ android:name="meta-data-name"
+ android:value="meta-data-value" />
+\endcode
+
+The following is a list of such meta-data defined by Qt:
+
+\table
+\header
+ \li Meta-data Name
+ \li Description
+\row
+ \li android.app.lib_name \target android.app.lib_name
+ \li The filename of the native C++ library that is used by the activity.
+ \note This attribute is mandatory and shouldn't be removed.
+ Default value is the Qt project's target name.
+\row
+ \li android.app.extract_android_style
+ \li The method used to extract the native Android Style information.
+ For more information, see \l {Style Extraction}.
+ The default value is \c minimal.
+\row
+ \li android.app.background_running \target android.app.background_running
+ \li Sets whether the app keeps running tasks in the background.
+ Setting this to \c true is the equivalent of setting the environment
+ variable \c QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED to \c 0.
+ The default value is \c false.
+
+ \warning Setting this to \c true may cause unexpected crash if the
+ application tries to draw after \l {QGuiApplication::applicationStateChanged()}
+ signal is sent with a \l {Qt::ApplicationSuspended} state.
+\row
+ \li android.app.arguments \target android.app.arguments
+ \li Sets a list of arguments to pass to the app \c {"arg1 arg2"}.
+ Populated from \c ANDROID_APPLICATION_ARGUMENTS (qmake) and
+ \c QT_ANDROID_APPLICATION_ARGUMENTS (CMake).
+ Default value is not set.
+\row
+ \li android.app.splash_screen_drawable_portrait
+ \li Sets a drawable for a splash screen specific to portrait mode.
+ For example: \c {android:resource="@drawable/splash_portrait"}.
+ Default value is not set.
+\row
+ \li android.app.splash_screen_drawable_landscape
+ \li Sets a drawable for a splash screen specific to landscape mode.
+ For example: \c {android:resource="@drawable/splash_landscape"}.
+ Default value is not set.
+\row
+ \li android.app.splash_screen_drawable
+ \li Sets a drawable for a splash screen at the start of the app.
+ \note Orientation specific splash screens are checked first,
+ if not set, this is used instead.
+ For example: \c {android:resource="@drawable/splash"}.
+ Default value is not set.
+\row
+ \li android.app.splash_screen_sticky
+ \li Sets whether the splash screen stays visible until explicitly hidden
+ by the app.
+ For more information, see
+ \l {QNativeInterface::}{QAndroidApplication::hideSplashScreen()}.
+\row
+ \li android.app.trace_location
+ \li Specifies a location on device where the application can save tracing files.
+ For example: /storage/emulated/0/Android/data/<app_package_name>/files/.
+ This is needed when using Common Trace Format (CTF) tracing backend.
+ \note The application needs storage permission for the location.
+ Default: not set.
+\endtable
+
+\section2 Application Specific Meta-data
+
+Some meta-data attributes are application-wide, and should be placed under
+the \c <application> section:
+
+\table
+\header
+ \li Meta-data Name
+ \li Description
+\row
+ \li android.app.system_libs_prefix \target android.app.system_libs_prefix
+ \li Specifies a custom system library path to use for library loading lookup.
+ This is necessary when using Qt libraries installed outside an app's
+ default native (JNI) library directory.
+ The default value is \c {/system/lib/}.
+\endtable
+
+\section2 Meta-data in Services
+
+Some meta-data attributes can also be used in \l {Android Services}{Services}.
+The main ones are:
+
+\list
+ \li \l {android.app.lib_name}
+ \li \l {android.app.background_running}
+ \li \l {android.app.arguments}
+\endlist
+
+\section2 Qt Permissions and Features
+
+Different Qt modules might require some Android permissions or features to
+function properly, for example, Camera permission in \l {QtMultimedia}.
+l{The androiddeployqt Tool} takes care of including such requirements into the
+Android manifest during the build. Qt defines the following lines into the
+manifest, which they get replaced by
+the actual values:
+
+\badcode
+<manifest ...
+ <!-- %%INSERT_PERMISSIONS -->
+ <!-- %%INSERT_FEATURES -->
+ ...
+</manifest>
+\endcode
+
+\note If those lines are removed from the project manifest, Qt won't be
+able to include the correct permissions. So some functionalities
+might not work properly.
+
+\section2 Style Extraction
+
+Qt uses different methods to determine how Qt Widgets and Qt Quick Controls
+should be styled:
+
+\list
+ \li \c default or \c full: when using Qt Widgets or Qt Quick Controls 1.
+ \note This method uses some Android non-SDK interfaces, that are being
+ restricted and removed by Google starting from Android 9.0 (API 28).
+ For that reason, this is not recommended for Android 9.0 or greater.
+ \li \c minimal: when using Qt Quick Controls 2 and no Qt Widgets or Qt Quick
+ Controls 1. This is faster than using the default or full options.
+ \li \c none: no style extraction.
+\endlist
+
+\section1 Qt Manifest before 6.2 Release
+
+Versions of Qt earlier than 6.2 used to have an additional set of meta-data
+defined by Qt. These attributes used to manage dependencies and some were
+used by the discontinued \c Ministro service. With Qt 6.2, they should be removed.
+Here is a list of these attributes:
+
+\list
+ \li android.app.qt_sources_resource_id
+ \li android.app.repository
+ \li android.app.bundled_libs_resource_id
+ \li android.app.bundle_local_qt_libs
+ \li android.app.use_local_qt_libs
+ \li android.app.libs_prefix
+ \li android.app.load_local_libs_resource_id
+ \li android.app.load_local_jars
+ \li android.app.static_init_classes
+ \li android.app.qt_libs_resource_id
+ \li android.app.ministro_not_found_msg
+ \li android.app.ministro_needed_msg
+ \li android.app.fatal_error_msg
+\endlist
+
+For more information on the Android Manifest, see
+\l{Android: App Manifest}{Android App Manifest}.
+*/
diff --git a/src/android/templates/res/values/libs.xml b/src/android/templates/res/values/libs.xml
index 6b1a4a2a02..fe63866f72 100644
--- a/src/android/templates/res/values/libs.xml
+++ b/src/android/templates/res/values/libs.xml
@@ -1,11 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <array name="qt_sources">
- <item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
- </array>
-
- <!-- The following is handled automatically by the deployment tool. It should
- not be edited manually. -->
+ <!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
@@ -19,4 +14,8 @@
<!-- %%INSERT_LOCAL_LIBS%% -->
</array>
+ <string name="static_init_classes"><!-- %%INSERT_INIT_CLASSES%% --></string>
+ <string name="use_local_qt_libs"><!-- %%USE_LOCAL_QT_LIBS%% --></string>
+ <string name="bundle_local_qt_libs"><!-- %%BUNDLE_LOCAL_QT_LIBS%% --></string>
+ <string name="system_libs_prefix"><!-- %%SYSTEM_LIBS_PREFIX%% --></string>
</resources>
diff --git a/src/android/templates/res/xml/qtprovider_paths.xml b/src/android/templates/res/xml/qtprovider_paths.xml
new file mode 100644
index 0000000000..ae5b4b6074
--- /dev/null
+++ b/src/android/templates/res/xml/qtprovider_paths.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <files-path name="files_path" path="/"/>
+</paths>
diff --git a/src/android/templates/templates.pro b/src/android/templates/templates.pro
deleted file mode 100644
index 9a64251ee3..0000000000
--- a/src/android/templates/templates.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-CONFIG += single_arch
-
-CONFIG -= qt android_install
-
-templates.files = \
- $$PWD/AndroidManifest.xml \
- $$PWD/build.gradle \
- $$PWD/res
-
-templates.path = $$[QT_INSTALL_PREFIX]/src/android/templates
-
-INSTALLS += templates
-
-!prefix_build:!equals(OUT_PWD, $$PWD) {
- RETURN = $$escape_expand(\\n\\t)
- equals(QMAKE_HOST.os, Windows) {
- RETURN = $$escape_expand(\\r\\n\\t)
- }
- OUT_PATH = $$shell_path($$OUT_PWD)
-
- QMAKE_POST_LINK += \
- $${QMAKE_COPY} $$shell_path($$PWD/AndroidManifest.xml) $$OUT_PATH $$RETURN \
- $${QMAKE_COPY} $$shell_path($$PWD/build.gradle) $$OUT_PATH $$RETURN \
- $${QMAKE_COPY_DIR} $$shell_path($$PWD/res) $$OUT_PATH
-}