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