Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
QtActivityDelegate.java
Go to the documentation of this file.
1// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2023 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.pm.ActivityInfo;
10import android.content.pm.PackageManager;
11import android.content.res.Configuration;
12import android.graphics.Color;
13import android.graphics.drawable.ColorDrawable;
14import android.graphics.drawable.Drawable;
15import android.graphics.Rect;
16import android.os.Build;
17import android.util.DisplayMetrics;
18import android.util.Log;
19import android.util.TypedValue;
20import android.view.Display;
21import android.view.ViewTreeObserver;
22import android.view.animation.AccelerateInterpolator;
23import android.view.animation.AlphaAnimation;
24import android.view.animation.Animation;
25import android.view.Menu;
26import android.view.View;
27import android.view.ViewConfiguration;
28import android.view.ViewGroup;
29import android.view.Window;
30import android.view.WindowInsetsController;
31import android.widget.ImageView;
32import android.widget.PopupMenu;
33
34import java.util.HashMap;
35
36class QtActivityDelegate extends QtActivityDelegateBase
37{
38 private static final String QtTAG = "QtActivityDelegate";
39
40 private QtRootLayout m_layout = null;
41 private ImageView m_splashScreen = null;
42 private boolean m_splashScreenSticky = false;
43
44 private View m_dummyView = null;
45 private HashMap<Integer, View> m_nativeViews = new HashMap<Integer, View>();
46
47
48 QtActivityDelegate(Activity activity)
49 {
50 super(activity);
51
52 setActionBarVisibility(false);
53 setActivityBackgroundDrawable();
54 }
55
56
57 @UsedFromNativeCode
58 @Override
59 QtLayout getQtLayout()
60 {
61 return m_layout;
62 }
63
64 @UsedFromNativeCode
65 @Override
66 void setSystemUiVisibility(int systemUiVisibility)
67 {
68 QtNative.runAction(() -> {
69 m_displayManager.setSystemUiVisibility(systemUiVisibility);
70 m_layout.requestLayout();
71 QtNative.updateWindow();
72 });
73 }
74
75 @Override
76 public boolean updateActivityAfterRestart(Activity activity) {
77 boolean updated = super.updateActivityAfterRestart(activity);
78 // TODO verify whether this is even needed, the last I checked the initMembers
79 // recreates the layout anyway
80 // update the new activity content view to old layout
81 ViewGroup layoutParent = (ViewGroup)m_layout.getParent();
82 if (layoutParent != null)
83 layoutParent.removeView(m_layout);
84
85 m_activity.setContentView(m_layout);
86
87 return updated;
88 }
89
90 @Override
91 void startNativeApplicationImpl(String appParams, String mainLib)
92 {
93 m_layout.getViewTreeObserver().addOnGlobalLayoutListener(
94 new ViewTreeObserver.OnGlobalLayoutListener() {
95 @Override
96 public void onGlobalLayout() {
97 QtNative.startApplication(appParams, mainLib);
98 m_layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
99 }
100 });
101 }
102
103 @Override
104 protected void setUpLayout()
105 {
106 int orientation = m_activity.getResources().getConfiguration().orientation;
107 m_layout = new QtRootLayout(m_activity);
108
109 setUpSplashScreen(orientation);
110 m_activity.registerForContextMenu(m_layout);
111 m_activity.setContentView(m_layout,
112 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
113 ViewGroup.LayoutParams.MATCH_PARENT));
114 QtDisplayManager.handleOrientationChanges(m_activity);
115
116 handleUiModeChange(m_activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
117
118 Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
119 ? m_activity.getWindowManager().getDefaultDisplay()
120 : m_activity.getDisplay();
121 QtDisplayManager.handleRefreshRateChanged(QtDisplayManager.getRefreshRate(display));
122
123 m_layout.getViewTreeObserver().addOnPreDrawListener(() -> {
124 if (!m_inputDelegate.isKeyboardVisible())
125 return true;
126
127 Rect r = new Rect();
128 m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
129 DisplayMetrics metrics = new DisplayMetrics();
130 m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
131 final int kbHeight = metrics.heightPixels - r.bottom;
132 if (kbHeight < 0) {
133 m_inputDelegate.setKeyboardVisibility(false, System.nanoTime());
134 return true;
135 }
136 final int[] location = new int[2];
137 m_layout.getLocationOnScreen(location);
138 QtInputDelegate.keyboardGeometryChanged(location[0], r.bottom - location[1],
139 r.width(), kbHeight);
140 return true;
141 });
142 registerGlobalFocusChangeListener(m_layout);
143 m_inputDelegate.setEditPopupMenu(new EditPopupMenu(m_activity, m_layout));
144 }
145
146 @Override
147 protected void setUpSplashScreen(int orientation)
148 {
149 try {
150 ActivityInfo info = m_activity.getPackageManager().getActivityInfo(
151 m_activity.getComponentName(),
152 PackageManager.GET_META_DATA);
153
154 String splashScreenKey = "android.app.splash_screen_drawable_"
155 + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
156 if (!info.metaData.containsKey(splashScreenKey))
157 splashScreenKey = "android.app.splash_screen_drawable";
158
159 if (info.metaData.containsKey(splashScreenKey)) {
160 m_splashScreenSticky =
161 info.metaData.containsKey("android.app.splash_screen_sticky") &&
162 info.metaData.getBoolean("android.app.splash_screen_sticky");
163
164 int id = info.metaData.getInt(splashScreenKey);
165 m_splashScreen = new ImageView(m_activity);
166 m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(
167 id, m_activity.getTheme()));
168 m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
169 m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(
170 ViewGroup.LayoutParams.MATCH_PARENT,
171 ViewGroup.LayoutParams.MATCH_PARENT));
172 m_layout.addView(m_splashScreen);
173 }
174 } catch (Exception e) {
175 e.printStackTrace();
176 }
177 }
178
179 @Override
180 protected void hideSplashScreen(final int duration)
181 {
182 QtNative.runAction(() -> {
183 if (m_splashScreen == null)
184 return;
185
186 if (duration <= 0) {
187 m_layout.removeView(m_splashScreen);
188 m_splashScreen = null;
189 return;
190 }
191
192 final Animation fadeOut = new AlphaAnimation(1, 0);
193 fadeOut.setInterpolator(new AccelerateInterpolator());
194 fadeOut.setDuration(duration);
195
196 fadeOut.setAnimationListener(new Animation.AnimationListener() {
197 @Override
198 public void onAnimationEnd(Animation animation) {
199 hideSplashScreen(0);
200 }
201
202 @Override
203 public void onAnimationRepeat(Animation animation) {
204 }
205
206 @Override
207 public void onAnimationStart(Animation animation) {
208 }
209 });
210
211 m_splashScreen.startAnimation(fadeOut);
212 });
213 }
214
215 @UsedFromNativeCode
216 public void initializeAccessibility()
217 {
218 QtNative.runAction(() -> {
219 // FIXME make QtAccessibilityDelegate window based
220 if (m_layout != null)
221 m_accessibilityDelegate = new QtAccessibilityDelegate(m_layout);
222 else
223 Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
224 });
225 }
226
227 @UsedFromNativeCode
228 public void resetOptionsMenu()
229 {
230 QtNative.runAction(() -> m_activity.invalidateOptionsMenu());
231 }
232
233 @UsedFromNativeCode
234 public void openOptionsMenu()
235 {
236 QtNative.runAction(() -> m_activity.openOptionsMenu());
237 }
238
239 private boolean m_contextMenuVisible = false;
240
241 public void onCreatePopupMenu(Menu menu)
242 {
243 QtNative.fillContextMenu(menu);
244 m_contextMenuVisible = true;
245 }
246
247 @UsedFromNativeCode
248 @Override
249 public void openContextMenu(final int x, final int y, final int w, final int h)
250 {
251 m_layout.postDelayed(() -> {
252 final QtEditText focusedEditText = m_inputDelegate.getCurrentQtEditText();
253 if (focusedEditText == null) {
254 Log.w(QtTAG, "No focused view when trying to open context menu");
255 return;
256 }
257 m_layout.setLayoutParams(focusedEditText, new QtLayout.LayoutParams(w, h, x, y), false);
258 PopupMenu popup = new PopupMenu(m_activity, focusedEditText);
259 QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
260 popup.setOnMenuItemClickListener(menuItem ->
261 m_activity.onContextItemSelected(menuItem));
262 popup.setOnDismissListener(popupMenu ->
263 m_activity.onContextMenuClosed(popupMenu.getMenu()));
264 popup.show();
265 }, 100);
266 }
267
268 @UsedFromNativeCode
269 public void closeContextMenu()
270 {
271 QtNative.runAction(() -> m_activity.closeContextMenu());
272 }
273
274 @Override
275 void setActionBarVisibility(boolean visible)
276 {
277 if (m_activity.getActionBar() == null)
278 return;
279 if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible)
280 m_activity.getActionBar().hide();
281 else
282 m_activity.getActionBar().show();
283 }
284
285 @UsedFromNativeCode
286 @Override
287 public void addTopLevelWindow(final QtWindow window)
288 {
289 if (window == null)
290 return;
291
292 QtNative.runAction(()-> {
293 if (m_topLevelWindows.size() == 0) {
294 if (m_dummyView != null) {
295 m_layout.removeView(m_dummyView);
296 m_dummyView = null;
297 }
298 }
299
300 window.setLayoutParams(new ViewGroup.LayoutParams(
301 ViewGroup.LayoutParams.MATCH_PARENT,
302 ViewGroup.LayoutParams.MATCH_PARENT));
303
304 m_layout.addView(window, m_topLevelWindows.size());
305 m_topLevelWindows.put(window.getId(), window);
306 if (!m_splashScreenSticky)
307 hideSplashScreen();
308 });
309 }
310
311 @UsedFromNativeCode
312 @Override
313 void removeTopLevelWindow(final int id)
314 {
315 QtNative.runAction(()-> {
316 if (m_topLevelWindows.containsKey(id)) {
317 QtWindow window = m_topLevelWindows.remove(id);
318 if (m_topLevelWindows.isEmpty()) {
319 // Keep last frame in stack until it is replaced to get correct
320 // shutdown transition
321 m_dummyView = window;
322 } else {
323 m_layout.removeView(window);
324 }
325 }
326 });
327 }
328
329 @UsedFromNativeCode
330 @Override
331 void bringChildToFront(final int id)
332 {
333 QtNative.runAction(() -> {
334 QtWindow window = m_topLevelWindows.get(id);
335 if (window != null)
336 m_layout.moveChild(window, m_topLevelWindows.size() - 1);
337 });
338 }
339
340 @UsedFromNativeCode
341 @Override
342 void bringChildToBack(int id)
343 {
344 QtNative.runAction(() -> {
345 QtWindow window = m_topLevelWindows.get(id);
346 if (window != null)
347 m_layout.moveChild(window, 0);
348 });
349 }
350
351 @Override
352 QtAccessibilityDelegate createAccessibilityDelegate()
353 {
354 if (m_layout != null)
355 return new QtAccessibilityDelegate(m_layout);
356
357 Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
358 return null;
359 }
360
361 private void setActivityBackgroundDrawable()
362 {
363 TypedValue attr = new TypedValue();
364 m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground,
365 attr, true);
366 Drawable backgroundDrawable;
367 if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
368 attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
369 backgroundDrawable = new ColorDrawable(attr.data);
370 } else {
371 backgroundDrawable = m_activity.getResources().
372 getDrawable(attr.resourceId, m_activity.getTheme());
373 }
374
375 m_activity.getWindow().setBackgroundDrawable(backgroundDrawable);
376 }
377
378 // TODO: QTBUG-122761 To be removed after QtAndroidAutomotive does not depend on it.
379 @UsedFromNativeCode
380 public void insertNativeView(int id, View view, int x, int y, int w, int h)
381 {
382 QtNative.runAction(()-> {
383 if (m_dummyView != null) {
384 m_layout.removeView(m_dummyView);
385 m_dummyView = null;
386 }
387
388 if (m_nativeViews.containsKey(id))
389 m_layout.removeView(m_nativeViews.remove(id));
390
391 if (w < 0 || h < 0) {
392 view.setLayoutParams(new ViewGroup.LayoutParams(
393 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
394 } else {
395 view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
396 }
397
398 view.setId(id);
399 m_layout.addView(view);
400 m_nativeViews.put(id, view);
401 });
402 }
403
404 // TODO: QTBUG-122761 To be removed after QtAndroidAutomotive does not depend on it.
405 @UsedFromNativeCode
406 public void setNativeViewGeometry(int id, int x, int y, int w, int h)
407 {
408 QtNative.runAction(() -> {
409 if (m_nativeViews.containsKey(id)) {
410 View view = m_nativeViews.get(id);
411 view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
412 } else {
413 Log.e(QtTAG, "View " + id + " not found!");
414 }
415 });
416 }
417}
static QtJniTypes::QtInputDelegate m_inputDelegate
struct wl_display * display
Definition linuxdmabuf.h:41
GLint location
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLboolean r
[2]
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLint y
GLfloat GLfloat GLfloat GLfloat h
XID Drawable
struct _XDisplay Display
QPropertyAnimation animation
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QMenu menu
[5]
QHostInfo info
[0]
QQuickView * view
[0]