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 | 1390 |
1 files changed, 1390 insertions, 0 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 new file mode 100644 index 0000000000..40b3a79bf0 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -0,0 +1,1390 @@ +/**************************************************************************** +** +** 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.qt.android; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +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.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 android.provider.DocumentsContract; + +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 HashMap<String, Uri> m_cachedUris = new HashMap<String, Uri>(); + private static ArrayList<String> m_knownDirs = new ArrayList<String>(); + + 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; + } + + // 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; + } 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"); + 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(); + 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; + } + } + + 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"); + + if (uri == null) { + Log.e(QtTAG, "getSize(): No permissions to open Uri"); + return size; + } else { + m_cachedUris.putIfAbsent(contentUrl, uri); + } + + 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 boolean checkFileExists(Context context, String contentUrl) + { + 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; + } + } + + public static boolean checkIfDir(Context context, String contentUrl) + { + 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; + } + } + public static String[] listContentsFromTreeUri(Context context, String contentUrl) + { + 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); + } + 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); + } + } + } + }); + } + + // 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); + } + } + } + }); + } + + 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]; + } + + 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) { + e.printStackTrace(); + } + } + } + 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) { + e.printStackTrace(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + 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 +} |