Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
QtNative.java
Go to the documentation of this file.
1// Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2023 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5package org.qtproject.qt.android;
6
7import android.app.Activity;
8import android.app.Service;
9import android.content.Context;
10import android.content.Intent;
11import android.content.UriPermission;
12import android.content.pm.PackageManager;
13import android.net.Uri;
14import android.os.Handler;
15import android.os.IBinder;
16import android.os.Looper;
17import android.util.Log;
18import android.view.ContextMenu;
19import android.view.Menu;
20import android.view.View;
21
22import java.lang.ref.WeakReference;
23import java.security.KeyStore;
24import java.security.cert.X509Certificate;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.Objects;
28
29import javax.net.ssl.TrustManager;
30import javax.net.ssl.TrustManagerFactory;
31import javax.net.ssl.X509TrustManager;
32
33class QtNative
34{
35 private static WeakReference<Activity> m_activity = null;
36 private static WeakReference<Service> m_service = null;
37 public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
38
39 private static final ApplicationStateDetails m_stateDetails = new ApplicationStateDetails();
40
41 public static final String QtTAG = "Qt JAVA";
42
43 // a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
44 private static final ArrayList<Runnable> m_lostActions = new ArrayList<>();
45
46 private static final QtThread m_qtThread = new QtThread();
47 private static ClassLoader m_classLoader = null;
48
49 private static final Runnable runPendingCppRunnablesRunnable = QtNative::runPendingCppRunnables;
50 private static final ArrayList<AppStateDetailsListener> m_appStateListeners = new ArrayList<>();
51 private static final Object m_appStateListenersLock = new Object();
52
53 @UsedFromNativeCode
54 public static ClassLoader classLoader()
55 {
56 return m_classLoader;
57 }
58
59 public static void setClassLoader(ClassLoader classLoader)
60 {
61 m_classLoader = classLoader;
62 }
63
64 public static void setActivity(Activity qtMainActivity)
65 {
66 synchronized (m_mainActivityMutex) {
67 m_activity = new WeakReference<>(qtMainActivity);
68 }
69 }
70
71 public static void setService(Service qtMainService)
72 {
73 synchronized (m_mainActivityMutex) {
74 m_service = new WeakReference<>(qtMainService);
75 }
76 }
77
78 @UsedFromNativeCode
79 public static Activity activity()
80 {
81 synchronized (m_mainActivityMutex) {
82 return m_activity != null ? m_activity.get() : null;
83 }
84 }
85
86 public static boolean isActivityValid()
87 {
88 return m_activity != null && m_activity.get() != null;
89 }
90
91 @UsedFromNativeCode
92 public static Service service()
93 {
94 synchronized (m_mainActivityMutex) {
95 return m_service != null ? m_service.get() : null;
96 }
97 }
98
99 public static boolean isServiceValid()
100 {
101 return m_service != null && m_service.get() != null;
102 }
103
104 @UsedFromNativeCode
105 public static Context getContext() {
106 if (isActivityValid())
107 return m_activity.get();
108 return service();
109 }
110
111 @UsedFromNativeCode
112 public static String[] getStringArray(String joinedString)
113 {
114 return joinedString.split(",");
115 }
116
117 private static String getCurrentMethodNameLog()
118 {
119 return new Exception().getStackTrace()[1].getMethodName() + ": ";
120 }
121
123 private static Uri getUriWithValidPermission(Context context, String uri, String openMode)
124 {
125 Uri parsedUri;
126 try {
127 parsedUri = Uri.parse(uri);
128 } catch (NullPointerException e) {
129 e.printStackTrace();
130 return null;
131 }
132
133 try {
134 String scheme = parsedUri.getScheme();
135
136 // We only want to check permissions for content Uris
137 if (scheme != null && scheme.compareTo("content") != 0)
138 return parsedUri;
139
140 List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions();
141 String uriStr = parsedUri.getPath();
142
143 for (int i = 0; i < permissions.size(); ++i) {
144 Uri iterUri = permissions.get(i).getUri();
145 boolean isRequestPermission = permissions.get(i).isReadPermission();
146
147 if (!openMode.equals("r"))
148 isRequestPermission = permissions.get(i).isWritePermission();
149
150 if (Objects.equals(iterUri.getPath(), uriStr) && isRequestPermission)
151 return iterUri;
152 }
153
154 // if we only have transient permissions on uri all the above will fail,
155 // but we will be able to read the file anyway, so continue with uri here anyway
156 // and check for SecurityExceptions later
157 return parsedUri;
158 } catch (SecurityException e) {
159 Log.e(QtTAG, getCurrentMethodNameLog() + e);
160 return parsedUri;
161 }
162 }
163
164 @UsedFromNativeCode
165 public static boolean openURL(Context context, String url, String mime)
166 {
167 final Uri uri = getUriWithValidPermission(context, url, "r");
168 if (uri == null) {
169 Log.e(QtTAG, getCurrentMethodNameLog() + "received invalid/null Uri");
170 return false;
171 }
172
173 try {
174 Intent intent = new Intent(Intent.ACTION_VIEW, uri);
175 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
176 if (!mime.isEmpty())
177 intent.setDataAndType(uri, mime);
178
179 Activity activity = activity();
180 if (activity == null) {
181 Log.w(QtTAG, "openURL(): The activity reference is null");
182 return false;
183 }
184
185 activity.startActivity(intent);
186
187 return true;
188 } catch (Exception e) {
189 Log.e(QtTAG, getCurrentMethodNameLog() + e);
190 return false;
191 }
192 }
193
194 static QtThread getQtThread() {
195 return m_qtThread;
196 }
197
202
203 // Keep in sync with src/corelib/global/qnamespace.h
204 public static class ApplicationState {
205 static final int ApplicationSuspended = 0x0;
206 static final int ApplicationHidden = 0x1;
207 static final int ApplicationInactive = 0x2;
208 static final int ApplicationActive = 0x4;
209 }
210
211 public static class ApplicationStateDetails {
212 int state = ApplicationState.ApplicationSuspended;
213 boolean nativePluginIntegrationReady = false;
214 boolean isStarted = false;
215 }
216
217 public static ApplicationStateDetails getStateDetails()
218 {
219 return m_stateDetails;
220 }
221
222 public static void setStarted(boolean started)
223 {
224 m_stateDetails.isStarted = started;
225 notifyAppStateDetailsChanged(m_stateDetails);
226 }
227
228 @UsedFromNativeCode
229 public static void notifyNativePluginIntegrationReady(boolean ready)
230 {
231 m_stateDetails.nativePluginIntegrationReady = ready;
232 notifyNativePluginIntegrationReadyChanged(ready);
233 notifyAppStateDetailsChanged(m_stateDetails);
234 }
235
236 public static void setApplicationState(int state)
237 {
238 synchronized (m_mainActivityMutex) {
239 m_stateDetails.state = state;
240 if (state == ApplicationState.ApplicationActive) {
241 for (Runnable mLostAction : m_lostActions)
242 runAction(mLostAction);
243 m_lostActions.clear();
244 }
245 }
246 updateApplicationState(state);
247 notifyAppStateDetailsChanged(m_stateDetails);
248 }
249
250 static void registerAppStateListener(AppStateDetailsListener listener) {
251 synchronized (m_appStateListenersLock) {
252 if (!m_appStateListeners.contains(listener))
253 m_appStateListeners.add(listener);
254 }
255 }
256
257 static void unregisterAppStateListener(AppStateDetailsListener listener) {
258 synchronized (m_appStateListenersLock) {
259 m_appStateListeners.remove(listener);
260 }
261 }
262
263 static void notifyNativePluginIntegrationReadyChanged(boolean ready) {
264 synchronized (m_appStateListenersLock) {
265 for (final AppStateDetailsListener listener : m_appStateListeners)
266 listener.onNativePluginIntegrationReadyChanged(ready);
267 }
268 }
269
270 static void notifyAppStateDetailsChanged(ApplicationStateDetails details) {
271 synchronized (m_appStateListenersLock) {
272 for (AppStateDetailsListener listener : m_appStateListeners)
273 listener.onAppStateDetailsChanged(details);
274 }
275 }
276
277 // Post a runnable to Main (UI) Thread if the app is active,
278 // otherwise, queue it to be posted when the the app is active again
279 public static void runAction(Runnable action)
280 {
281 runAction(action, true);
282 }
283
284 public static void runAction(Runnable action, boolean queueWhenInactive)
285 {
286 synchronized (m_mainActivityMutex) {
287 final Looper mainLooper = Looper.getMainLooper();
288 final Handler handler = new Handler(mainLooper);
289
290 if (queueWhenInactive) {
291 final boolean isStateVisible =
292 (m_stateDetails.state != ApplicationState.ApplicationSuspended)
293 && (m_stateDetails.state != ApplicationState.ApplicationHidden);
294 final boolean active = (isActivityValid() && isStateVisible) || isServiceValid();
295 if (!active || !handler.post(action))
296 m_lostActions.add(action);
297 } else {
298 handler.post(action);
299 }
300 }
301 }
302
303 @UsedFromNativeCode
304 private static void runPendingCppRunnablesOnAndroidThread()
305 {
306 synchronized (m_mainActivityMutex) {
307 if (isActivityValid()) {
308 if (m_stateDetails.state == ApplicationState.ApplicationActive)
309 m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable);
310 else
311 runAction(runPendingCppRunnablesRunnable);
312 } else {
313 final Looper mainLooper = Looper.getMainLooper();
314 final Thread looperThread = mainLooper.getThread();
315 if (looperThread.equals(Thread.currentThread())) {
316 runPendingCppRunnablesRunnable.run();
317 } else {
318 final Handler handler = new Handler(mainLooper);
319 handler.post(runPendingCppRunnablesRunnable);
320 }
321 }
322 }
323 }
324
325 @UsedFromNativeCode
326 private static void setViewVisibility(final View view, final boolean visible)
327 {
328 runAction(() -> view.setVisibility(visible ? View.VISIBLE : View.GONE));
329 }
330
331 public static void startApplication(String params, String mainLib)
332 {
333 synchronized (m_mainActivityMutex) {
334 m_qtThread.run(() -> {
335 final String qtParams = mainLib + " " + params;
336 if (!startQtAndroidPlugin(qtParams))
337 Log.e(QtTAG, "An error occurred while starting the Qt Android plugin");
338 });
339 m_qtThread.post(QtNative::startQtApplication);
340 waitForServiceSetup();
341 m_stateDetails.isStarted = true;
342 notifyAppStateDetailsChanged(m_stateDetails);
343 }
344 }
345
346 public static void quitApp()
347 {
348 runAction(() -> {
349 quitQtAndroidPlugin();
350 if (isActivityValid())
351 m_activity.get().finish();
352 if (isServiceValid())
353 m_service.get().stopSelf();
354 m_stateDetails.isStarted = false;
355 notifyAppStateDetailsChanged(m_stateDetails);
356 });
357 }
358
359 @UsedFromNativeCode
360 public static int checkSelfPermission(String permission)
361 {
362 synchronized (m_mainActivityMutex) {
363 Context context = getContext();
364 PackageManager pm = context.getPackageManager();
365 return pm.checkPermission(permission, context.getPackageName());
366 }
367 }
368
369 @UsedFromNativeCode
370 private static byte[][] getSSLCertificates()
371 {
372 ArrayList<byte[]> certificateList = new ArrayList<>();
373
374 try {
375 TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
376 factory.init((KeyStore) null);
377
378 for (TrustManager manager : factory.getTrustManagers()) {
379 if (manager instanceof X509TrustManager) {
380 X509TrustManager trustManager = (X509TrustManager) manager;
381
382 for (X509Certificate certificate : trustManager.getAcceptedIssuers()) {
383 byte[] buffer = certificate.getEncoded();
384 certificateList.add(buffer);
385 }
386 }
387 }
388 } catch (Exception e) {
389 Log.e(QtTAG, "Failed to get certificates", e);
390 }
391
392 byte[][] certificateArray = new byte[certificateList.size()][];
393 certificateArray = certificateList.toArray(certificateArray);
394 return certificateArray;
395 }
396
397 @UsedFromNativeCode
398 private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
399 String [] list;
400 ArrayList<String> res = new ArrayList<>();
401 try {
402 list = asset.list(path);
403 if (list != null) {
404 for (String file : list) {
405 try {
406 String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
407 if (isDir != null && isDir.length > 0)
408 file += "/";
409 res.add(file);
410 } catch (Exception e) {
411 e.printStackTrace();
412 }
413 }
414 }
415 } catch (Exception e) {
416 e.printStackTrace();
417 }
418 return res.toArray(new String[0]);
419 }
420
421 // application methods
422 public static native boolean startQtAndroidPlugin(String params);
423 public static native void startQtApplication();
424 public static native void waitForServiceSetup();
425 public static native void quitQtCoreApplication();
426 public static native void quitQtAndroidPlugin();
427 public static native void terminateQt();
428 public static native boolean updateNativeActivity();
429 // application methods
430
431 // surface methods
432 public static native void setSurface(int id, Object surface);
433 // surface methods
434
435 // window methods
436 public static native void updateWindow();
437 // window methods
438
439 // application methods
440 public static native void updateApplicationState(int state);
441
442 // menu methods
443 public static native boolean onPrepareOptionsMenu(Menu menu);
444 public static native boolean onOptionsItemSelected(int itemId, boolean checked);
445 public static native void onOptionsMenuClosed(Menu menu);
446
447 public static native void onCreateContextMenu(ContextMenu menu);
448 public static native void fillContextMenu(Menu menu);
449 public static native boolean onContextItemSelected(int itemId, boolean checked);
450 public static native void onContextMenuClosed(Menu menu);
451 // menu methods
452
453 // activity methods
454 public static native void onActivityResult(int requestCode, int resultCode, Intent data);
455 public static native void onNewIntent(Intent data);
456
457 public static native void runPendingCppRunnables();
458
459 public static native void sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
460 // activity methods
461
462 // service methods
463 public static native IBinder onBind(Intent intent);
464 // service methods
465}
Definition main.cpp:8
else opt state
[0]
default void onNativePluginIntegrationReadyChanged(boolean ready)
default void onAppStateDetailsChanged(ApplicationStateDetails details)
Q_CORE_EXPORT QtJniTypes::Activity activity()
jobject classLoader()
ApplicationState
Definition qnamespace.h:262
static void * context
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
void ** params
GLuint res
GLsizei const GLchar *const * path
@ Handler
QList< int > list
[14]
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
application x qt windows mime
[2]
QItemEditorFactory * factory
QMenu menu
[5]
QNetworkAccessManager manager
QQuickView * view
[0]