1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the Android port of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 package org.qtproject.qt5.android;
42 
43 import java.io.File;
44 import java.io.FileNotFoundException;
45 import java.util.ArrayList;
46 import java.util.Objects;
47 import java.util.concurrent.Semaphore;
48 import java.io.IOException;
49 import java.util.HashMap;
50 
51 import android.app.Activity;
52 import android.app.Service;
53 import android.content.ActivityNotFoundException;
54 import android.content.Context;
55 import android.content.ContentResolver;
56 import android.content.Intent;
57 import android.content.pm.PackageManager;
58 import android.content.pm.ApplicationInfo;
59 import android.content.UriPermission;
60 import android.net.Uri;
61 import android.os.Build;
62 import android.os.Handler;
63 import android.os.IBinder;
64 import android.os.Looper;
65 import android.content.ClipboardManager;
66 import android.content.ClipboardManager.OnPrimaryClipChangedListener;
67 import android.content.ClipData;
68 import android.os.ParcelFileDescriptor;
69 import android.util.Log;
70 import android.view.ContextMenu;
71 import android.view.KeyEvent;
72 import android.view.Menu;
73 import android.view.MotionEvent;
74 import android.view.View;
75 import android.view.InputDevice;
76 import android.database.Cursor;
77 import android.provider.DocumentsContract;
78 
79 import java.lang.reflect.Method;
80 import java.security.KeyStore;
81 import java.security.cert.X509Certificate;
82 import java.util.Iterator;
83 import java.util.List;
84 import javax.net.ssl.TrustManagerFactory;
85 import javax.net.ssl.TrustManager;
86 import javax.net.ssl.X509TrustManager;
87 
88 public class QtNative
89 {
90     private static Activity m_activity = null;
91     private static boolean m_activityPaused = false;
92     private static Service m_service = null;
93     private static QtActivityDelegate m_activityDelegate = null;
94     private static QtServiceDelegate m_serviceDelegate = null;
95     public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
96 
97     public static final String QtTAG = "Qt JAVA"; // string used for Log.x
98     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.)
99     private static boolean m_started = false;
100     private static int m_displayMetricsScreenWidthPixels = 0;
101     private static int m_displayMetricsScreenHeightPixels = 0;
102     private static int m_displayMetricsDesktopWidthPixels = 0;
103     private static int m_displayMetricsDesktopHeightPixels = 0;
104     private static double m_displayMetricsXDpi = .0;
105     private static double m_displayMetricsYDpi = .0;
106     private static double m_displayMetricsScaledDensity = 1.0;
107     private static double m_displayMetricsDensity = 1.0;
108     private static int m_oldx, m_oldy;
109     private static final int m_moveThreshold = 0;
110     private static ClipboardManager m_clipboardManager = null;
111     private static Method m_checkSelfPermissionMethod = null;
112     private static Boolean m_tabletEventSupported = null;
113     private static boolean m_usePrimaryClip = false;
114     public static QtThread m_qtThread = new QtThread();
115     private static HashMap<String, Uri> m_cachedUris = new HashMap<String, Uri>();
116     private static ArrayList<String> m_knownDirs = new ArrayList<String>();
117     private static final String NoPermissionErrorMessage = "No permissions to open Uri";
118 
119     private static final Runnable runPendingCppRunnablesRunnable = new Runnable() {
120         @Override
121         public void run() {
122             runPendingCppRunnables();
123         }
124     };
125 
126     private static ClassLoader m_classLoader = null;
classLoader()127     public static ClassLoader classLoader()
128     {
129         return m_classLoader;
130     }
131 
setClassLoader(ClassLoader classLoader)132     public static void setClassLoader(ClassLoader classLoader)
133     {
134             m_classLoader = classLoader;
135     }
136 
activity()137     public static Activity activity()
138     {
139         synchronized (m_mainActivityMutex) {
140             return m_activity;
141         }
142     }
143 
service()144     public static Service service()
145     {
146         synchronized (m_mainActivityMutex) {
147             return m_service;
148         }
149     }
150 
151 
activityDelegate()152     public static QtActivityDelegate activityDelegate()
153     {
154         synchronized (m_mainActivityMutex) {
155             return m_activityDelegate;
156         }
157     }
158 
serviceDelegate()159     public static QtServiceDelegate serviceDelegate()
160     {
161         synchronized (m_mainActivityMutex) {
162             return m_serviceDelegate;
163         }
164     }
165 
getStringArray(String joinedString)166     public static String[] getStringArray(String joinedString)
167     {
168         return joinedString.split(",");
169     }
170 
getUriWithValidPermission(Context context, String uri, String openMode)171     private static Uri getUriWithValidPermission(Context context, String uri, String openMode)
172     {
173         try {
174             Uri parsedUri = Uri.parse(uri);
175             String scheme = parsedUri.getScheme();
176 
177             // We only want to check permissions for content Uris
178             if (scheme.compareTo("content") != 0)
179                 return parsedUri;
180 
181             List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions();
182             String uriStr = parsedUri.getPath();
183 
184             for (int i = 0; i < permissions.size(); ++i) {
185                 Uri iterUri = permissions.get(i).getUri();
186                 boolean isRightPermission = permissions.get(i).isReadPermission();
187 
188                 if (!openMode.equals("r"))
189                    isRightPermission = permissions.get(i).isWritePermission();
190 
191                 if (iterUri.getPath().equals(uriStr) && isRightPermission)
192                     return iterUri;
193             }
194 
195             // if we only have transient permissions on uri all the above will fail,
196             // but we will be able to read the file anyway, so continue with uri here anyway
197             // and check for SecurityExceptions later
198             return parsedUri;
199         } catch (SecurityException e) {
200             e.printStackTrace();
201             return null;
202         }
203     }
204 
openURL(Context context, String url, String mime)205     public static boolean openURL(Context context, String url, String mime)
206     {
207         Uri uri = getUriWithValidPermission(context, url, "r");
208 
209         if (uri == null) {
210             Log.e(QtTAG, "openURL(): No permissions to open Uri");
211             return false;
212         }
213 
214         try {
215             Intent intent = new Intent(Intent.ACTION_VIEW, uri);
216             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
217             if (!mime.isEmpty())
218                 intent.setDataAndType(uri, mime);
219 
220             activity().startActivity(intent);
221 
222             return true;
223         } catch (IllegalArgumentException e) {
224             Log.e(QtTAG, "openURL(): Invalid Uri");
225             e.printStackTrace();
226             return false;
227         } catch (UnsupportedOperationException e) {
228             Log.e(QtTAG, "openURL(): Unsupported operation for given Uri");
229             e.printStackTrace();
230             return false;
231         } catch (Exception e) {
232             e.printStackTrace();
233             return false;
234         }
235     }
236 
openFdForContentUrl(Context context, String contentUrl, String openMode)237     public static int openFdForContentUrl(Context context, String contentUrl, String openMode)
238     {
239         Uri uri = m_cachedUris.get(contentUrl);
240         if (uri == null)
241             uri = getUriWithValidPermission(context, contentUrl, openMode);
242         int error = -1;
243 
244         if (uri == null) {
245             Log.e(QtTAG, "openFdForContentUrl(): " + NoPermissionErrorMessage);
246             return error;
247         }
248 
249         try {
250             ContentResolver resolver = context.getContentResolver();
251             ParcelFileDescriptor fdDesc = resolver.openFileDescriptor(uri, openMode);
252             return fdDesc.detachFd();
253         } catch (FileNotFoundException e) {
254             e.printStackTrace();
255         } catch (IllegalArgumentException e) {
256             Log.e(QtTAG, "openFdForContentUrl(): Invalid Uri");
257             e.printStackTrace();
258         } catch (SecurityException e) {
259             Log.e(QtTAG, NoPermissionErrorMessage);
260         }
261         return error;
262     }
263 
getSize(Context context, String contentUrl)264     public static long getSize(Context context, String contentUrl)
265     {
266         long size = -1;
267         Uri uri = m_cachedUris.get(contentUrl);
268         if (uri == null)
269             uri = getUriWithValidPermission(context, contentUrl, "r");
270 
271         if (uri == null) {
272             Log.e(QtTAG, NoPermissionErrorMessage);
273             return size;
274         } else if (!m_cachedUris.containsKey(contentUrl)) {
275             m_cachedUris.put(contentUrl, uri);
276         }
277 
278         try {
279             ContentResolver resolver = context.getContentResolver();
280             Cursor cur = resolver.query(uri, new String[] { DocumentsContract.Document.COLUMN_SIZE }, null, null, null);
281             if (cur != null) {
282                 if (cur.moveToFirst())
283                     size = cur.getLong(0);
284                 cur.close();
285             }
286             return size;
287         } catch (IllegalArgumentException e) {
288             Log.e(QtTAG, "getSize(): Invalid Uri");
289             e.printStackTrace();
290         }  catch (UnsupportedOperationException e) {
291             Log.e(QtTAG, "getSize(): Unsupported operation for given Uri");
292             e.printStackTrace();
293         } catch (SecurityException e) {
294             Log.e(QtTAG, NoPermissionErrorMessage);
295         }
296         return size;
297     }
298 
checkFileExists(Context context, String contentUrl)299     public static boolean checkFileExists(Context context, String contentUrl)
300     {
301         boolean exists = false;
302         Uri uri = m_cachedUris.get(contentUrl);
303         if (uri == null)
304             uri = getUriWithValidPermission(context, contentUrl, "r");
305         if (uri == null) {
306             Log.e(QtTAG, NoPermissionErrorMessage);
307             return exists;
308         } else {
309             if (!m_cachedUris.containsKey(contentUrl))
310                 m_cachedUris.put(contentUrl, uri);
311         }
312 
313         try {
314             ContentResolver resolver = context.getContentResolver();
315             Cursor cur = resolver.query(uri, null, null, null, null);
316             if (cur != null) {
317                 exists = true;
318                 cur.close();
319             }
320             return exists;
321         } catch (IllegalArgumentException e) {
322             Log.e(QtTAG, "checkFileExists(): Invalid Uri");
323             e.printStackTrace();
324         } catch (UnsupportedOperationException e) {
325             Log.e(QtTAG, "checkFileExists(): Unsupported operation for given Uri");
326             e.printStackTrace();
327         } catch (SecurityException e) {
328             Log.e(QtTAG, NoPermissionErrorMessage);
329         }
330         return exists;
331     }
332 
checkIfWritable(Context context, String contentUrl)333     public static boolean checkIfWritable(Context context, String contentUrl)
334     {
335         return getUriWithValidPermission(context, contentUrl, "w") != null;
336     }
337 
checkIfDir(Context context, String contentUrl)338     public static boolean checkIfDir(Context context, String contentUrl)
339     {
340         boolean isDir = false;
341         Uri uri = m_cachedUris.get(contentUrl);
342         if (m_knownDirs.contains(contentUrl))
343             return true;
344         if (uri == null) {
345             uri = getUriWithValidPermission(context, contentUrl, "r");
346         }
347         if (uri == null) {
348             Log.e(QtTAG, NoPermissionErrorMessage);
349             return isDir;
350         } else {
351             if (!m_cachedUris.containsKey(contentUrl))
352                 m_cachedUris.put(contentUrl, uri);
353         }
354 
355         try {
356             final List<String> paths = uri.getPathSegments();
357             // getTreeDocumentId will throw an exception if it is not a directory so check manually
358             if (!paths.get(0).equals("tree"))
359                 return false;
360             ContentResolver resolver = context.getContentResolver();
361             Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri));
362             if (!docUri.toString().startsWith(uri.toString()))
363                 return false;
364             Cursor cur = resolver.query(docUri, new String[] { DocumentsContract.Document.COLUMN_MIME_TYPE }, null, null, null);
365             if (cur != null) {
366                 if (cur.moveToFirst()) {
367                     final String dirStr = new String(DocumentsContract.Document.MIME_TYPE_DIR);
368                     isDir = cur.getString(0).equals(dirStr);
369                     if (isDir)
370                         m_knownDirs.add(contentUrl);
371                 }
372                 cur.close();
373             }
374             return isDir;
375         } catch (IllegalArgumentException e) {
376             Log.e(QtTAG, "checkIfDir(): Invalid Uri");
377             e.printStackTrace();
378         } catch (UnsupportedOperationException e) {
379             Log.e(QtTAG, "checkIfDir(): Unsupported operation for given Uri");
380             e.printStackTrace();
381         } catch (SecurityException e) {
382             Log.e(QtTAG, NoPermissionErrorMessage);
383         }
384         return false;
385     }
listContentsFromTreeUri(Context context, String contentUrl)386     public static String[] listContentsFromTreeUri(Context context, String contentUrl)
387     {
388         Uri treeUri = Uri.parse(contentUrl);
389         final ArrayList<String> results = new ArrayList<String>();
390         if (treeUri == null) {
391             Log.e(QtTAG, "listContentsFromTreeUri(): Invalid uri");
392             return results.toArray(new String[results.size()]);
393         }
394         final ContentResolver resolver = context.getContentResolver();
395         final Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri,
396                 DocumentsContract.getTreeDocumentId(treeUri));
397         final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(docUri,
398                                 DocumentsContract.getDocumentId(docUri));
399         Cursor c = null;
400         final String dirStr = new String(DocumentsContract.Document.MIME_TYPE_DIR);
401         try {
402             c = resolver.query(childrenUri, new String[] {
403                     DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_MIME_TYPE }, null, null, null);
404             while (c.moveToNext()) {
405                 final String fileString = c.getString(1);
406                 if (!m_cachedUris.containsKey(contentUrl + "/" + fileString)) {
407                     m_cachedUris.put(contentUrl + "/" + fileString,
408                                      DocumentsContract.buildDocumentUriUsingTree(treeUri, c.getString(0)));
409                 }
410                 results.add(fileString);
411                 if (c.getString(2).equals(dirStr))
412                     m_knownDirs.add(contentUrl + "/" + fileString);
413             }
414             c.close();
415         } catch (Exception e) {
416             Log.w(QtTAG, "Failed query: " + e);
417             return results.toArray(new String[results.size()]);
418         }
419         return results.toArray(new String[results.size()]);
420     }
421     // this method loads full path libs
loadQtLibraries(final ArrayList<String> libraries)422     public static void loadQtLibraries(final ArrayList<String> libraries)
423     {
424         m_qtThread.run(new Runnable() {
425             @Override
426             public void run() {
427                 if (libraries == null)
428                     return;
429                 for (String libName : libraries) {
430                     try {
431                         File f = new File(libName);
432                         if (f.exists())
433                             System.load(libName);
434                         else
435                             Log.i(QtTAG, "Can't find '" + libName + "'");
436                     } catch (SecurityException e) {
437                         Log.i(QtTAG, "Can't load '" + libName + "'", e);
438                     } catch (Exception e) {
439                         Log.i(QtTAG, "Can't load '" + libName + "'", e);
440                     }
441                 }
442             }
443         });
444     }
445 
446     // this method loads bundled libs by name.
loadBundledLibraries(final ArrayList<String> libraries, final String nativeLibraryDir)447     public static void loadBundledLibraries(final ArrayList<String> libraries, final String nativeLibraryDir)
448     {
449         m_qtThread.run(new Runnable() {
450             @Override
451             public void run() {
452                 if (libraries == null)
453                     return;
454 
455                 for (String libName : libraries) {
456                     try {
457                         String libNameTemplate = "lib" + libName + ".so";
458                         File f = new File(nativeLibraryDir + libNameTemplate);
459                         if (!f.exists()) {
460                             Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
461                             try {
462                                 ApplicationInfo info = getContext().getApplicationContext().getPackageManager()
463                                     .getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
464                                 String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
465                                 if (info.metaData.containsKey("android.app.system_libs_prefix"))
466                                     systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
467                                 f = new File(systemLibraryDir + libNameTemplate);
468                             } catch (Exception e) {
469                                 e.printStackTrace();
470                             }
471                         }
472                         if (f.exists())
473                             System.load(f.getAbsolutePath());
474                         else
475                             Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
476                     } catch (Exception e) {
477                         Log.i(QtTAG, "Can't load '" + libName + "'", e);
478                     }
479                 }
480             }
481         });
482     }
483 
loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)484     public static String loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)
485     {
486         final String[] res = new String[1];
487         res[0] = null;
488         m_qtThread.run(new Runnable() {
489             @Override
490             public void run() {
491                 try {
492                     String mainLibNameTemplate = "lib" + mainLibrary + ".so";
493                     File f = new File(nativeLibraryDir + mainLibNameTemplate);
494                     if (!f.exists()) {
495                         try {
496                             ApplicationInfo info = getContext().getApplicationContext().getPackageManager()
497                                     .getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
498                             String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
499                             if (info.metaData.containsKey("android.app.system_libs_prefix"))
500                                 systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
501                             f = new File(systemLibraryDir + mainLibNameTemplate);
502                         } catch (Exception e) {
503                             e.printStackTrace();
504                             return;
505                         }
506                     }
507                     if (!f.exists())
508                         return;
509                     System.load(f.getAbsolutePath());
510                     res[0] = f.getAbsolutePath();
511                 } catch (Exception e) {
512                     Log.e(QtTAG, "Can't load '" + mainLibrary + "'", e);
513                 }
514             }
515         });
516         return res[0];
517     }
518 
setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)519     public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
520     {
521         synchronized (m_mainActivityMutex) {
522             m_activity = qtMainActivity;
523             m_activityDelegate = qtActivityDelegate;
524         }
525     }
526 
setService(Service qtMainService, QtServiceDelegate qtServiceDelegate)527     public static void setService(Service qtMainService, QtServiceDelegate qtServiceDelegate)
528     {
529         synchronized (m_mainActivityMutex) {
530             m_service = qtMainService;
531             m_serviceDelegate = qtServiceDelegate;
532         }
533     }
534 
setApplicationState(int state)535     public static void setApplicationState(int state)
536     {
537         synchronized (m_mainActivityMutex) {
538             switch (state) {
539                 case QtActivityDelegate.ApplicationActive:
540                     m_activityPaused = false;
541                     Iterator<Runnable> itr = m_lostActions.iterator();
542                     while (itr.hasNext())
543                         runAction(itr.next());
544                     m_lostActions.clear();
545                     break;
546                 default:
547                     m_activityPaused = true;
548                     break;
549             }
550         }
551         updateApplicationState(state);
552     }
553 
runAction(Runnable action)554     private static void runAction(Runnable action)
555     {
556         synchronized (m_mainActivityMutex) {
557             final Looper mainLooper = Looper.getMainLooper();
558             final Handler handler = new Handler(mainLooper);
559             final boolean actionIsQueued = !m_activityPaused && m_activity != null && mainLooper != null && handler.post(action);
560             if (!actionIsQueued)
561                 m_lostActions.add(action);
562         }
563     }
564 
runPendingCppRunnablesOnAndroidThread()565     private static void runPendingCppRunnablesOnAndroidThread()
566     {
567         synchronized (m_mainActivityMutex) {
568             if (m_activity != null) {
569                 if (!m_activityPaused)
570                     m_activity.runOnUiThread(runPendingCppRunnablesRunnable);
571                 else
572                     runAction(runPendingCppRunnablesRunnable);
573             } else {
574                 final Looper mainLooper = Looper.getMainLooper();
575                 final Thread looperThread = mainLooper.getThread();
576                 if (looperThread.equals(Thread.currentThread())) {
577                     runPendingCppRunnablesRunnable.run();
578                 } else {
579                     final Handler handler = new Handler(mainLooper);
580                     handler.post(runPendingCppRunnablesRunnable);
581                 }
582             }
583         }
584     }
585 
setViewVisibility(final View view, final boolean visible)586     private static void setViewVisibility(final View view, final boolean visible)
587     {
588         runAction(new Runnable() {
589             @Override
590             public void run() {
591                 view.setVisibility(visible ? View.VISIBLE : View.GONE);
592             }
593         });
594     }
595 
startApplication(String params, final String environment, String mainLib)596     public static boolean startApplication(String params, final String environment, String mainLib) throws Exception
597     {
598         if (params == null)
599             params = "-platform\tandroid";
600 
601         final boolean[] res = new boolean[1];
602         res[0] = false;
603         synchronized (m_mainActivityMutex) {
604             if (params.length() > 0 && !params.startsWith("\t"))
605                 params = "\t" + params;
606             final String qtParams = mainLib + params;
607             m_qtThread.run(new Runnable() {
608                 @Override
609                 public void run() {
610                     res[0] = startQtAndroidPlugin(qtParams, environment);
611                     setDisplayMetrics(m_displayMetricsScreenWidthPixels,
612                                       m_displayMetricsScreenHeightPixels,
613                                       m_displayMetricsDesktopWidthPixels,
614                                       m_displayMetricsDesktopHeightPixels,
615                                       m_displayMetricsXDpi,
616                                       m_displayMetricsYDpi,
617                                       m_displayMetricsScaledDensity,
618                                       m_displayMetricsDensity);
619                 }
620             });
621             m_qtThread.post(new Runnable() {
622                 @Override
623                 public void run() {
624                     startQtApplication();
625                 }
626             });
627             waitForServiceSetup();
628             m_started = true;
629         }
630         return res[0];
631     }
632 
setApplicationDisplayMetrics(int screenWidthPixels, int screenHeightPixels, int desktopWidthPixels, int desktopHeightPixels, double XDpi, double YDpi, double scaledDensity, double density)633     public static void setApplicationDisplayMetrics(int screenWidthPixels,
634                                                     int screenHeightPixels,
635                                                     int desktopWidthPixels,
636                                                     int desktopHeightPixels,
637                                                     double XDpi,
638                                                     double YDpi,
639                                                     double scaledDensity,
640                                                     double density)
641     {
642         /* Fix buggy dpi report */
643         if (XDpi < android.util.DisplayMetrics.DENSITY_LOW)
644             XDpi = android.util.DisplayMetrics.DENSITY_LOW;
645         if (YDpi < android.util.DisplayMetrics.DENSITY_LOW)
646             YDpi = android.util.DisplayMetrics.DENSITY_LOW;
647 
648         synchronized (m_mainActivityMutex) {
649             if (m_started) {
650                 setDisplayMetrics(screenWidthPixels,
651                                   screenHeightPixels,
652                                   desktopWidthPixels,
653                                   desktopHeightPixels,
654                                   XDpi,
655                                   YDpi,
656                                   scaledDensity,
657                                   density);
658             } else {
659                 m_displayMetricsScreenWidthPixels = screenWidthPixels;
660                 m_displayMetricsScreenHeightPixels = screenHeightPixels;
661                 m_displayMetricsDesktopWidthPixels = desktopWidthPixels;
662                 m_displayMetricsDesktopHeightPixels = desktopHeightPixels;
663                 m_displayMetricsXDpi = XDpi;
664                 m_displayMetricsYDpi = YDpi;
665                 m_displayMetricsScaledDensity = scaledDensity;
666                 m_displayMetricsDensity = density;
667             }
668         }
669     }
670 
671 
672 
673     // application methods
startQtAndroidPlugin(String params, String env)674     public static native boolean startQtAndroidPlugin(String params, String env);
startQtApplication()675     public static native void startQtApplication();
waitForServiceSetup()676     public static native void waitForServiceSetup();
quitQtCoreApplication()677     public static native void quitQtCoreApplication();
quitQtAndroidPlugin()678     public static native void quitQtAndroidPlugin();
terminateQt()679     public static native void terminateQt();
680     // application methods
681 
quitApp()682     private static void quitApp()
683     {
684         runAction(new Runnable() {
685             @Override
686             public void run() {
687                 quitQtAndroidPlugin();
688                 if (m_activity != null)
689                      m_activity.finish();
690                  if (m_service != null)
691                      m_service.stopSelf();
692             }
693         });
694     }
695 
696     //@ANDROID-9
getAction(int index, MotionEvent event)697     static private int getAction(int index, MotionEvent event)
698     {
699         int action = event.getActionMasked();
700         if (action == MotionEvent.ACTION_MOVE) {
701             int hsz = event.getHistorySize();
702             if (hsz > 0) {
703                 float x = event.getX(index);
704                 float y = event.getY(index);
705                 for (int h = 0; h < hsz; ++h) {
706                     if ( event.getHistoricalX(index, h) != x ||
707                          event.getHistoricalY(index, h) != y )
708                         return 1;
709                 }
710                 return 2;
711             }
712             return 1;
713         }
714         if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN && index == event.getActionIndex()) {
715             return 0;
716         } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_POINTER_UP && index == event.getActionIndex()) {
717             return 3;
718         }
719         return 2;
720     }
721     //@ANDROID-9
722 
sendTouchEvent(MotionEvent event, int id)723     static public void sendTouchEvent(MotionEvent event, int id)
724     {
725         int pointerType = 0;
726 
727         if (m_tabletEventSupported == null)
728             m_tabletEventSupported = isTabletEventSupported();
729 
730         switch (event.getToolType(0)) {
731         case MotionEvent.TOOL_TYPE_STYLUS:
732             pointerType = 1; // QTabletEvent::Pen
733             break;
734         case MotionEvent.TOOL_TYPE_ERASER:
735             pointerType = 3; // QTabletEvent::Eraser
736             break;
737         }
738 
739         if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
740             sendMouseEvent(event, id);
741         } else if (m_tabletEventSupported && pointerType != 0) {
742             tabletEvent(id, event.getDeviceId(), event.getEventTime(), event.getAction(), pointerType,
743                 event.getButtonState(), event.getX(), event.getY(), event.getPressure());
744         } else {
745             touchBegin(id);
746             for (int i = 0; i < event.getPointerCount(); ++i) {
747                     touchAdd(id,
748                              event.getPointerId(i),
749                              getAction(i, event),
750                              i == 0,
751                              (int)event.getX(i),
752                              (int)event.getY(i),
753                              event.getTouchMajor(i),
754                              event.getTouchMinor(i),
755                              event.getOrientation(i),
756                              event.getPressure(i));
757             }
758 
759             switch (event.getAction()) {
760                 case MotionEvent.ACTION_DOWN:
761                     touchEnd(id, 0);
762                     break;
763 
764                 case MotionEvent.ACTION_UP:
765                     touchEnd(id, 2);
766                     break;
767 
768                 default:
769                     touchEnd(id, 1);
770             }
771         }
772     }
773 
sendTrackballEvent(MotionEvent event, int id)774     static public void sendTrackballEvent(MotionEvent event, int id)
775     {
776         sendMouseEvent(event,id);
777     }
778 
sendGenericMotionEvent(MotionEvent event, int id)779     static public boolean sendGenericMotionEvent(MotionEvent event, int id)
780     {
781         if (((event.getAction() & (MotionEvent.ACTION_SCROLL | MotionEvent.ACTION_HOVER_MOVE)) == 0)
782                 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != InputDevice.SOURCE_CLASS_POINTER) {
783             return false;
784         }
785 
786         return sendMouseEvent(event, id);
787     }
788 
sendMouseEvent(MotionEvent event, int id)789     static public boolean sendMouseEvent(MotionEvent event, int id)
790     {
791         switch (event.getActionMasked()) {
792             case MotionEvent.ACTION_UP:
793                 mouseUp(id, (int) event.getX(), (int) event.getY());
794                 break;
795 
796             case MotionEvent.ACTION_DOWN:
797                 mouseDown(id, (int) event.getX(), (int) event.getY());
798                 m_oldx = (int) event.getX();
799                 m_oldy = (int) event.getY();
800                 break;
801             case MotionEvent.ACTION_HOVER_MOVE:
802             case MotionEvent.ACTION_MOVE:
803                 if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
804                     mouseMove(id, (int) event.getX(), (int) event.getY());
805                 } else {
806                     int dx = (int) (event.getX() - m_oldx);
807                     int dy = (int) (event.getY() - m_oldy);
808                     if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
809                         mouseMove(id, (int) event.getX(), (int) event.getY());
810                         m_oldx = (int) event.getX();
811                         m_oldy = (int) event.getY();
812                     }
813                 }
814                 break;
815             case MotionEvent.ACTION_SCROLL:
816                 mouseWheel(id, (int) event.getX(), (int) event.getY(),
817                         event.getAxisValue(MotionEvent.AXIS_HSCROLL), event.getAxisValue(MotionEvent.AXIS_VSCROLL));
818                 break;
819             default:
820                 return false;
821         }
822         return true;
823     }
824 
getContext()825     public static Context getContext() {
826         if (m_activity != null)
827             return m_activity;
828         return m_service;
829     }
830 
checkSelfPermission(String permission)831     public static int checkSelfPermission(String permission)
832     {
833         int perm = PackageManager.PERMISSION_DENIED;
834         synchronized (m_mainActivityMutex) {
835             Context context = getContext();
836             try {
837                 if (Build.VERSION.SDK_INT >= 23) {
838                     if (m_checkSelfPermissionMethod == null)
839                         m_checkSelfPermissionMethod = Context.class.getMethod("checkSelfPermission", String.class);
840                     perm = (Integer)m_checkSelfPermissionMethod.invoke(context, permission);
841                 } else {
842                     final PackageManager pm = context.getPackageManager();
843                     perm = pm.checkPermission(permission, context.getApplicationContext().getPackageName());
844                 }
845             } catch (Exception e) {
846                 e.printStackTrace();
847             }
848         }
849 
850         return perm;
851     }
852 
updateSelection(final int selStart, final int selEnd, final int candidatesStart, final int candidatesEnd)853     private static void updateSelection(final int selStart,
854                                         final int selEnd,
855                                         final int candidatesStart,
856                                         final int candidatesEnd)
857     {
858         runAction(new Runnable() {
859             @Override
860             public void run() {
861                 if (m_activityDelegate != null)
862                     m_activityDelegate.updateSelection(selStart, selEnd, candidatesStart, candidatesEnd);
863             }
864         });
865     }
866 
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)867     private static void updateHandles(final int mode,
868                                       final int editX,
869                                       final int editY,
870                                       final int editButtons,
871                                       final int x1,
872                                       final int y1,
873                                       final int x2,
874                                       final int y2,
875                                       final boolean rtl)
876     {
877         runAction(new Runnable() {
878             @Override
879             public void run() {
880                 m_activityDelegate.updateHandles(mode, editX, editY, editButtons, x1, y1, x2, y2, rtl);
881             }
882         });
883     }
884 
showSoftwareKeyboard(final int x, final int y, final int width, final int height, final int inputHints, final int enterKeyType)885     private static void showSoftwareKeyboard(final int x,
886                                              final int y,
887                                              final int width,
888                                              final int height,
889                                              final int inputHints,
890                                              final int enterKeyType)
891     {
892         runAction(new Runnable() {
893             @Override
894             public void run() {
895                 if (m_activityDelegate != null)
896                     m_activityDelegate.showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
897             }
898         });
899     }
900 
resetSoftwareKeyboard()901     private static void resetSoftwareKeyboard()
902     {
903         runAction(new Runnable() {
904             @Override
905             public void run() {
906                 if (m_activityDelegate != null)
907                     m_activityDelegate.resetSoftwareKeyboard();
908             }
909         });
910     }
911 
hideSoftwareKeyboard()912     private static void hideSoftwareKeyboard()
913     {
914         runAction(new Runnable() {
915             @Override
916             public void run() {
917                 if (m_activityDelegate != null)
918                     m_activityDelegate.hideSoftwareKeyboard();
919             }
920         });
921     }
922 
setFullScreen(final boolean fullScreen)923     private static void setFullScreen(final boolean fullScreen)
924     {
925         runAction(new Runnable() {
926             @Override
927             public void run() {
928                 if (m_activityDelegate != null) {
929                     m_activityDelegate.setFullScreen(fullScreen);
930                 }
931                 updateWindow();
932             }
933         });
934     }
935 
notifyAccessibilityLocationChange()936     private static void notifyAccessibilityLocationChange()
937     {
938         runAction(new Runnable() {
939             @Override
940             public void run() {
941                 if (m_activityDelegate != null) {
942                     m_activityDelegate.notifyAccessibilityLocationChange();
943                 }
944             }
945         });
946     }
947 
notifyObjectHide(final int viewId)948     private static void notifyObjectHide(final int viewId)
949     {
950         runAction(new Runnable() {
951             @Override
952             public void run() {
953                 if (m_activityDelegate != null) {
954                     m_activityDelegate.notifyObjectHide(viewId);
955                 }
956             }
957         });
958     }
959 
notifyObjectFocus(final int viewId)960     private static void notifyObjectFocus(final int viewId)
961     {
962         runAction(new Runnable() {
963             @Override
964             public void run() {
965                 if (m_activityDelegate != null) {
966                     m_activityDelegate.notifyObjectFocus(viewId);
967                 }
968             }
969         });
970     }
971 
registerClipboardManager()972     private static void registerClipboardManager()
973     {
974         if (m_service == null || m_activity != null) { // Avoid freezing if only service
975             final Semaphore semaphore = new Semaphore(0);
976             runAction(new Runnable() {
977                 @Override
978                 public void run() {
979                     if (m_activity != null)
980                         m_clipboardManager = (android.content.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE);
981                     if (m_clipboardManager != null) {
982                         m_clipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
983                             public void onPrimaryClipChanged() {
984                                 onClipboardDataChanged();
985                             }
986                         });
987                     }
988                     semaphore.release();
989                 }
990             });
991             try {
992                 semaphore.acquire();
993             } catch (Exception e) {
994                 e.printStackTrace();
995             }
996         }
997     }
998 
clearClipData()999     private static void clearClipData()
1000     {
1001         if (Build.VERSION.SDK_INT >= 28 && m_clipboardManager != null)
1002             m_clipboardManager.clearPrimaryClip();
1003     }
setClipboardText(String text)1004     private static void setClipboardText(String text)
1005     {
1006         if (m_clipboardManager != null) {
1007             ClipData clipData = ClipData.newPlainText("text/plain", text);
1008             updatePrimaryClip(clipData);
1009         }
1010     }
1011 
hasClipboardText()1012     public static boolean hasClipboardText()
1013     {
1014         try {
1015             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1016                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1017                 for (int i = 0; i < primaryClip.getItemCount(); ++i)
1018                     if (primaryClip.getItemAt(i).getText() != null)
1019                         return true;
1020             }
1021         } catch (Exception e) {
1022             Log.e(QtTAG, "Failed to get clipboard data", e);
1023         }
1024         return false;
1025     }
1026 
getClipboardText()1027     private static String getClipboardText()
1028     {
1029         try {
1030             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1031                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1032                 for (int i = 0; i < primaryClip.getItemCount(); ++i)
1033                     if (primaryClip.getItemAt(i).getText() != null)
1034                         return primaryClip.getItemAt(i).getText().toString();
1035             }
1036         } catch (Exception e) {
1037             Log.e(QtTAG, "Failed to get clipboard data", e);
1038         }
1039         return "";
1040     }
1041 
updatePrimaryClip(ClipData clipData)1042     private static void updatePrimaryClip(ClipData clipData)
1043     {
1044         try {
1045             if (m_usePrimaryClip) {
1046                 ClipData clip = m_clipboardManager.getPrimaryClip();
1047                 if (Build.VERSION.SDK_INT >= 26) {
1048                     Objects.requireNonNull(clip).addItem(m_activity.getContentResolver(), clipData.getItemAt(0));
1049                 } else {
1050                     Objects.requireNonNull(clip).addItem(clipData.getItemAt(0));
1051                 }
1052                 m_clipboardManager.setPrimaryClip(clip);
1053             } else {
1054                 m_clipboardManager.setPrimaryClip(clipData);
1055                 m_usePrimaryClip = true;
1056             }
1057         } catch (Exception e) {
1058             Log.e(QtTAG, "Failed to set clipboard data", e);
1059         }
1060     }
1061 
setClipboardHtml(String text, String html)1062     private static void setClipboardHtml(String text, String html)
1063     {
1064         if (m_clipboardManager != null) {
1065             ClipData clipData = ClipData.newHtmlText("text/html", text, html);
1066             updatePrimaryClip(clipData);
1067         }
1068     }
1069 
hasClipboardHtml()1070     public static boolean hasClipboardHtml()
1071     {
1072         try {
1073             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1074                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1075                 for (int i = 0; i < Objects.requireNonNull(primaryClip).getItemCount(); ++i)
1076                     if (primaryClip.getItemAt(i).getHtmlText() != null)
1077                         return true;
1078             }
1079         } catch (Exception e) {
1080             Log.e(QtTAG, "Failed to get clipboard data", e);
1081         }
1082         return false;
1083     }
1084 
getClipboardHtml()1085     private static String getClipboardHtml()
1086     {
1087         try {
1088             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1089                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1090                 for (int i = 0; i < primaryClip.getItemCount(); ++i)
1091                     if (primaryClip.getItemAt(i).getHtmlText() != null)
1092                         return primaryClip.getItemAt(i).getHtmlText().toString();
1093             }
1094         } catch (Exception e) {
1095             Log.e(QtTAG, "Failed to get clipboard data", e);
1096         }
1097         return "";
1098     }
1099 
setClipboardUri(String uriString)1100     private static void setClipboardUri(String uriString)
1101     {
1102         if (m_clipboardManager != null) {
1103             ClipData clipData = ClipData.newUri(m_activity.getContentResolver(), "text/uri-list",
1104                                                 Uri.parse(uriString));
1105             updatePrimaryClip(clipData);
1106         }
1107     }
1108 
hasClipboardUri()1109     public static boolean hasClipboardUri()
1110     {
1111         try {
1112             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1113                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1114                 for (int i = 0; i < primaryClip.getItemCount(); ++i)
1115                     if (primaryClip.getItemAt(i).getUri() != null)
1116                         return true;
1117             }
1118         } catch (Exception e) {
1119             Log.e(QtTAG, "Failed to get clipboard data", e);
1120         }
1121         return false;
1122     }
1123 
getClipboardUris()1124     private static String[] getClipboardUris()
1125     {
1126         ArrayList<String> uris = new ArrayList<String>();
1127         try {
1128             if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
1129                 ClipData primaryClip = m_clipboardManager.getPrimaryClip();
1130                 for (int i = 0; i < primaryClip.getItemCount(); ++i)
1131                     if (primaryClip.getItemAt(i).getUri() != null)
1132                         uris.add(primaryClip.getItemAt(i).getUri().toString());
1133             }
1134         } catch (Exception e) {
1135             Log.e(QtTAG, "Failed to get clipboard data", e);
1136         }
1137         String[] strings = new String[uris.size()];
1138         strings = uris.toArray(strings);
1139         return strings;
1140     }
1141 
openContextMenu(final int x, final int y, final int w, final int h)1142     private static void openContextMenu(final int x, final int y, final int w, final int h)
1143     {
1144         runAction(new Runnable() {
1145             @Override
1146             public void run() {
1147                 if (m_activityDelegate != null)
1148                     m_activityDelegate.openContextMenu(x, y, w, h);
1149             }
1150         });
1151     }
1152 
closeContextMenu()1153     private static void closeContextMenu()
1154     {
1155         runAction(new Runnable() {
1156             @Override
1157             public void run() {
1158                 if (m_activityDelegate != null)
1159                     m_activityDelegate.closeContextMenu();
1160             }
1161         });
1162     }
1163 
resetOptionsMenu()1164     private static void resetOptionsMenu()
1165     {
1166         runAction(new Runnable() {
1167             @Override
1168             public void run() {
1169                 if (m_activityDelegate != null)
1170                     m_activityDelegate.resetOptionsMenu();
1171             }
1172         });
1173     }
1174 
openOptionsMenu()1175     private static void openOptionsMenu()
1176     {
1177         runAction(new Runnable() {
1178             @Override
1179             public void run() {
1180                 if (m_activity != null)
1181                     m_activity.openOptionsMenu();
1182             }
1183         });
1184     }
1185 
getSSLCertificates()1186     private static byte[][] getSSLCertificates()
1187     {
1188         ArrayList<byte[]> certificateList = new ArrayList<byte[]>();
1189 
1190         try {
1191             TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
1192             factory.init((KeyStore) null);
1193 
1194             for (TrustManager manager : factory.getTrustManagers()) {
1195                 if (manager instanceof X509TrustManager) {
1196                     X509TrustManager trustManager = (X509TrustManager) manager;
1197 
1198                     for (X509Certificate certificate : trustManager.getAcceptedIssuers()) {
1199                         byte buffer[] = certificate.getEncoded();
1200                         certificateList.add(buffer);
1201                     }
1202                 }
1203             }
1204         } catch (Exception e) {
1205             Log.e(QtTAG, "Failed to get certificates", e);
1206         }
1207 
1208         byte[][] certificateArray = new byte[certificateList.size()][];
1209         certificateArray = certificateList.toArray(certificateArray);
1210         return certificateArray;
1211     }
1212 
createSurface(final int id, final boolean onTop, final int x, final int y, final int w, final int h, final int imageDepth)1213     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)
1214     {
1215         runAction(new Runnable() {
1216             @Override
1217             public void run() {
1218                 if (m_activityDelegate != null)
1219                     m_activityDelegate.createSurface(id, onTop, x, y, w, h, imageDepth);
1220             }
1221         });
1222     }
1223 
insertNativeView(final int id, final View view, final int x, final int y, final int w, final int h)1224     private static void insertNativeView(final int id, final View view, final int x, final int y, final int w, final int h)
1225     {
1226         runAction(new Runnable() {
1227             @Override
1228             public void run() {
1229                 if (m_activityDelegate != null)
1230                     m_activityDelegate.insertNativeView(id, view, x, y, w, h);
1231             }
1232         });
1233     }
1234 
setSurfaceGeometry(final int id, final int x, final int y, final int w, final int h)1235     private static void setSurfaceGeometry(final int id, final int x, final int y, final int w, final int h)
1236     {
1237         runAction(new Runnable() {
1238             @Override
1239             public void run() {
1240                 if (m_activityDelegate != null)
1241                     m_activityDelegate.setSurfaceGeometry(id, x, y, w, h);
1242             }
1243         });
1244     }
1245 
bringChildToFront(final int id)1246     private static void bringChildToFront(final int id)
1247     {
1248         runAction(new Runnable() {
1249             @Override
1250             public void run() {
1251                 if (m_activityDelegate != null)
1252                     m_activityDelegate.bringChildToFront(id);
1253             }
1254         });
1255     }
1256 
bringChildToBack(final int id)1257     private static void bringChildToBack(final int id)
1258     {
1259         runAction(new Runnable() {
1260             @Override
1261             public void run() {
1262                 if (m_activityDelegate != null)
1263                     m_activityDelegate.bringChildToBack(id);
1264             }
1265         });
1266     }
1267 
destroySurface(final int id)1268     private static void destroySurface(final int id)
1269     {
1270         runAction(new Runnable() {
1271             @Override
1272             public void run() {
1273                 if (m_activityDelegate != null)
1274                     m_activityDelegate.destroySurface(id);
1275             }
1276         });
1277     }
1278 
initializeAccessibility()1279     private static void initializeAccessibility()
1280     {
1281         runAction(new Runnable() {
1282             @Override
1283             public void run() {
1284                 m_activityDelegate.initializeAccessibility();
1285             }
1286         });
1287     }
1288 
hideSplashScreen(final int duration)1289     private static void hideSplashScreen(final int duration)
1290     {
1291         runAction(new Runnable() {
1292             @Override
1293             public void run() {
1294                 if (m_activityDelegate != null)
1295                     m_activityDelegate.hideSplashScreen(duration);
1296             }
1297         });
1298     }
1299 
listAssetContent(android.content.res.AssetManager asset, String path)1300     private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
1301         String [] list;
1302         ArrayList<String> res = new ArrayList<String>();
1303         try {
1304             list = asset.list(path);
1305             if (list.length > 0) {
1306                 for (String file : list) {
1307                     try {
1308                         String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
1309                         if (isDir != null && isDir.length > 0)
1310                             file += "/";
1311                         res.add(file);
1312                     } catch (Exception e) {
1313                         e.printStackTrace();
1314                     }
1315                 }
1316             }
1317         } catch (Exception e) {
1318             e.printStackTrace();
1319         }
1320         return res.toArray(new String[res.size()]);
1321     }
1322 
1323     // screen methods
setDisplayMetrics(int screenWidthPixels, int screenHeightPixels, int desktopWidthPixels, int desktopHeightPixels, double XDpi, double YDpi, double scaledDensity, double density)1324     public static native void setDisplayMetrics(int screenWidthPixels,
1325                                                 int screenHeightPixels,
1326                                                 int desktopWidthPixels,
1327                                                 int desktopHeightPixels,
1328                                                 double XDpi,
1329                                                 double YDpi,
1330                                                 double scaledDensity,
1331                                                 double density);
handleOrientationChanged(int newRotation, int nativeOrientation)1332     public static native void handleOrientationChanged(int newRotation, int nativeOrientation);
1333     // screen methods
1334 
1335     // pointer methods
mouseDown(int winId, int x, int y)1336     public static native void mouseDown(int winId, int x, int y);
mouseUp(int winId, int x, int y)1337     public static native void mouseUp(int winId, int x, int y);
mouseMove(int winId, int x, int y)1338     public static native void mouseMove(int winId, int x, int y);
mouseWheel(int winId, int x, int y, float hdelta, float vdelta)1339     public static native void mouseWheel(int winId, int x, int y, float hdelta, float vdelta);
touchBegin(int winId)1340     public static native void touchBegin(int winId);
touchAdd(int winId, int pointerId, int action, boolean primary, int x, int y, float major, float minor, float rotation, float pressure)1341     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);
touchEnd(int winId, int action)1342     public static native void touchEnd(int winId, int action);
longPress(int winId, int x, int y)1343     public static native void longPress(int winId, int x, int y);
1344     // pointer methods
1345 
1346     // tablet methods
isTabletEventSupported()1347     public static native boolean isTabletEventSupported();
tabletEvent(int winId, int deviceId, long time, int action, int pointerType, int buttonState, float x, float y, float pressure)1348     public static native void tabletEvent(int winId, int deviceId, long time, int action, int pointerType, int buttonState, float x, float y, float pressure);
1349     // tablet methods
1350 
1351     // keyboard methods
keyDown(int key, int unicode, int modifier, boolean autoRepeat)1352     public static native void keyDown(int key, int unicode, int modifier, boolean autoRepeat);
keyUp(int key, int unicode, int modifier, boolean autoRepeat)1353     public static native void keyUp(int key, int unicode, int modifier, boolean autoRepeat);
keyboardVisibilityChanged(boolean visibility)1354     public static native void keyboardVisibilityChanged(boolean visibility);
keyboardGeometryChanged(int x, int y, int width, int height)1355     public static native void keyboardGeometryChanged(int x, int y, int width, int height);
1356     // keyboard methods
1357 
1358     // handle methods
1359     public static final int IdCursorHandle = 1;
1360     public static final int IdLeftHandle = 2;
1361     public static final int IdRightHandle = 3;
handleLocationChanged(int id, int x, int y)1362     public static native void handleLocationChanged(int id, int x, int y);
1363     // handle methods
1364 
1365     // dispatch events methods
dispatchGenericMotionEvent(MotionEvent ev)1366     public static native boolean dispatchGenericMotionEvent(MotionEvent ev);
dispatchKeyEvent(KeyEvent event)1367     public static native boolean dispatchKeyEvent(KeyEvent event);
1368     // dispatch events methods
1369 
1370     // surface methods
setSurface(int id, Object surface, int w, int h)1371     public static native void setSurface(int id, Object surface, int w, int h);
1372     // surface methods
1373 
1374     // window methods
updateWindow()1375     public static native void updateWindow();
1376     // window methods
1377 
1378     // application methods
updateApplicationState(int state)1379     public static native void updateApplicationState(int state);
1380 
1381     // menu methods
onPrepareOptionsMenu(Menu menu)1382     public static native boolean onPrepareOptionsMenu(Menu menu);
onOptionsItemSelected(int itemId, boolean checked)1383     public static native boolean onOptionsItemSelected(int itemId, boolean checked);
onOptionsMenuClosed(Menu menu)1384     public static native void onOptionsMenuClosed(Menu menu);
1385 
onCreateContextMenu(ContextMenu menu)1386     public static native void onCreateContextMenu(ContextMenu menu);
fillContextMenu(Menu menu)1387     public static native void fillContextMenu(Menu menu);
onContextItemSelected(int itemId, boolean checked)1388     public static native boolean onContextItemSelected(int itemId, boolean checked);
onContextMenuClosed(Menu menu)1389     public static native void onContextMenuClosed(Menu menu);
1390     // menu methods
1391 
1392     // clipboard methods
onClipboardDataChanged()1393     public static native void onClipboardDataChanged();
1394     // clipboard methods
1395 
1396     // activity methods
onActivityResult(int requestCode, int resultCode, Intent data)1397     public static native void onActivityResult(int requestCode, int resultCode, Intent data);
onNewIntent(Intent data)1398     public static native void onNewIntent(Intent data);
1399 
runPendingCppRunnables()1400     public static native void runPendingCppRunnables();
1401 
sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)1402     public static native void sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
1403 
setNativeActivity(Activity activity)1404     private static native void setNativeActivity(Activity activity);
setNativeService(Service service)1405     private static native void setNativeService(Service service);
1406     // activity methods
1407 
1408     // service methods
onBind(Intent intent)1409     public static native IBinder onBind(Intent intent);
1410     // service methods
1411 }
1412