Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
QtWindowInsetsController.java
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4package org.qtproject.qt.android;
5
6import android.app.Activity;
7import android.content.Context;
8import android.content.res.Resources;
9import android.content.res.TypedArray;
10import android.os.Build;
11import android.view.View;
12import android.view.WindowInsets;
13import android.view.WindowManager;
14import android.view.WindowInsetsController;
15import android.view.Window;
16
17import android.graphics.Color;
18import android.util.TypedValue;
19import android.content.res.Resources.Theme;
20
21class QtWindowInsetsController
22{
23 /*
24 * Convenience method to call the API deprecated in Android V (35),
25 * a no-op when the system enforces edge-to-edge for this app.
26 */
27 @SuppressWarnings ("deprecation")
28 private static void setDecorFitsSystemWindows(Window window, boolean enable)
29 {
30 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
31 return;
32 if (isEdgeToEdgeEnforced(window.getContext()))
33 return;
34 window.setDecorFitsSystemWindows(enable);
35 }
36
40 private static int themeCutoutMode(Context context)
41 {
42 int[] attrs = new int[] { android.R.attr.windowLayoutInDisplayCutoutMode };
43 TypedArray ta = context.getTheme().obtainStyledAttributes(attrs);
44 try {
45 return ta.getInt(0, WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT);
46 } finally {
47 ta.recycle();
48 }
49 }
50
51 private static void useCutoutShortEdges(Window window, boolean enabled)
52 {
53 if (window == null)
54 return;
55
56 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
57 WindowManager.LayoutParams layoutParams = window.getAttributes();
58 layoutParams.layoutInDisplayCutoutMode = enabled
59 ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
60 : themeCutoutMode(window.getContext());
61 window.setAttributes(layoutParams);
62 }
63 }
64
65 @UsedFromNativeCode
66 static void showNormal(Activity activity)
67 {
68 Window window = activity.getWindow();
69 if (window == null)
70 return;
71
72 final View decor = window.getDecorView();
73 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
74 setDecorFitsSystemWindows(window, true);
75 WindowInsetsController ctrl = window.getInsetsController();
76 if (ctrl != null) {
77 ctrl.show(WindowInsets.Type.systemBars());
78 ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT);
79 }
80 } else {
81 @SuppressWarnings("deprecation")
82 int flags = View.SYSTEM_UI_FLAG_VISIBLE; // clear all flags
83 setSystemUiVisibility(decor, flags);
84 }
85
86 setTransparentSystemBars(activity, false);
87 useCutoutShortEdges(window, false);
88
89 decor.post(() -> decor.requestApplyInsets());
90 }
91
92 /*
93 * Make system bars transparent for Andorid versions prior to Android 15.
94 */
95 @SuppressWarnings("deprecation")
96 private static void setTransparentSystemBars(Activity activity, boolean transparent)
97 {
98 Window window = activity.getWindow();
99 if (window == null)
100 return;
101
102 if (isEdgeToEdgeEnforced(activity))
103 return;
104
105 // These are needed to operate on system bar colors
106 window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
107 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
108 window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
109
110 if (transparent) {
111 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
112 window.setStatusBarColor(Color.TRANSPARENT);
113 window.setNavigationBarColor(Color.TRANSPARENT);
114 } else {
115 // Android 9 and prior doesn't add the semi-transparent bars
116 // to avoid low contrast system icons, so try to mimick it
117 // by taking the current color and only increase the opacity.
118 int statusBarColor = window.getStatusBarColor();
119 int transparentStatusBar = statusBarColor & 0x00FFFFFF;
120 window.setStatusBarColor(transparentStatusBar);
121
122 int navigationBarColor = window.getNavigationBarColor();
123 int semiTransparentNavigationBar = navigationBarColor & 0x7FFFFFFF;
124 window.setNavigationBarColor(semiTransparentNavigationBar);
125 }
126 } else {
127 // Restore theme's system bars colors
128 int defaultStatusBarColor = getThemeDefaultStatusBarColor(activity);
129 window.setStatusBarColor(defaultStatusBarColor);
130
131 int defaultNavigationBarColor = getThemeDefaultNavigationBarColor(activity);
132 window.setNavigationBarColor(defaultNavigationBarColor);
133 }
134 }
135
136 @UsedFromNativeCode
137 static void showExpanded(Activity activity)
138 {
139 Window window = activity.getWindow();
140 if (window == null)
141 return;
142
143 final View decor = window.getDecorView();
144 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
145 setDecorFitsSystemWindows(window, false);
146 WindowInsetsController ctrl = window.getInsetsController();
147 if (ctrl != null) {
148 ctrl.show(WindowInsets.Type.systemBars());
149 ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT);
150 }
151 } else {
152 @SuppressWarnings("deprecation")
153 int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
154 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
155 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
156 setSystemUiVisibility(decor, flags);
157 }
158
159 setTransparentSystemBars(activity, true);
160 useCutoutShortEdges(window, true);
161
162 decor.post(() -> decor.requestApplyInsets());
163 }
164
165 @UsedFromNativeCode
166 public static void showFullScreen(Activity activity)
167 {
168 Window window = activity.getWindow();
169 if (window == null)
170 return;
171
172 final View decor = window.getDecorView();
173 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
174 setDecorFitsSystemWindows(window, false);
175 WindowInsetsController ctrl = window.getInsetsController();
176 if (ctrl != null) {
177 ctrl.hide(WindowInsets.Type.systemBars());
178 ctrl.setSystemBarsBehavior(
179 WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
180 }
181 } else {
182 @SuppressWarnings("deprecation")
183 int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
184 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
185 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
186 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
187 | View.SYSTEM_UI_FLAG_FULLSCREEN
188 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
189 setSystemUiVisibility(decor, flags);
190 }
191
192 useCutoutShortEdges(window, true);
193
194 decor.post(() -> decor.requestApplyInsets());
195 }
196
197 /*
198 * The system enforces edge-to-edge only for apps targeting Android V (35)
199 * or later, regardless of the version the device runs.
200 */
201 private static boolean isEdgeToEdgeEnforced(Context context) {
202 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM)
203 return false;
204
205 final int targetSdk = context.getApplicationInfo().targetSdkVersion;
206 if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM)
207 return false;
208 if (targetSdk > Build.VERSION_CODES.VANILLA_ICE_CREAM)
209 return true;
210
211 // Apps targeting exactly Android V (35) can still opt out
212 int[] attrs = new int[] { android.R.attr.windowOptOutEdgeToEdgeEnforcement };
213 TypedArray ta = context.getTheme().obtainStyledAttributes(attrs);
214 try {
215 return !ta.getBoolean(0, false);
216 } finally {
217 ta.recycle();
218 }
219 }
220
221 static boolean isFullScreen(Activity activity)
222 {
223 Window window = activity.getWindow();
224 if (window == null)
225 return false;
226
227 final View decor = window.getDecorView();
228 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
229 WindowInsetsController controller = window.getInsetsController();
230 if (controller != null) {
231 final int behavior = controller.getSystemBarsBehavior();
232 return behavior == WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
233 }
234 } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
235 WindowInsets insets = decor.getRootWindowInsets();
236 if (insets != null)
237 return !insets.isVisible(WindowInsets.Type.statusBars());
238 } else {
239 @SuppressWarnings("deprecation")
240 int flags = decor.getSystemUiVisibility();
241 @SuppressWarnings("deprecation")
242 int immersiveMask = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
243 return (flags & immersiveMask) == immersiveMask;
244 }
245
246 return false;
247 }
248
249 static boolean isExpandedClientArea(Activity activity)
250 {
251 if (isEdgeToEdgeEnforced(activity))
252 return true;
253
254 @SuppressWarnings("deprecation")
255 int statusBarColor = activity.getWindow().getStatusBarColor();
256 // If the status bar is not fully opaque assume we have expanded client
257 // area and we're drawing under it.
258 int statusBarAlpha = statusBarColor >>> 24;
259 return statusBarAlpha != 0xFF;
260 }
261
262 static boolean decorFitsSystemWindows(Activity activity)
263 {
264 return !isFullScreen(activity) && !isExpandedClientArea(activity);
265 }
266
267 private static native void applySystemUiNative();
268
269 /*
270 * Some lifecycle events reset window state on the OS side, let the
271 * Qt side re-assert the mode it owns, if any beyond the default.
272 */
273 static void applySystemUi()
274 {
275 if (QtNative.getStateDetails().isStarted)
276 applySystemUiNative();
277 }
278
279 /*
280 * Convenience method to call deprecated API prior to Android R (30).
281 */
282 @SuppressWarnings ("deprecation")
283 private static void setSystemUiVisibility(View decorView, int flags)
284 {
285 decorView.setSystemUiVisibility(flags);
286 }
287
288 /*
289 * Set the status bar color scheme hint so that the system decides how to color the icons.
290 */
291 @UsedFromNativeCode
292 static void setStatusBarColorHint(Activity activity, boolean isLight)
293 {
294 Window window = activity.getWindow();
295 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
296 WindowInsetsController controller = window.getInsetsController();
297 if (controller != null) {
298 int lightStatusBarMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
299 int appearance = isLight ? lightStatusBarMask : 0;
300 controller.setSystemBarsAppearance(appearance, lightStatusBarMask);
301 }
302 } else {
303 @SuppressWarnings("deprecation")
304 int currentFlags = window.getDecorView().getSystemUiVisibility();
305 @SuppressWarnings("deprecation")
306 int lightStatusBarMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
307 int appearance = isLight
308 ? currentFlags | lightStatusBarMask
309 : currentFlags & ~lightStatusBarMask;
310 setSystemUiVisibility(window.getDecorView(), appearance);
311 }
312 }
313
314 /*
315 * Set the navigation bar color scheme hint so that the system decides how to color the icons.
316 */
317 @UsedFromNativeCode
318 static void setNavigationBarColorHint(Activity activity, boolean isLight)
319 {
320 Window window = activity.getWindow();
321 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
322 WindowInsetsController controller = window.getInsetsController();
323 if (controller != null) {
324 int lightNavigationBarMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
325 int appearance = isLight ? lightNavigationBarMask : 0;
326 controller.setSystemBarsAppearance(appearance, lightNavigationBarMask);
327 }
328 } else {
329 @SuppressWarnings("deprecation")
330 int currentFlags = window.getDecorView().getSystemUiVisibility();
331 @SuppressWarnings("deprecation")
332 int lightNavigationBarMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
333 int appearance = isLight
334 ? currentFlags | lightNavigationBarMask
335 : currentFlags & ~lightNavigationBarMask;
336 setSystemUiVisibility(window.getDecorView(), appearance);
337 }
338 }
339
340 private static int resolveColorAttribute(Activity activity, int attribute)
341 {
342 Theme theme = activity.getTheme();
343 Resources resources = activity.getResources();
344 TypedValue tv = new TypedValue();
345
346 if (theme.resolveAttribute(attribute, tv, true)) {
347 if (tv.resourceId != 0)
348 return resources.getColor(tv.resourceId, theme);
349 if (tv.type >= TypedValue.TYPE_FIRST_COLOR_INT && tv.type <= TypedValue.TYPE_LAST_COLOR_INT)
350 return tv.data;
351 }
352
353 return -1;
354 }
355
356 @SuppressWarnings("deprecation")
357 static int getThemeDefaultStatusBarColor(Activity activity)
358 {
359 if (isEdgeToEdgeEnforced(activity))
360 return -1;
361 return resolveColorAttribute(activity, android.R.attr.statusBarColor);
362 }
363
364 @SuppressWarnings("deprecation")
365 static int getThemeDefaultNavigationBarColor(Activity activity)
366 {
367 if (isEdgeToEdgeEnforced(activity))
368 return -1;
369 return resolveColorAttribute(activity, android.R.attr.navigationBarColor);
370 }
371
372 static void enableSystemBarsBackgroundDrawing(Window window)
373 {
374 // These are needed to operate on system bar colors
375 window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
376 @SuppressWarnings("deprecation")
377 final int translucentFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
378 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
379 window.clearFlags(translucentFlags);
380 }
381
382 @SuppressWarnings("deprecation")
383 static void setStatusBarColor(Window window, int color)
384 {
385 if (isEdgeToEdgeEnforced(window.getContext()))
386 return;
387 window.setStatusBarColor(color);
388 }
389
390 @SuppressWarnings("deprecation")
391 static void setNavigationBarColor(Window window, int color)
392 {
393 if (isEdgeToEdgeEnforced(window.getContext()))
394 return;
395 window.setNavigationBarColor(color);
396 }
397}
QPainter Context
static const QString context()
Definition java.cpp:396
Napi::TypedArray TypedArray
Definition qnapi_p.h:76
Q_CORE_EXPORT QtJniTypes::Activity activity()
self window
[2]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return
static struct AttrInfo attrs[]
EGLOutputLayerEXT EGLint attribute
GLbitfield flags
GLuint color
[setDefaultFactory]
GLboolean enable
#define enabled
manager post(request, myJson, this, [this](QRestReply &reply) { if(!reply.isSuccess()) { } if(std::optional json=reply.readJson()) { } })
if(foo.startsWith("("+type+") 0x")) ... QString hello("hello")
[0]