Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
QtActivityDelegate.java
Go to the documentation of this file.
1// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2022 The Qt Company Ltd.
3// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6package org.qtproject.qt.android;
7
8import android.app.Activity;
9import android.content.Context;
10import android.content.Intent;
11import android.content.pm.ActivityInfo;
12import android.content.pm.ApplicationInfo;
13import android.content.pm.PackageManager;
14import android.content.pm.PackageManager.NameNotFoundException;
15import android.content.res.AssetManager;
16import android.content.res.Configuration;
17import android.graphics.Color;
18import android.graphics.drawable.ColorDrawable;
19import android.graphics.drawable.Drawable;
20import android.graphics.Rect;
21import android.net.LocalServerSocket;
22import android.net.LocalSocket;
23import android.os.Build;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.ResultReceiver;
27import android.text.method.MetaKeyKeyListener;
28import android.util.Base64;
29import android.util.DisplayMetrics;
30import android.util.Log;
31import android.util.TypedValue;
32import android.view.animation.AccelerateInterpolator;
33import android.view.animation.AlphaAnimation;
34import android.view.animation.Animation;
35import android.view.ContextMenu;
36import android.view.ContextMenu.ContextMenuInfo;
37import android.view.Display;
38import android.view.KeyCharacterMap;
39import android.view.KeyEvent;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.MotionEvent;
43import android.view.Surface;
44import android.view.View;
45import android.view.ViewConfiguration;
46import android.view.ViewGroup;
47import android.view.Window;
48import android.view.WindowInsetsController;
49import android.view.WindowManager;
50import android.view.inputmethod.InputMethodManager;
51import android.view.ViewTreeObserver;
52import android.widget.ImageView;
53import android.widget.PopupMenu;
54import android.hardware.display.DisplayManager;
55
56import java.io.BufferedReader;
57import java.io.DataOutputStream;
58import java.io.File;
59import java.io.FileWriter;
60import java.io.InputStreamReader;
61import java.io.IOException;
62import java.lang.reflect.Constructor;
63import java.lang.reflect.Method;
64import java.util.ArrayList;
65import java.util.HashMap;
66import java.util.Objects;
67
69
71{
72 private Activity m_activity = null;
73 private Method m_super_dispatchKeyEvent = null;
74 private Method m_super_onRestoreInstanceState = null;
75 private Method m_super_onRetainNonConfigurationInstance = null;
76 private Method m_super_onSaveInstanceState = null;
77 private Method m_super_onKeyDown = null;
78 private Method m_super_onKeyUp = null;
79 private Method m_super_onConfigurationChanged = null;
80 private Method m_super_onActivityResult = null;
81 private Method m_super_dispatchGenericMotionEvent = null;
82 private Method m_super_onWindowFocusChanged = null;
83
84 private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
85 private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
86 private static final String MAIN_LIBRARY_KEY = "main.library";
87 private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
88 private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
89 private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
90
91 public static final int SYSTEM_UI_VISIBILITY_NORMAL = 0;
92 public static final int SYSTEM_UI_VISIBILITY_FULLSCREEN = 1;
93 public static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2;
94
95 private static String m_applicationParameters = null;
96
97 private int m_currentRotation = -1; // undefined
98 private int m_nativeOrientation = Configuration.ORIENTATION_UNDEFINED;
99
100 private String m_mainLib;
101 private long m_metaState;
102 private int m_lastChar = 0;
103 private int m_softInputMode = 0;
104 private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
105 private boolean m_started = false;
106 private HashMap<Integer, QtSurface> m_surfaces = null;
107 private HashMap<Integer, View> m_nativeViews = null;
108 private QtLayout m_layout = null;
109 private ImageView m_splashScreen = null;
110 private boolean m_splashScreenSticky = false;
111 private QtEditText m_editText = null;
112 private InputMethodManager m_imm = null;
113 private boolean m_quitApp = true;
114 private View m_dummyView = null;
115 private boolean m_keyboardIsVisible = false;
116 public boolean m_backKeyPressedSent = false;
117 private long m_showHideTimeStamp = System.nanoTime();
118 private int m_portraitKeyboardHeight = 0;
119 private int m_landscapeKeyboardHeight = 0;
120 private int m_probeKeyboardHeightDelay = 50; // ms
121 private CursorHandle m_cursorHandle;
122 private CursorHandle m_leftSelectionHandle;
123 private CursorHandle m_rightSelectionHandle;
124 private EditPopupMenu m_editPopupMenu;
125 private boolean m_isPluginRunning = false;
126
127 private QtAccessibilityDelegate m_accessibilityDelegate = null;
128
129
130 public void setSystemUiVisibility(int systemUiVisibility)
131 {
132 if (m_systemUiVisibility == systemUiVisibility)
133 return;
134
135 m_systemUiVisibility = systemUiVisibility;
136 setLayoutInDisplayCutoutMode();
137
138 int systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_VISIBLE;
139 switch (m_systemUiVisibility) {
141 m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
142 m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
143 break;
145 m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
146 m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
147 systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
148 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
149 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
150 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
151 | View.SYSTEM_UI_FLAG_FULLSCREEN
152 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
153 | View.INVISIBLE;
154 break;
156 m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
157 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
158 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
159 m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
160 break;
161 };
162
163 m_activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags);
164
165 m_layout.requestLayout();
166 }
167
168 private void setLayoutInDisplayCutoutMode()
169 {
170 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
171 int cutOutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
172 if (SYSTEM_UI_VISIBILITY_FULLSCREEN == m_systemUiVisibility)
173 cutOutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
174
175 m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode = cutOutMode;
176 }
177 }
178
179 public void updateFullScreen()
180 {
181 if (m_systemUiVisibility == SYSTEM_UI_VISIBILITY_FULLSCREEN) {
182 m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
184 }
185 }
186
187 public boolean isKeyboardVisible()
188 {
189 return m_keyboardIsVisible;
190 }
191
192 // input method hints - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
193 private final int ImhHiddenText = 0x1;
194 private final int ImhSensitiveData = 0x2;
195 private final int ImhNoAutoUppercase = 0x4;
196 private final int ImhPreferNumbers = 0x8;
197 private final int ImhPreferUppercase = 0x10;
198 private final int ImhPreferLowercase = 0x20;
199 private final int ImhNoPredictiveText = 0x40;
200
201 private final int ImhDate = 0x80;
202 private final int ImhTime = 0x100;
203
204 private final int ImhPreferLatin = 0x200;
205
206 private final int ImhMultiLine = 0x400;
207
208 private final int ImhDigitsOnly = 0x10000;
209 private final int ImhFormattedNumbersOnly = 0x20000;
210 private final int ImhUppercaseOnly = 0x40000;
211 private final int ImhLowercaseOnly = 0x80000;
212 private final int ImhDialableCharactersOnly = 0x100000;
213 private final int ImhEmailCharactersOnly = 0x200000;
214 private final int ImhUrlCharactersOnly = 0x400000;
215 private final int ImhLatinOnly = 0x800000;
216
217 // enter key type - must be kept in sync with QTDIR/src/corelib/global/qnamespace.h
218 private final int EnterKeyDefault = 0;
219 private final int EnterKeyReturn = 1;
220 private final int EnterKeyDone = 2;
221 private final int EnterKeyGo = 3;
222 private final int EnterKeySend = 4;
223 private final int EnterKeySearch = 5;
224 private final int EnterKeyNext = 6;
225 private final int EnterKeyPrevious = 7;
226
227 // application state
228 public static final int ApplicationSuspended = 0x0;
229 public static final int ApplicationHidden = 0x1;
230 public static final int ApplicationInactive = 0x2;
231 public static final int ApplicationActive = 0x4;
232
233
234 public boolean setKeyboardVisibility(boolean visibility, long timeStamp)
235 {
236 if (m_showHideTimeStamp > timeStamp)
237 return false;
238 m_showHideTimeStamp = timeStamp;
239
240 if (m_keyboardIsVisible == visibility)
241 return false;
242 m_keyboardIsVisible = visibility;
243 QtNative.keyboardVisibilityUpdated(m_keyboardIsVisible);
244
245 if (visibility == false)
246 updateFullScreen(); // Hiding the keyboard clears the immersive mode, so we need to set it again.
247
248 return true;
249 }
251 {
252 if (m_imm == null)
253 return;
254 m_editText.postDelayed(new Runnable() {
255 @Override
256 public void run() {
257 m_imm.restartInput(m_editText);
258 m_editText.m_optionsChanged = false;
259 }
260 }, 5);
261 }
262
263 public void showSoftwareKeyboard(final int x, final int y, final int width, final int height, final int inputHints, final int enterKeyType)
264 {
265 if (m_imm == null)
266 return;
267
268 DisplayMetrics metrics = new DisplayMetrics();
269 m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
270
271 // If the screen is in portrait mode than we estimate that keyboard height will not be higher than 2/5 of the screen.
272 // else than we estimate that keyboard height will not be higher than 2/3 of the screen
273 final int visibleHeight;
274 if (metrics.widthPixels < metrics.heightPixels)
275 visibleHeight = m_portraitKeyboardHeight != 0 ? m_portraitKeyboardHeight : metrics.heightPixels * 3 / 5;
276 else
277 visibleHeight = m_landscapeKeyboardHeight != 0 ? m_landscapeKeyboardHeight : metrics.heightPixels / 3;
278
279 if (m_softInputMode != 0) {
280 m_activity.getWindow().setSoftInputMode(m_softInputMode);
281 final boolean softInputIsHidden = (m_softInputMode & WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) != 0;
282 if (softInputIsHidden)
283 return;
284 } else {
285 if (height > visibleHeight)
286 m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
287 else
288 m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
289 }
290
291 int initialCapsMode = 0;
292
293 int imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
294
295 switch (enterKeyType) {
296 case EnterKeyReturn:
297 imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
298 break;
299 case EnterKeyGo:
300 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
301 break;
302 case EnterKeySend:
303 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEND;
304 break;
305 case EnterKeySearch:
306 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH;
307 break;
308 case EnterKeyNext:
309 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
310 break;
311 case EnterKeyPrevious:
312 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS;
313 break;
314 }
315
316 int inputType = android.text.InputType.TYPE_CLASS_TEXT;
317
318 if ((inputHints & (ImhPreferNumbers | ImhDigitsOnly | ImhFormattedNumbersOnly)) != 0) {
319 inputType = android.text.InputType.TYPE_CLASS_NUMBER;
320 if ((inputHints & ImhFormattedNumbersOnly) != 0) {
321 inputType |= (android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL
322 | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
323 }
324
325 if ((inputHints & ImhHiddenText) != 0)
326 inputType |= android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD;
327 } else if ((inputHints & ImhDialableCharactersOnly) != 0) {
328 inputType = android.text.InputType.TYPE_CLASS_PHONE;
329 } else if ((inputHints & (ImhDate | ImhTime)) != 0) {
330 inputType = android.text.InputType.TYPE_CLASS_DATETIME;
331 if ((inputHints & (ImhDate | ImhTime)) != (ImhDate | ImhTime)) {
332 if ((inputHints & ImhDate) != 0)
333 inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_DATE;
334 else
335 inputType |= android.text.InputType.TYPE_DATETIME_VARIATION_TIME;
336 } // else { TYPE_DATETIME_VARIATION_NORMAL(0) }
337 } else { // CLASS_TEXT
338 if ((inputHints & ImhHiddenText) != 0) {
339 inputType |= android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
340 } else if ((inputHints & ImhSensitiveData) != 0 ||
341 ((inputHints & ImhNoPredictiveText) != 0 &&
342 System.getenv("QT_ANDROID_ENABLE_WORKAROUND_TO_DISABLE_PREDICTIVE_TEXT") != null)) {
343 inputType |= android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
344 } else if ((inputHints & ImhUrlCharactersOnly) != 0) {
345 inputType |= android.text.InputType.TYPE_TEXT_VARIATION_URI;
346 if (enterKeyType == 0) // not explicitly overridden
347 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO;
348 } else if ((inputHints & ImhEmailCharactersOnly) != 0) {
349 inputType |= android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
350 }
351
352 if ((inputHints & ImhMultiLine) != 0) {
353 inputType |= android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE;
354 // Clear imeOptions for Multi-Line Type
355 // User should be able to insert new line in such case
356 imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
357 }
358 if ((inputHints & (ImhNoPredictiveText | ImhSensitiveData | ImhHiddenText)) != 0)
359 inputType |= android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
360
361 if ((inputHints & ImhUppercaseOnly) != 0) {
362 initialCapsMode |= android.text.TextUtils.CAP_MODE_CHARACTERS;
363 inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
364 } else if ((inputHints & ImhLowercaseOnly) == 0 && (inputHints & ImhNoAutoUppercase) == 0) {
365 initialCapsMode |= android.text.TextUtils.CAP_MODE_SENTENCES;
366 inputType |= android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
367 }
368 }
369
370 if (enterKeyType == 0 && (inputHints & ImhMultiLine) != 0)
371 imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
372
373 m_editText.setInitialCapsMode(initialCapsMode);
374 m_editText.setImeOptions(imeOptions);
375 m_editText.setInputType(inputType);
376
377 m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(width, height, x, y), false);
378 m_editText.requestFocus();
379 m_editText.postDelayed(new Runnable() {
380 @Override
381 public void run() {
382 m_imm.showSoftInput(m_editText, 0, new ResultReceiver(new Handler()) {
383 @Override
384 protected void onReceiveResult(int resultCode, Bundle resultData) {
385 switch (resultCode) {
386 case InputMethodManager.RESULT_SHOWN:
387 QtNativeInputConnection.updateCursorPosition();
388 //FALLTHROUGH
389 case InputMethodManager.RESULT_UNCHANGED_SHOWN:
390 setKeyboardVisibility(true, System.nanoTime());
391 if (m_softInputMode == 0) {
392 // probe for real keyboard height
393 m_layout.postDelayed(new Runnable() {
394 @Override
395 public void run() {
396 if (!m_keyboardIsVisible)
397 return;
398 DisplayMetrics metrics = new DisplayMetrics();
399 m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
400 Rect r = new Rect();
401 m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
402 if (metrics.heightPixels != r.bottom) {
403 if (metrics.widthPixels > metrics.heightPixels) { // landscape
404 if (m_landscapeKeyboardHeight != r.bottom) {
405 m_landscapeKeyboardHeight = r.bottom;
406 showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
407 }
408 } else {
409 if (m_portraitKeyboardHeight != r.bottom) {
410 m_portraitKeyboardHeight = r.bottom;
411 showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType);
412 }
413 }
414 } else {
415 // no luck ?
416 // maybe the delay was too short, so let's make it longer
417 if (m_probeKeyboardHeightDelay < 1000)
418 m_probeKeyboardHeightDelay *= 2;
419 }
420 }
421 }, m_probeKeyboardHeightDelay);
422 }
423 break;
424 case InputMethodManager.RESULT_HIDDEN:
425 case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
426 setKeyboardVisibility(false, System.nanoTime());
427 break;
428 }
429 }
430 });
431 if (m_editText.m_optionsChanged) {
432 m_imm.restartInput(m_editText);
433 m_editText.m_optionsChanged = false;
434 }
435 }
436 }, 15);
437 }
438
440 {
441 if (m_imm == null)
442 return;
443 m_imm.hideSoftInputFromWindow(m_editText.getWindowToken(), 0, new ResultReceiver(new Handler()) {
444 @Override
445 protected void onReceiveResult(int resultCode, Bundle resultData) {
446 switch (resultCode) {
447 case InputMethodManager.RESULT_SHOWN:
448 case InputMethodManager.RESULT_UNCHANGED_SHOWN:
449 setKeyboardVisibility(true, System.nanoTime());
450 break;
451 case InputMethodManager.RESULT_HIDDEN:
452 case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
453 setKeyboardVisibility(false, System.nanoTime());
454 break;
455 }
456 }
457 });
458 }
459
460 int getAppIconSize(Activity a)
461 {
462 int size = a.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
463 if (size < 36 || size > 512) { // check size sanity
464 DisplayMetrics metrics = new DisplayMetrics();
465 a.getWindowManager().getDefaultDisplay().getMetrics(metrics);
466 size = metrics.densityDpi / 10 * 3;
467 if (size < 36)
468 size = 36;
469
470 if (size > 512)
471 size = 512;
472 }
473
474 return size;
475 }
476
477 public void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
478 {
479 if (m_imm == null)
480 return;
481
482 m_imm.updateSelection(m_editText, selStart, selEnd, candidatesStart, candidatesEnd);
483 }
484
485 // Values coming from QAndroidInputContext::CursorHandleShowMode
486 private static final int CursorHandleNotShown = 0;
487 private static final int CursorHandleShowNormal = 1;
488 private static final int CursorHandleShowSelection = 2;
489 private static final int CursorHandleShowEdit = 0x100;
490
492 {
493 int width = 0;
494 if (m_leftSelectionHandle != null && m_rightSelectionHandle != null) {
495 width = Math.max(m_leftSelectionHandle.width(), m_rightSelectionHandle.width());
496 } else if (m_cursorHandle != null) {
497 width = m_cursorHandle.width();
498 }
499 return width;
500 }
501
502 /* called from the C++ code when the position of the cursor or selection handles needs to
503 be adjusted.
504 mode is one of QAndroidInputContext::CursorHandleShowMode
505 */
506 public void updateHandles(int mode, int editX, int editY, int editButtons, int x1, int y1, int x2, int y2, boolean rtl)
507 {
508 switch (mode & 0xff)
509 {
510 case CursorHandleNotShown:
511 if (m_cursorHandle != null) {
512 m_cursorHandle.hide();
513 m_cursorHandle = null;
514 }
515 if (m_rightSelectionHandle != null) {
516 m_rightSelectionHandle.hide();
517 m_leftSelectionHandle.hide();
518 m_rightSelectionHandle = null;
519 m_leftSelectionHandle = null;
520 }
521 if (m_editPopupMenu != null)
522 m_editPopupMenu.hide();
523 break;
524
525 case CursorHandleShowNormal:
526 if (m_cursorHandle == null) {
527 m_cursorHandle = new CursorHandle(m_activity, m_layout, QtNative.IdCursorHandle,
528 android.R.attr.textSelectHandle, false);
529 }
530 m_cursorHandle.setPosition(x1, y1);
531 if (m_rightSelectionHandle != null) {
532 m_rightSelectionHandle.hide();
533 m_leftSelectionHandle.hide();
534 m_rightSelectionHandle = null;
535 m_leftSelectionHandle = null;
536 }
537 break;
538
539 case CursorHandleShowSelection:
540 if (m_rightSelectionHandle == null) {
541 m_leftSelectionHandle = new CursorHandle(m_activity, m_layout, QtNative.IdLeftHandle,
542 !rtl ? android.R.attr.textSelectHandleLeft :
543 android.R.attr.textSelectHandleRight,
544 rtl);
545 m_rightSelectionHandle = new CursorHandle(m_activity, m_layout, QtNative.IdRightHandle,
546 !rtl ? android.R.attr.textSelectHandleRight :
547 android.R.attr.textSelectHandleLeft,
548 rtl);
549 }
550 m_leftSelectionHandle.setPosition(x1,y1);
551 m_rightSelectionHandle.setPosition(x2,y2);
552 if (m_cursorHandle != null) {
553 m_cursorHandle.hide();
554 m_cursorHandle = null;
555 }
556 mode |= CursorHandleShowEdit;
557 break;
558 }
559
561 editButtons &= ~EditContextView.PASTE_BUTTON;
562
563 if ((mode & CursorHandleShowEdit) == CursorHandleShowEdit && editButtons != 0) {
564 m_editPopupMenu.setPosition(editX, editY, editButtons, m_cursorHandle, m_leftSelectionHandle,
565 m_rightSelectionHandle);
566 } else {
567 if (m_editPopupMenu != null)
568 m_editPopupMenu.hide();
569 }
570 }
571
572 private final DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener()
573 {
574 @Override
575 public void onDisplayAdded(int displayId) {
576 QtNative.handleScreenAdded(displayId);
577 }
578
579 private boolean isSimilarRotation(int r1, int r2)
580 {
581 return (r1 == r2)
582 || (r1 == Surface.ROTATION_0 && r2 == Surface.ROTATION_180)
583 || (r1 == Surface.ROTATION_180 && r2 == Surface.ROTATION_0)
584 || (r1 == Surface.ROTATION_90 && r2 == Surface.ROTATION_270)
585 || (r1 == Surface.ROTATION_270 && r2 == Surface.ROTATION_90);
586 }
587
588 @Override
589 public void onDisplayChanged(int displayId)
590 {
591 Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
592 ? m_activity.getWindowManager().getDefaultDisplay()
593 : m_activity.getDisplay();
594 m_currentRotation = display.getRotation();
595 m_layout.setActivityDisplayRotation(m_currentRotation);
596 // Process orientation change only if it comes after the size
597 // change, or if the screen is rotated by 180 degrees.
598 // Otherwise it will be processed in QtLayout.
599 if (isSimilarRotation(m_currentRotation, m_layout.displayRotation()))
600 QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation);
601
602 float refreshRate = display.getRefreshRate();
603 QtNative.handleRefreshRateChanged(refreshRate);
604 QtNative.handleScreenChanged(displayId);
605 }
606
607 @Override
608 public void onDisplayRemoved(int displayId) {
609 QtNative.handleScreenRemoved(displayId);
610 }
611 };
612
613 public boolean updateActivity(Activity activity)
614 {
615 try {
616 // set new activity
617 loadActivity(activity);
618
619 // update the new activity content view to old layout
620 ViewGroup layoutParent = (ViewGroup)m_layout.getParent();
621 if (layoutParent != null)
622 layoutParent.removeView(m_layout);
623
624 m_activity.setContentView(m_layout);
625
626 // force c++ native activity object to update
628 } catch (Exception e) {
629 Log.w(QtNative.QtTAG, "Failed to update the activity.");
630 e.printStackTrace();
631 return false;
632 }
633 }
634
635 private void loadActivity(Activity activity)
636 throws NoSuchMethodException, PackageManager.NameNotFoundException
637 {
638 m_activity = activity;
639
640 QtNative.setActivity(m_activity, this);
641 setActionBarVisibility(false);
642
643 Class<?> activityClass = m_activity.getClass();
644 m_super_dispatchKeyEvent =
645 activityClass.getMethod("super_dispatchKeyEvent", KeyEvent.class);
646 m_super_onRestoreInstanceState =
647 activityClass.getMethod("super_onRestoreInstanceState", Bundle.class);
648 m_super_onRetainNonConfigurationInstance =
649 activityClass.getMethod("super_onRetainNonConfigurationInstance");
650 m_super_onSaveInstanceState =
651 activityClass.getMethod("super_onSaveInstanceState", Bundle.class);
652 m_super_onKeyDown =
653 activityClass.getMethod("super_onKeyDown", Integer.TYPE, KeyEvent.class);
654 m_super_onKeyUp =
655 activityClass.getMethod("super_onKeyUp", Integer.TYPE, KeyEvent.class);
656 m_super_onConfigurationChanged =
657 activityClass.getMethod("super_onConfigurationChanged", Configuration.class);
658 m_super_onActivityResult =
659 activityClass.getMethod("super_onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class);
660 m_super_onWindowFocusChanged =
661 activityClass.getMethod("super_onWindowFocusChanged", Boolean.TYPE);
662 m_super_dispatchGenericMotionEvent =
663 activityClass.getMethod("super_dispatchGenericMotionEvent", MotionEvent.class);
664
665 m_softInputMode = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), 0).softInputMode;
666
667 DisplayManager displayManager = (DisplayManager)m_activity.getSystemService(Context.DISPLAY_SERVICE);
668 displayManager.registerDisplayListener(displayListener, null);
669 }
670
671 public boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams)
672 {
674 if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY)
675 || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY)
676 || !loaderParams.containsKey(ENVIRONMENT_VARIABLES_KEY)) {
677 return false;
678 }
679
680 try {
681 loadActivity(activity);
682 QtNative.setClassLoader(classLoader);
683 } catch (Exception e) {
684 e.printStackTrace();
685 return false;
686 }
687
688 if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) {
689 for (String className: Objects.requireNonNull(loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY))) {
690 if (className.length() == 0)
691 continue;
692
693 try {
694 Class<?> initClass = classLoader.loadClass(className);
695 Object staticInitDataObject = initClass.newInstance(); // create an instance
696 try {
697 Method m = initClass.getMethod("setActivity", Activity.class, Object.class);
698 m.invoke(staticInitDataObject, m_activity, this);
699 } catch (Exception e) {
700 Log.d(QtNative.QtTAG, "Class " + className + " does not implement setActivity method");
701 }
702
703 // For modules that don't need/have setActivity
704 try {
705 Method m = initClass.getMethod("setContext", Context.class);
706 m.invoke(staticInitDataObject, (Context)m_activity);
707 } catch (Exception e) {
708 e.printStackTrace();
709 }
710 } catch (Exception e) {
711 e.printStackTrace();
712 }
713 }
714 }
715 QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY));
716 ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY);
717 String nativeLibsDir = QtNativeLibrariesDir.nativeLibrariesDir(m_activity);
718 QtNative.loadBundledLibraries(libraries, nativeLibsDir);
719 m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY);
720 // older apps provide the main library as the last bundled library; look for this if the main library isn't provided
721 if (null == m_mainLib && libraries.size() > 0) {
722 m_mainLib = libraries.get(libraries.size() - 1);
723 libraries.remove(libraries.size() - 1);
724 }
725
726 ExtractStyle.setup(loaderParams);
727 ExtractStyle.runIfNeeded(m_activity, isUiModeDark(m_activity.getResources().getConfiguration()));
728
729 QtNative.setEnvironmentVariables(loaderParams.getString(ENVIRONMENT_VARIABLES_KEY));
730 QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_MONOSPACE",
731 "Droid Sans Mono;Droid Sans;Droid Sans Fallback");
732 QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_SERIF", "Droid Serif");
733 QtNative.setEnvironmentVariable("HOME", m_activity.getFilesDir().getAbsolutePath());
734 QtNative.setEnvironmentVariable("TMPDIR", m_activity.getCacheDir().getAbsolutePath());
735 QtNative.setEnvironmentVariable("QT_ANDROID_FONTS",
736 "Roboto;Droid Sans;Droid Sans Fallback");
737 QtNative.setEnvironmentVariable("QT_ANDROID_APP_ICON_SIZE",
738 String.valueOf(getAppIconSize(activity)));
739
740 if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY))
741 m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY);
742 else
743 m_applicationParameters = "";
744
745 m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
746 return m_mainLib != null;
747 }
748
749 public boolean startApplication()
750 {
751 // start application
752 try {
753
754 Bundle extras = m_activity.getIntent().getExtras();
755 if (extras != null) {
756 try {
757 final boolean isDebuggable = (m_activity.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
758 if (!isDebuggable)
759 throw new Exception();
760
761 if (extras.containsKey("extraenvvars")) {
762 try {
764 Base64.decode(extras.getString("extraenvvars"), Base64.DEFAULT),
765 "UTF-8"));
766 } catch (Exception e) {
767 e.printStackTrace();
768 }
769 }
770
771 if (extras.containsKey("extraappparams")) {
772 try {
773 m_applicationParameters += "\t" + new String(Base64.decode(extras.getString("extraappparams"), Base64.DEFAULT), "UTF-8");
774 } catch (Exception e) {
775 e.printStackTrace();
776 }
777 }
778 } catch (Exception e) {
779 Log.e(QtNative.QtTAG, "Not in debug mode! It is not allowed to use " +
780 "extra arguments in non-debug mode.");
781 // This is not an error, so keep it silent
782 // e.printStackTrace();
783 }
784 } // extras != null
785
786 if (null == m_surfaces)
787 onCreate(null);
788 return true;
789 } catch (Exception e) {
790 e.printStackTrace();
791 return false;
792 }
793 }
794
795 public void onTerminate()
796 {
799 }
800
801 public void onCreate(Bundle savedInstanceState)
802 {
803 m_quitApp = true;
804 Runnable startApplication = null;
805 if (null == savedInstanceState) {
806 startApplication = new Runnable() {
807 @Override
808 public void run() {
809 try {
810 QtNative.startApplication(m_applicationParameters, m_mainLib);
811 m_started = true;
812 } catch (Exception e) {
813 e.printStackTrace();
814 m_activity.finish();
815 }
816 }
817 };
818 }
819 m_layout = new QtLayout(m_activity, startApplication);
820
821 int orientation = m_activity.getResources().getConfiguration().orientation;
822
823 try {
824 ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
825
826 String splashScreenKey = "android.app.splash_screen_drawable_"
827 + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
828 if (!info.metaData.containsKey(splashScreenKey))
829 splashScreenKey = "android.app.splash_screen_drawable";
830
831 if (info.metaData.containsKey(splashScreenKey)) {
832 m_splashScreenSticky = info.metaData.containsKey("android.app.splash_screen_sticky") && info.metaData.getBoolean("android.app.splash_screen_sticky");
833 int id = info.metaData.getInt(splashScreenKey);
834 m_splashScreen = new ImageView(m_activity);
835 m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(id, m_activity.getTheme()));
836 m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
837 m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
838 m_layout.addView(m_splashScreen);
839 }
840 } catch (Exception e) {
841 e.printStackTrace();
842 }
843
844 m_editText = new QtEditText(m_activity, this);
845 m_imm = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE);
846 m_surfaces = new HashMap<Integer, QtSurface>();
847 m_nativeViews = new HashMap<Integer, View>();
848 m_activity.registerForContextMenu(m_layout);
849 m_activity.setContentView(m_layout,
850 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
851 ViewGroup.LayoutParams.MATCH_PARENT));
852
853 int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
854 boolean rot90 = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
855 boolean currentlyLandscape = (orientation == Configuration.ORIENTATION_LANDSCAPE);
856 if ((currentlyLandscape && !rot90) || (!currentlyLandscape && rot90))
857 m_nativeOrientation = Configuration.ORIENTATION_LANDSCAPE;
858 else
859 m_nativeOrientation = Configuration.ORIENTATION_PORTRAIT;
860
861 m_layout.setNativeOrientation(m_nativeOrientation);
862 QtNative.handleOrientationChanged(rotation, m_nativeOrientation);
863 m_currentRotation = rotation;
864
865 handleUiModeChange(m_activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
866
867 float refreshRate = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
868 ? m_activity.getWindowManager().getDefaultDisplay().getRefreshRate()
869 : m_activity.getDisplay().getRefreshRate();
871
872 m_layout.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
873 @Override
874 public boolean onPreDraw() {
875 if (!m_keyboardIsVisible)
876 return true;
877
878 Rect r = new Rect();
879 m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
880 DisplayMetrics metrics = new DisplayMetrics();
881 m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
882 final int kbHeight = metrics.heightPixels - r.bottom;
883 if (kbHeight < 0) {
884 setKeyboardVisibility(false, System.nanoTime());
885 return true;
886 }
887 final int[] location = new int[2];
888 m_layout.getLocationOnScreen(location);
890 r.width(), kbHeight);
891 return true;
892 }
893 });
894 m_editPopupMenu = new EditPopupMenu(m_activity, m_layout);
895 }
896
897 public void hideSplashScreen()
898 {
900 }
901
902 public void hideSplashScreen(final int duration)
903 {
904 if (m_splashScreen == null)
905 return;
906
907 if (duration <= 0) {
908 m_layout.removeView(m_splashScreen);
909 m_splashScreen = null;
910 return;
911 }
912
913 final Animation fadeOut = new AlphaAnimation(1, 0);
914 fadeOut.setInterpolator(new AccelerateInterpolator());
915 fadeOut.setDuration(duration);
916
917 fadeOut.setAnimationListener(new Animation.AnimationListener() {
918 @Override
919 public void onAnimationEnd(Animation animation) { hideSplashScreen(0); }
920
921 @Override
922 public void onAnimationRepeat(Animation animation) {}
923
924 @Override
925 public void onAnimationStart(Animation animation) {}
926 });
927
928 m_splashScreen.startAnimation(fadeOut);
929 }
930
931 public void notifyAccessibilityLocationChange(int viewId)
932 {
933 if (m_accessibilityDelegate == null)
934 return;
935 m_accessibilityDelegate.notifyLocationChange(viewId);
936 }
937
938 public void notifyObjectHide(int viewId, int parentId)
939 {
940 if (m_accessibilityDelegate == null)
941 return;
942 m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
943 }
944
945 public void notifyObjectFocus(int viewId)
946 {
947 if (m_accessibilityDelegate == null)
948 return;
949 m_accessibilityDelegate.notifyObjectFocus(viewId);
950 }
951
952 public void notifyValueChanged(int viewId, String value)
953 {
954 if (m_accessibilityDelegate == null)
955 return;
956 m_accessibilityDelegate.notifyValueChanged(viewId, value);
957 }
958
959 public void notifyScrolledEvent(int viewId)
960 {
961 if (m_accessibilityDelegate == null)
962 return;
963 m_accessibilityDelegate.notifyScrolledEvent(viewId);
964 }
965
966
968 {
969 m_isPluginRunning = running;
970 }
971
973 {
974 m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity, m_layout, this);
975 }
976
977 public void onWindowFocusChanged(boolean hasFocus) {
978 try {
979 m_super_onWindowFocusChanged.invoke(m_activity, hasFocus);
980 } catch (Exception e) {
981 e.printStackTrace();
982 }
983 if (hasFocus)
984 updateFullScreen();
985 }
986
987 boolean isUiModeDark(Configuration config)
988 {
989 return (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
990 }
991
992 private void handleUiModeChange(int uiMode)
993 {
994 // QTBUG-108365
995 if (Build.VERSION.SDK_INT >= 30) {
996 // Since 29 version we are using Theme_DeviceDefault_DayNight
997 Window window = m_activity.getWindow();
998 WindowInsetsController controller = window.getInsetsController();
999 if (controller != null) {
1000 // set APPEARANCE_LIGHT_STATUS_BARS if needed
1001 int appearanceLight = Color.luminance(window.getStatusBarColor()) > 0.5 ?
1002 WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS : 0;
1003 controller.setSystemBarsAppearance(appearanceLight,
1004 WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
1005 }
1006 }
1007 switch (uiMode) {
1008 case Configuration.UI_MODE_NIGHT_NO:
1009 ExtractStyle.runIfNeeded(m_activity, false);
1010 QtNative.handleUiDarkModeChanged(0);
1011 break;
1012 case Configuration.UI_MODE_NIGHT_YES:
1013 ExtractStyle.runIfNeeded(m_activity, true);
1014 QtNative.handleUiDarkModeChanged(1);
1015 break;
1016 }
1017 }
1018
1019 public void onConfigurationChanged(Configuration configuration)
1020 {
1021 try {
1022 m_super_onConfigurationChanged.invoke(m_activity, configuration);
1023 } catch (Exception e) {
1024 e.printStackTrace();
1025 }
1026 handleUiModeChange(configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK);
1027 }
1028
1029 public void onDestroy()
1030 {
1031 if (m_quitApp) {
1033 QtNative.setActivity(null, null);
1035 System.exit(0);
1036 }
1037 }
1038
1039 public void onPause()
1040 {
1041 if (Build.VERSION.SDK_INT < 24 || !m_activity.isInMultiWindowMode())
1042 QtNative.setApplicationState(ApplicationInactive);
1043 }
1044
1045 public void onResume()
1046 {
1047 QtNative.setApplicationState(ApplicationActive);
1048 if (m_started) {
1050 updateFullScreen(); // Suspending the app clears the immersive mode, so we need to set it again.
1051 }
1052 }
1053
1054 public void onNewIntent(Intent data)
1055 {
1057 }
1058
1059 public void onActivityResult(int requestCode, int resultCode, Intent data)
1060 {
1061 try {
1062 m_super_onActivityResult.invoke(m_activity, requestCode, resultCode, data);
1063 } catch (Exception e) {
1064 e.printStackTrace();
1065 }
1066
1067 QtNative.onActivityResult(requestCode, resultCode, data);
1068 }
1069
1070
1071 public void onStop()
1072 {
1073 QtNative.setApplicationState(ApplicationSuspended);
1074 }
1075
1077 {
1078 try {
1079 m_super_onRetainNonConfigurationInstance.invoke(m_activity);
1080 } catch (Exception e) {
1081 e.printStackTrace();
1082 }
1083 m_quitApp = false;
1084 return true;
1085 }
1086
1087 public void onSaveInstanceState(Bundle outState) {
1088 try {
1089 m_super_onSaveInstanceState.invoke(m_activity, outState);
1090 } catch (Exception e) {
1091 e.printStackTrace();
1092 }
1093 outState.putInt("SystemUiVisibility", m_systemUiVisibility);
1094 outState.putBoolean("Started", m_started);
1095 // It should never
1096 }
1097
1098 public void onRestoreInstanceState(Bundle savedInstanceState)
1099 {
1100 try {
1101 m_super_onRestoreInstanceState.invoke(m_activity, savedInstanceState);
1102 } catch (Exception e) {
1103 e.printStackTrace();
1104 }
1105 m_started = savedInstanceState.getBoolean("Started");
1106 // FIXME restore all surfaces
1107
1108 }
1109
1110 public boolean onKeyDown(int keyCode, KeyEvent event)
1111 {
1112 if (!m_started || !m_isPluginRunning)
1113 return false;
1114
1115 m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event);
1116 int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState) | event.getMetaState());
1117 int lc = c;
1118 m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState);
1119
1120 if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
1121 c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
1122 int composed = KeyEvent.getDeadChar(m_lastChar, c);
1123 c = composed;
1124 }
1125
1126 if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
1127 || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
1128 || keyCode == KeyEvent.KEYCODE_MUTE)
1129 && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
1130 return false;
1131 }
1132
1133 m_lastChar = lc;
1134 if (keyCode == KeyEvent.KEYCODE_BACK) {
1135 m_backKeyPressedSent = !m_keyboardIsVisible;
1136 if (!m_backKeyPressedSent)
1137 return true;
1138 }
1139 QtNative.keyDown(keyCode, c, event.getMetaState(), event.getRepeatCount() > 0);
1140
1141 return true;
1142 }
1143
1144 public boolean onKeyUp(int keyCode, KeyEvent event)
1145 {
1146 if (!m_started || !m_isPluginRunning)
1147 return false;
1148
1149 if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
1150 || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
1151 || keyCode == KeyEvent.KEYCODE_MUTE)
1152 && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) {
1153 return false;
1154 }
1155
1156 if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) {
1157 hideSoftwareKeyboard();
1158 setKeyboardVisibility(false, System.nanoTime());
1159 return true;
1160 }
1161
1162 m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event);
1163 QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState(), event.getRepeatCount() > 0);
1164 return true;
1165 }
1166
1167 public boolean dispatchKeyEvent(KeyEvent event)
1168 {
1169 if (m_started
1170 && event.getAction() == KeyEvent.ACTION_MULTIPLE
1171 && event.getCharacters() != null
1172 && event.getCharacters().length() == 1
1173 && event.getKeyCode() == 0) {
1174 QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0);
1175 QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0);
1176 }
1177
1179 return true;
1180
1181 try {
1182 return (Boolean) m_super_dispatchKeyEvent.invoke(m_activity, event);
1183 } catch (Exception e) {
1184 e.printStackTrace();
1185 }
1186 return false;
1187 }
1188
1189 private boolean m_optionsMenuIsVisible = false;
1190 public boolean onCreateOptionsMenu(Menu menu)
1191 {
1192 menu.clear();
1193 return true;
1194 }
1195 public boolean onPrepareOptionsMenu(Menu menu)
1196 {
1197 m_optionsMenuIsVisible = true;
1199 setActionBarVisibility(res && menu.size() > 0);
1200 return res;
1201 }
1202
1203 public boolean onOptionsItemSelected(MenuItem item)
1204 {
1205 return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked());
1206 }
1207
1208 public void onOptionsMenuClosed(Menu menu)
1209 {
1210 m_optionsMenuIsVisible = false;
1212 }
1213
1214 public void resetOptionsMenu()
1215 {
1216 m_activity.invalidateOptionsMenu();
1217 }
1218
1219 private boolean m_contextMenuVisible = false;
1220 public void onCreateContextMenu(ContextMenu menu,
1221 View v,
1222 ContextMenuInfo menuInfo)
1223 {
1224 menu.clearHeader();
1226 m_contextMenuVisible = true;
1227 }
1228
1229 public void onCreatePopupMenu(Menu menu)
1230 {
1232 m_contextMenuVisible = true;
1233 }
1234
1235 public void onContextMenuClosed(Menu menu)
1236 {
1237 if (!m_contextMenuVisible)
1238 return;
1239 m_contextMenuVisible = false;
1241 }
1242
1243 public boolean onContextItemSelected(MenuItem item)
1244 {
1245 m_contextMenuVisible = false;
1246 return QtNative.onContextItemSelected(item.getItemId(), item.isChecked());
1247 }
1248
1249 public void openContextMenu(final int x, final int y, final int w, final int h)
1250 {
1251 m_layout.postDelayed(new Runnable() {
1252 @Override
1253 public void run() {
1254 m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(w, h, x, y), false);
1255 PopupMenu popup = new PopupMenu(m_activity, m_editText);
1256 QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
1257 popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
1258 @Override
1259 public boolean onMenuItemClick(MenuItem menuItem) {
1260 return QtActivityDelegate.this.onContextItemSelected(menuItem);
1261 }
1262 });
1263 popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
1264 @Override
1265 public void onDismiss(PopupMenu popupMenu) {
1266 QtActivityDelegate.this.onContextMenuClosed(popupMenu.getMenu());
1267 }
1268 });
1269 popup.show();
1270 }
1271 }, 100);
1272 }
1273
1274 public void closeContextMenu()
1275 {
1276 m_activity.closeContextMenu();
1277 }
1278
1279 private void setActionBarVisibility(boolean visible)
1280 {
1281 if (m_activity.getActionBar() == null)
1282 return;
1283 if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible)
1284 m_activity.getActionBar().hide();
1285 else
1286 m_activity.getActionBar().show();
1287 }
1288
1289 public void insertNativeView(int id, View view, int x, int y, int w, int h) {
1290 if (m_dummyView != null) {
1291 m_layout.removeView(m_dummyView);
1292 m_dummyView = null;
1293 }
1294
1295 if (m_nativeViews.containsKey(id))
1296 m_layout.removeView(m_nativeViews.remove(id));
1297
1298 if (w < 0 || h < 0) {
1299 view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1300 ViewGroup.LayoutParams.MATCH_PARENT));
1301 } else {
1302 view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
1303 }
1304
1305 view.setId(id);
1306 m_layout.addView(view);
1307 m_nativeViews.put(id, view);
1308 }
1309
1310 public void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth) {
1311 if (m_surfaces.size() == 0) {
1312 TypedValue attr = new TypedValue();
1313 m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground, attr, true);
1314 if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT && attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
1315 m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(attr.data));
1316 } else {
1317 m_activity.getWindow().setBackgroundDrawable(m_activity.getResources().getDrawable(attr.resourceId, m_activity.getTheme()));
1318 }
1319 if (m_dummyView != null) {
1320 m_layout.removeView(m_dummyView);
1321 m_dummyView = null;
1322 }
1323 }
1324
1325 if (m_surfaces.containsKey(id))
1326 m_layout.removeView(m_surfaces.remove(id));
1327
1328 QtSurface surface = new QtSurface(m_activity, id, onTop, imageDepth);
1329 if (w < 0 || h < 0) {
1330 surface.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1331 ViewGroup.LayoutParams.MATCH_PARENT));
1332 } else {
1333 surface.setLayoutParams( new QtLayout.LayoutParams(w, h, x, y));
1334 }
1335
1336 // Native views are always inserted in the end of the stack (i.e., on top).
1337 // All other views are stacked based on the order they are created.
1338 final int surfaceCount = getSurfaceCount();
1339 m_layout.addView(surface, surfaceCount);
1340
1341 m_surfaces.put(id, surface);
1342 if (!m_splashScreenSticky)
1343 hideSplashScreen();
1344 }
1345
1346 public void setSurfaceGeometry(int id, int x, int y, int w, int h) {
1347 if (m_surfaces.containsKey(id)) {
1348 QtSurface surface = m_surfaces.get(id);
1349 surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
1350 } else if (m_nativeViews.containsKey(id)) {
1351 View view = m_nativeViews.get(id);
1352 view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
1353 } else {
1354 Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
1355 return;
1356 }
1357 }
1358
1359 public void destroySurface(int id) {
1360 View view = null;
1361
1362 if (m_surfaces.containsKey(id)) {
1363 view = m_surfaces.remove(id);
1364 } else if (m_nativeViews.containsKey(id)) {
1365 view = m_nativeViews.remove(id);
1366 } else {
1367 Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
1368 }
1369
1370 if (view == null)
1371 return;
1372
1373 // Keep last frame in stack until it is replaced to get correct
1374 // shutdown transition
1375 if (m_surfaces.size() == 0 && m_nativeViews.size() == 0) {
1376 m_dummyView = view;
1377 } else {
1378 m_layout.removeView(view);
1379 }
1380 }
1381
1382 public int getSurfaceCount()
1383 {
1384 return m_surfaces.size();
1385 }
1386
1387 public void bringChildToFront(int id)
1388 {
1389 View view = m_surfaces.get(id);
1390 if (view != null) {
1391 final int surfaceCount = getSurfaceCount();
1392 if (surfaceCount > 0)
1393 m_layout.moveChild(view, surfaceCount - 1);
1394 return;
1395 }
1396
1397 view = m_nativeViews.get(id);
1398 if (view != null)
1399 m_layout.moveChild(view, -1);
1400 }
1401
1402 public void bringChildToBack(int id)
1403 {
1404 View view = m_surfaces.get(id);
1405 if (view != null) {
1406 m_layout.moveChild(view, 0);
1407 return;
1408 }
1409
1410 view = m_nativeViews.get(id);
1411 if (view != null) {
1412 final int index = getSurfaceCount();
1413 m_layout.moveChild(view, index);
1414 }
1415 }
1416
1417 public boolean dispatchGenericMotionEvent (MotionEvent ev)
1418 {
1419 if (m_started && QtNative.dispatchGenericMotionEvent(ev))
1420 return true;
1421
1422 try {
1423 return (Boolean) m_super_dispatchGenericMotionEvent.invoke(m_activity, ev);
1424 } catch (Exception e) {
1425 e.printStackTrace();
1426 }
1427 return false;
1428 }
1429
1430 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
1431 {
1432 QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults);
1433 }
1434}
QHash< int, AndroidSurfaceClient * > m_surfaces
Definition main.cpp:8
void clear()
Removes all the menu's actions.
Definition qmenu.cpp:2203
QSize size
the size of the widget excluding any window frame
Definition qwidget.h:113
void setPosition(final int x, final int y)
void setPosition(final int x, final int y, final int buttons, CursorHandle cursorHandle, CursorHandle leftSelectionHandle, CursorHandle rightSelectionHandle)
static void runIfNeeded(Context context, boolean extractDarkMode)
static void setup(Bundle loaderParams)
void insertNativeView(int id, View view, int x, int y, int w, int h)
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
void notifyObjectHide(int viewId, int parentId)
void notifyValueChanged(int viewId, String value)
void showSoftwareKeyboard(final int x, final int y, final int width, final int height, final int inputHints, final int enterKeyType)
boolean onKeyUp(int keyCode, KeyEvent event)
void onRestoreInstanceState(Bundle savedInstanceState)
void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth)
void onConfigurationChanged(Configuration configuration)
boolean setKeyboardVisibility(boolean visibility, long timeStamp)
boolean onKeyDown(int keyCode, KeyEvent event)
boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams)
void onActivityResult(int requestCode, int resultCode, Intent data)
void setSurfaceGeometry(int id, int x, int y, int w, int h)
void openContextMenu(final int x, final int y, final int w, final int h)
void updateHandles(int mode, int editX, int editY, int editButtons, int x1, int y1, int x2, int y2, boolean rtl)
void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
void setInputType(int m_inputType)
void setImeOptions(int m_imeOptions)
void setInitialCapsMode(int m_initialCapsMode)
LayoutParams(int width, int height, int x, int y)
void setActivityDisplayRotation(int rotation)
Definition QtLayout.java:32
void setLayoutParams(final View childView, final ViewGroup.LayoutParams params, final boolean forceRedraw)
void moveChild(View view, int index)
void setNativeOrientation(int orientation)
Definition QtLayout.java:37
static boolean startApplication(String params, String mainLib)
static native void handleScreenAdded(int displayId)
static native void keyUp(int key, int unicode, int modifier, boolean autoRepeat)
static native void terminateQt()
static void setEnvironmentVariables(String environmentVariables)
static native void fillContextMenu(Menu menu)
static native void onActivityResult(int requestCode, int resultCode, Intent data)
static native void keyboardGeometryChanged(int x, int y, int width, int height)
static native void sendRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
static native boolean dispatchGenericMotionEvent(MotionEvent ev)
static void setApplicationState(int state)
static void keyboardVisibilityUpdated(boolean visibility)
static native boolean onPrepareOptionsMenu(Menu menu)
static native boolean onContextItemSelected(int itemId, boolean checked)
static String loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)
static native void onCreateContextMenu(ContextMenu menu)
static native boolean onOptionsItemSelected(int itemId, boolean checked)
static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
static void loadBundledLibraries(final ArrayList< String > libraries, final String nativeLibraryDir)
static void setEnvironmentVariable(String key, String value)
static native void handleOrientationChanged(int newRotation, int nativeOrientation)
static native void onNewIntent(Intent data)
static native boolean dispatchKeyEvent(KeyEvent event)
static native void onContextMenuClosed(Menu menu)
static native void updateWindow()
static native void keyDown(int key, int unicode, int modifier, boolean autoRepeat)
static native void handleRefreshRateChanged(float refreshRate)
static native boolean updateNativeActivity()
static void loadQtLibraries(final ArrayList< String > libraries)
static native void onOptionsMenuClosed(Menu menu)
static void setClassLoader(ClassLoader classLoader)
double e
QMap< Name, StatePointer > Bundle
Definition lalr.h:47
struct wl_display * display
Definition linuxdmabuf.h:41
static Q_CONSTINIT QBasicAtomicInt running
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLint location
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLfloat GLfloat GLfloat x1
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLint GLsizei width
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint y
GLfloat GLfloat GLfloat GLfloat h
struct _cl_event * event
GLuint res
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLfixed GLfixed x2
struct _XDisplay Display
const char className[16]
[1]
Definition qwizard.cpp:100
XID Window
QFileInfo info(fileName)
[8]
QPropertyAnimation animation
[0]
QRect r1(100, 200, 11, 16)
[0]
QRect r2(QPoint(100, 200), QSize(11, 16))
QGraphicsItem * item
aWidget window() -> setWindowTitle("New Window Title")
[2]
QMenu menu
[5]
QQuickView * view
[0]