diff options
Diffstat (limited to 'src/android/jar/src/org/qtproject/qt/android/QtNative.java')
-rw-r--r-- | src/android/jar/src/org/qtproject/qt/android/QtNative.java | 1387 |
1 files changed, 202 insertions, 1185 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java index d221ff602e..b2a2887ad5 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -1,134 +1,56 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 java.io.File; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Objects; -import java.util.concurrent.Semaphore; -import java.io.IOException; -import java.util.HashMap; - 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.ApplicationInfo; import android.content.UriPermission; -import android.graphics.Rect; +import android.content.pm.PackageManager; 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.util.DisplayMetrics; 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 android.provider.DocumentsContract; -import java.lang.reflect.Method; +import java.lang.ref.WeakReference; import java.security.KeyStore; import java.security.cert.X509Certificate; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import javax.net.ssl.TrustManagerFactory; +import java.util.Objects; + import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -public class QtNative +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 boolean m_isKeyboardHiding = 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 HashMap<String, Uri> m_cachedUris = new HashMap<String, Uri>(); - private static ArrayList<String> m_knownDirs = new ArrayList<String>(); - private static final int KEYBOARD_HEIGHT_THRESHOLD = 100; - - private static final Runnable runPendingCppRunnablesRunnable = new Runnable() { - @Override - public void run() { - runPendingCppRunnables(); - } - }; + 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; @@ -139,48 +61,80 @@ public class QtNative m_classLoader = classLoader; } - public static Activity activity() + public static void setActivity(Activity qtMainActivity) { synchronized (m_mainActivityMutex) { - return m_activity; + m_activity = new WeakReference<>(qtMainActivity); } } - public static Service service() + public static void setService(Service qtMainService) { synchronized (m_mainActivityMutex) { - return m_service; + m_service = new WeakReference<>(qtMainService); } } - - public static QtActivityDelegate activityDelegate() + @UsedFromNativeCode + public static Activity activity() { synchronized (m_mainActivityMutex) { - return m_activityDelegate; + return m_activity != null ? m_activity.get() : null; } } - public static QtServiceDelegate serviceDelegate() + public static boolean isActivityValid() + { + return m_activity != null && m_activity.get() != null; + } + + @UsedFromNativeCode + public static Service service() { synchronized (m_mainActivityMutex) { - return m_serviceDelegate; + 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 { - Uri parsedUri = Uri.parse(uri); String scheme = parsedUri.getScheme(); // We only want to check permissions for content Uris - if (scheme.compareTo("content") != 0) + if (scheme != null && scheme.compareTo("content") != 0) return parsedUri; List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions(); @@ -188,32 +142,31 @@ public class QtNative for (int i = 0; i < permissions.size(); ++i) { Uri iterUri = permissions.get(i).getUri(); - boolean isRightPermission = permissions.get(i).isReadPermission(); + boolean isRequestPermission = permissions.get(i).isReadPermission(); if (!openMode.equals("r")) - isRightPermission = permissions.get(i).isWritePermission(); + isRequestPermission = permissions.get(i).isWritePermission(); - if (iterUri.getPath().equals(uriStr) && isRightPermission) + if (Objects.equals(iterUri.getPath(), uriStr) && isRequestPermission) return iterUri; } - // Android 6 and earlier could still manage to open the file so we can return the - // parsed uri here - if (Build.VERSION.SDK_INT < 24) - return parsedUri; - return null; + // 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) { - e.printStackTrace(); - return null; + Log.e(QtTAG, getCurrentMethodNameLog() + e); + return parsedUri; } } + @UsedFromNativeCode public static boolean openURL(Context context, String url, String mime) { - Uri uri = getUriWithValidPermission(context, url, "r"); - + final Uri uri = getUriWithValidPermission(context, url, "r"); if (uri == null) { - Log.e(QtTAG, "openURL(): No permissions to open Uri"); + Log.e(QtTAG, getCurrentMethodNameLog() + "received invalid/null Uri"); return false; } @@ -223,353 +176,137 @@ public class QtNative if (!mime.isEmpty()) intent.setDataAndType(uri, mime); - activity().startActivity(intent); + Activity activity = activity(); + if (activity == null) { + Log.w(QtTAG, "openURL(): The activity reference is null"); + return false; + } + + activity.startActivity(intent); return true; - } catch (IllegalArgumentException e) { - Log.e(QtTAG, "openURL(): Invalid Uri"); - e.printStackTrace(); - return false; - } catch (UnsupportedOperationException e) { - Log.e(QtTAG, "openURL(): Unsupported operation for given Uri"); - e.printStackTrace(); - return false; } catch (Exception e) { - e.printStackTrace(); + Log.e(QtTAG, getCurrentMethodNameLog() + e); return false; } } - public static int openFdForContentUrl(Context context, String contentUrl, String openMode) - { - Uri uri = m_cachedUris.get(contentUrl); - if (uri == null) - 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) { - e.printStackTrace(); - return error; - } catch (IllegalArgumentException e) { - Log.e(QtTAG, "openFdForContentUrl(): Invalid Uri"); - e.printStackTrace(); - return error; - } + static QtThread getQtThread() { + return m_qtThread; } - public static long getSize(Context context, String contentUrl) - { - long size = -1; - Uri uri = m_cachedUris.get(contentUrl); - if (uri == null) - uri = getUriWithValidPermission(context, contentUrl, "r"); + interface AppStateDetailsListener { + default void onAppStateDetailsChanged(ApplicationStateDetails details) {} + default void onNativePluginIntegrationReadyChanged(boolean ready) {} + } - if (uri == null) { - Log.e(QtTAG, "getSize(): No permissions to open Uri"); - return size; - } else if (!m_cachedUris.containsKey(contentUrl)) { - m_cachedUris.put(contentUrl, uri); - } + // 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; + } - try { - ContentResolver resolver = context.getContentResolver(); - Cursor cur = resolver.query(uri, new String[] { DocumentsContract.Document.COLUMN_SIZE }, null, null, null); - if (cur != null) { - if (cur.moveToFirst()) - size = cur.getLong(0); - cur.close(); - } - return size; - } catch (IllegalArgumentException e) { - Log.e(QtTAG, "getSize(): Invalid Uri"); - e.printStackTrace(); - return size; - } catch (UnsupportedOperationException e) { - Log.e(QtTAG, "getSize(): Unsupported operation for given Uri"); - e.printStackTrace(); - return size; - } + public static class ApplicationStateDetails { + int state = ApplicationState.ApplicationSuspended; + boolean nativePluginIntegrationReady = false; + boolean isStarted = false; } - public static boolean checkFileExists(Context context, String contentUrl) + public static ApplicationStateDetails getStateDetails() { - boolean exists = false; - Uri uri = m_cachedUris.get(contentUrl); - if (uri == null) - uri = getUriWithValidPermission(context, contentUrl, "r"); - if (uri == null) { - Log.e(QtTAG, "checkFileExists(): No permissions to open Uri"); - return exists; - } else { - if (!m_cachedUris.containsKey(contentUrl)) - m_cachedUris.put(contentUrl, uri); - } - - 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"); - e.printStackTrace(); - return exists; - } catch (UnsupportedOperationException e) { - Log.e(QtTAG, "checkFileExists(): Unsupported operation for given Uri"); - e.printStackTrace(); - return false; - } + return m_stateDetails; } - public static boolean checkIfWritable(Context context, String contentUrl) + public static void setStarted(boolean started) { - return getUriWithValidPermission(context, contentUrl, "w") != null; + m_stateDetails.isStarted = started; + notifyAppStateDetailsChanged(m_stateDetails); } - public static boolean checkIfDir(Context context, String contentUrl) + @UsedFromNativeCode + public static void notifyNativePluginIntegrationReady(boolean ready) { - boolean isDir = false; - Uri uri = m_cachedUris.get(contentUrl); - if (m_knownDirs.contains(contentUrl)) - return true; - if (uri == null) { - uri = getUriWithValidPermission(context, contentUrl, "r"); - } - if (uri == null) { - Log.e(QtTAG, "isDir(): No permissions to open Uri"); - return isDir; - } else { - if (!m_cachedUris.containsKey(contentUrl)) - m_cachedUris.put(contentUrl, uri); - } - - try { - final List<String> paths = uri.getPathSegments(); - // getTreeDocumentId will throw an exception if it is not a directory so check manually - if (!paths.get(0).equals("tree")) - return false; - ContentResolver resolver = context.getContentResolver(); - Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri)); - if (!docUri.toString().startsWith(uri.toString())) - return false; - Cursor cur = resolver.query(docUri, new String[] { DocumentsContract.Document.COLUMN_MIME_TYPE }, null, null, null); - if (cur != null) { - if (cur.moveToFirst()) { - final String dirStr = new String(DocumentsContract.Document.MIME_TYPE_DIR); - isDir = cur.getString(0).equals(dirStr); - if (isDir) - m_knownDirs.add(contentUrl); - } - cur.close(); - } - return isDir; - } catch (IllegalArgumentException e) { - Log.e(QtTAG, "checkIfDir(): Invalid Uri"); - e.printStackTrace(); - return false; - } catch (UnsupportedOperationException e) { - Log.e(QtTAG, "checkIfDir(): Unsupported operation for given Uri"); - e.printStackTrace(); - return false; - } + m_stateDetails.nativePluginIntegrationReady = ready; + notifyNativePluginIntegrationReadyChanged(ready); + notifyAppStateDetailsChanged(m_stateDetails); } - public static String[] listContentsFromTreeUri(Context context, String contentUrl) + + public static void setApplicationState(int state) { - Uri treeUri = Uri.parse(contentUrl); - final ArrayList<String> results = new ArrayList<String>(); - if (treeUri == null) { - Log.e(QtTAG, "listContentsFromTreeUri(): Invalid uri"); - return results.toArray(new String[results.size()]); - } - final ContentResolver resolver = context.getContentResolver(); - final Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, - DocumentsContract.getTreeDocumentId(treeUri)); - final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(docUri, - DocumentsContract.getDocumentId(docUri)); - Cursor c = null; - final String dirStr = new String(DocumentsContract.Document.MIME_TYPE_DIR); - try { - c = resolver.query(childrenUri, new String[] { - DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_MIME_TYPE }, null, null, null); - while (c.moveToNext()) { - final String fileString = c.getString(1); - if (!m_cachedUris.containsKey(contentUrl + "/" + fileString)) { - m_cachedUris.put(contentUrl + "/" + fileString, - DocumentsContract.buildDocumentUriUsingTree(treeUri, c.getString(0))); - } - results.add(fileString); - if (c.getString(2).equals(dirStr)) - m_knownDirs.add(contentUrl + "/" + fileString); + synchronized (m_mainActivityMutex) { + m_stateDetails.state = state; + if (state == ApplicationState.ApplicationActive) { + for (Runnable mLostAction : m_lostActions) + runAction(mLostAction); + m_lostActions.clear(); } - c.close(); - } catch (Exception e) { - Log.w(QtTAG, "Failed query: " + e); - return results.toArray(new String[results.size()]); } - return results.toArray(new String[results.size()]); - } - // 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); - } - } - } - }); + updateApplicationState(state); + notifyAppStateDetailsChanged(m_stateDetails); } - // 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 { - ApplicationInfo info = getContext().getApplicationContext().getPackageManager() - .getApplicationInfo(getContext().getPackageName(), 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) { - e.printStackTrace(); - } - } - 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); - } - } - } - }); + static void registerAppStateListener(AppStateDetailsListener listener) { + synchronized (m_appStateListenersLock) { + if (!m_appStateListeners.contains(listener)) + m_appStateListeners.add(listener); + } } - 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 { - ApplicationInfo info = getContext().getApplicationContext().getPackageManager() - .getApplicationInfo(getContext().getPackageName(), 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]; + static void unregisterAppStateListener(AppStateDetailsListener listener) { + synchronized (m_appStateListenersLock) { + m_appStateListeners.remove(listener); + } } - public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate) - { - synchronized (m_mainActivityMutex) { - m_activity = qtMainActivity; - m_activityDelegate = qtActivityDelegate; + static void notifyNativePluginIntegrationReadyChanged(boolean ready) { + synchronized (m_appStateListenersLock) { + for (final AppStateDetailsListener listener : m_appStateListeners) + listener.onNativePluginIntegrationReadyChanged(ready); } } - public static void setService(Service qtMainService, QtServiceDelegate qtServiceDelegate) - { - synchronized (m_mainActivityMutex) { - m_service = qtMainService; - m_serviceDelegate = qtServiceDelegate; + static void notifyAppStateDetailsChanged(ApplicationStateDetails details) { + synchronized (m_appStateListenersLock) { + for (AppStateDetailsListener listener : m_appStateListeners) + listener.onAppStateDetailsChanged(details); } } - public static void setApplicationState(int state) + // 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) { - 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); + runAction(action, true); } - private static void runAction(Runnable action) + public static void runAction(Runnable action, boolean queueWhenInactive) { 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); + + 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 (m_activity != null) { - if (!m_activityPaused) - m_activity.runOnUiThread(runPendingCppRunnablesRunnable); + if (isActivityValid()) { + if (m_stateDetails.state == ApplicationState.ApplicationActive) + m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable); else runAction(runPendingCppRunnablesRunnable); } else { @@ -585,639 +322,54 @@ public class QtNative } } + @UsedFromNativeCode private static void setViewVisibility(final View view, final boolean visible) { - runAction(new Runnable() { - @Override - public void run() { - view.setVisibility(visible ? View.VISIBLE : View.GONE); - } - }); + runAction(() -> view.setVisibility(visible ? View.VISIBLE : View.GONE)); } - public static boolean startApplication(String params, final String environment, String mainLib) throws Exception + public static void startApplication(String params, String mainLib) { - 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(); - } + 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_started = true; + m_stateDetails.isStarted = true; + notifyAppStateDetailsChanged(m_stateDetails); } - 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) + public static void quitApp() { - /* 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(); - } + runAction(() -> { + quitQtAndroidPlugin(); + if (isActivityValid()) + m_activity.get().finish(); + if (isServiceValid()) + m_service.get().stopSelf(); + m_stateDetails.isStarted = false; + notifyAppStateDetailsChanged(m_stateDetails); }); } - //@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_CANCEL || 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; - } - + @UsedFromNativeCode public static int checkSelfPermission(String permission) { - int perm = PackageManager.PERMISSION_DENIED; synchronized (m_mainActivityMutex) { Context context = getContext(); PackageManager pm = context.getPackageManager(); - perm = pm.checkPermission(permission, context.getPackageName()); + return pm.checkPermission(permission, context.getPackageName()); } - - 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 int getSelectHandleWidth() - { - return m_activityDelegate.getSelectHandleWidth(); - } - - 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 updateInputItemRectangle(final int x, - final int y, - final int w, - final int h) - { - runAction(new Runnable() { - @Override - public void run() { - m_activityDelegate.updateInputItemRectangle(x, y, w, h); - } - }); - } - - - private static void showSoftwareKeyboard(final int x, - final int y, - final int width, - final int height, - final int editorHeight, - final int inputHints, - final int enterKeyType) - { - runAction(new Runnable() { - @Override - public void run() { - if (m_activityDelegate != null) - m_activityDelegate.showSoftwareKeyboard(x, y, width, height, editorHeight, inputHints, enterKeyType); - } - }); - } - - private static void resetSoftwareKeyboard() - { - runAction(new Runnable() { - @Override - public void run() { - if (m_activityDelegate != null) - m_activityDelegate.resetSoftwareKeyboard(); - } - }); - } - - private static void hideSoftwareKeyboard() - { - m_isKeyboardHiding = true; - 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(); - } - }); - } - - public static boolean isSoftwareKeyboardVisible() - { - return m_activityDelegate.isKeyboardVisible() && !m_isKeyboardHiding; - } - - private static void notifyAccessibilityLocationChange() - { - runAction(new Runnable() { - @Override - public void run() { - if (m_activityDelegate != null) { - m_activityDelegate.notifyAccessibilityLocationChange(); - } - } - }); - } - - private static void notifyObjectHide(final int viewId) - { - runAction(new Runnable() { - @Override - public void run() { - if (m_activityDelegate != null) { - m_activityDelegate.notifyObjectHide(viewId); - } - } - }); - } - - private static void notifyObjectFocus(final int viewId) - { - runAction(new Runnable() { - @Override - public void run() { - if (m_activityDelegate != null) { - m_activityDelegate.notifyObjectFocus(viewId); - } - } - }); - } - - public static void notifyQtAndroidPluginRunning(final boolean running) - { - m_activityDelegate.notifyQtAndroidPluginRunning(running); - } - - 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_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) { - Objects.requireNonNull(clip).addItem(m_activity.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(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 < Objects.requireNonNull(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(); - } - }); } + @UsedFromNativeCode private static byte[][] getSSLCertificates() { - ArrayList<byte[]> certificateList = new ArrayList<byte[]>(); + ArrayList<byte[]> certificateList = new ArrayList<>(); try { TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); @@ -1228,7 +380,7 @@ public class QtNative X509TrustManager trustManager = (X509TrustManager) manager; for (X509Certificate certificate : trustManager.getAcceptedIssuers()) { - byte buffer[] = certificate.getEncoded(); + byte[] buffer = certificate.getEncoded(); certificateList.add(buffer); } } @@ -1242,105 +394,13 @@ public class QtNative 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); - } - }); - } - - public static void keyboardVisibilityUpdated(boolean visibility) - { - m_isKeyboardHiding = false; - keyboardVisibilityChanged(visibility); - } - + @UsedFromNativeCode private static String[] listAssetContent(android.content.res.AssetManager asset, String path) { String [] list; - ArrayList<String> res = new ArrayList<String>(); + ArrayList<String> res = new ArrayList<>(); try { list = asset.list(path); - if (list.length > 0) { + if (list != null) { for (String file : list) { try { String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file); @@ -1355,60 +415,21 @@ public class QtNative } catch (Exception e) { e.printStackTrace(); } - return res.toArray(new String[res.size()]); + return res.toArray(new String[0]); } - // 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 + // 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, int w, int h); + public static native void setSurface(int id, Object surface); // surface methods // window methods @@ -1429,10 +450,6 @@ public class QtNative 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); |