From 6e32a2e065a65f081b0b52f9ba768f18572545e1 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Thu, 28 Nov 2024 21:51:04 +0200 Subject: [PATCH] Android: improve fullscreen and maximized states handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework fullscreen and maximized/expanded states handling by simplifying and re-organizing the code, removing some unnecessary code. Also, use newer APIs and handling the cutout regions. For expanded mode, use transparent instead of translucent so that the user can decide what color to use if needed, and in any case using the translucent flags is deprecated. You might still notice some artifacts as in QTBUG-88676, a fix for that is outside the scope of this patch. When going off of fullscreen mode one some cases you might notice a white/black black at the bottom and that's because QtRootLayout.onSizeChanged() is reporting wrong available size which is also an existing issue and outside of this scope. Fixes: QTBUG-96105 Fixes: QTBUG-101968 Fixes: QTBUG-127394 Fixes: QTBUG-121820 Task-number: QTBUG-109878 Task-number: QTBUG-119594 Change-Id: I586775a1d0414ec0adbc968d50b9c1a1ce466422 Reviewed-by: Tor Arne Vestbø (cherry picked from commit 0abcb9bef8a7cb85df006adfed51bc9258868ed2) Reviewed-by: Petri Virkkunen --- .../qtproject/qt/android/QtActivityBase.java | 12 +- .../qt/android/QtActivityDelegate.java | 4 +- .../qt/android/QtActivityDelegateBase.java | 2 +- .../qt/android/QtDisplayManager.java | 134 +++++++++++------- .../qt/android/QtWindowInterface.java | 2 +- .../android/qandroidplatformwindow.cpp | 17 +-- .../android/qandroidplatformwindow.h | 7 - 7 files changed, 103 insertions(+), 75 deletions(-) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java index 248c0696680..79c67d87823 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java @@ -157,7 +157,7 @@ public class QtActivityBase extends Activity m_delegate.displayManager().registerDisplayListener(); QtNative.updateWindow(); // Suspending the app clears the immersive mode, so we need to set it again. - m_delegate.displayManager().updateFullScreen(); + m_delegate.displayManager().reinstateFullScreen(); } } @@ -290,8 +290,9 @@ public class QtActivityBase extends Activity { super.onRestoreInstanceState(savedInstanceState); QtNative.setStarted(savedInstanceState.getBoolean("Started")); - int savedSystemUiVisibility = savedInstanceState.getInt("SystemUiVisibility"); - m_delegate.displayManager().setSystemUiVisibility(savedSystemUiVisibility); + boolean isFullScreen = savedInstanceState.getBoolean("isFullScreen"); + boolean expandedToCutout = savedInstanceState.getBoolean("expandedToCutout"); + m_delegate.displayManager().setSystemUiVisibility(isFullScreen, expandedToCutout); // FIXME restore all surfaces } @@ -307,7 +308,8 @@ public class QtActivityBase extends Activity protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putInt("SystemUiVisibility", m_delegate.displayManager().systemUiVisibility()); + outState.putBoolean("isFullScreen", m_delegate.displayManager().isFullScreen()); + outState.putBoolean("expandedToCutout", m_delegate.displayManager().expandedToCutout()); outState.putBoolean("Started", QtNative.getStateDetails().isStarted); } @@ -316,7 +318,7 @@ public class QtActivityBase extends Activity { super.onWindowFocusChanged(hasFocus); if (hasFocus) - m_delegate.displayManager().updateFullScreen(); + m_delegate.displayManager().reinstateFullScreen(); } @Override diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index fdee51e419f..123f25596cc 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -88,14 +88,14 @@ class QtActivityDelegate extends QtActivityDelegateBase } @Override - public void setSystemUiVisibility(int systemUiVisibility) + public void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) { if (m_layout == null) return; QtNative.runAction(() -> { if (m_layout != null) { - m_displayManager.setSystemUiVisibility(systemUiVisibility); + m_displayManager.setSystemUiVisibility(isFullScreen, expandedToCutout); m_layout.requestLayout(); QtNative.updateWindow(); } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java index 5bca2bca975..773ec0afca7 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java @@ -60,7 +60,7 @@ abstract class QtActivityDelegateBase m_activity = activity; QtNative.setActivity(m_activity); m_displayManager = new QtDisplayManager(m_activity); - m_inputDelegate = new QtInputDelegate(m_displayManager::updateFullScreen); + m_inputDelegate = new QtInputDelegate(m_displayManager::reinstateFullScreen); m_accessibilityDelegate = new QtAccessibilityDelegate(); } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java index 6c8fbfd50bf..6f97f052aae 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java @@ -16,12 +16,19 @@ import android.view.Surface; import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; import android.view.WindowMetrics; +import android.view.WindowInsetsController; +import android.view.Window; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import android.graphics.Color; +import android.util.TypedValue; +import android.content.res.Resources.Theme; + class QtDisplayManager { // screen methods @@ -38,11 +45,8 @@ class QtDisplayManager { static native void handleScreenRemoved(int displayId); // screen methods - // Keep in sync with QtAndroid::SystemUiVisibility in androidjnimain.h - static final int SYSTEM_UI_VISIBILITY_NORMAL = 0; - static final int SYSTEM_UI_VISIBILITY_FULLSCREEN = 1; - static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2; - private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL; + private boolean m_isFullScreen = false; + private boolean m_expandedToCutout = false; private static int m_previousRotation = -1; @@ -123,62 +127,96 @@ class QtDisplayManager { displayManager.unregisterDisplayListener(m_displayListener); } - void setSystemUiVisibility(int systemUiVisibility) + void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) { - if (m_systemUiVisibility == systemUiVisibility) + if (m_isFullScreen == isFullScreen && m_expandedToCutout == expandedToCutout) return; - m_systemUiVisibility = systemUiVisibility; + m_isFullScreen = isFullScreen; + m_expandedToCutout = expandedToCutout; + Window window = m_activity.getWindow(); + View decorView = window.getDecorView(); - int systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_VISIBLE; - switch (m_systemUiVisibility) { - case SYSTEM_UI_VISIBILITY_NORMAL: - m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + int cutoutMode; + if (m_isFullScreen || m_expandedToCutout) { + window.setDecorFitsSystemWindows(false); + cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } else { + window.setDecorFitsSystemWindows(true); + cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; + } + LayoutParams layoutParams = window.getAttributes(); + layoutParams.layoutInDisplayCutoutMode = cutoutMode; + window.setAttributes(layoutParams); + + final WindowInsetsController insetsControl = window.getInsetsController(); + if (insetsControl != null) { + int sysBarsBehavior; + if (m_isFullScreen) { + insetsControl.hide(WindowInsets.Type.systemBars()); + sysBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + } else { + insetsControl.show(WindowInsets.Type.systemBars()); + sysBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT; } - break; - case SYSTEM_UI_VISIBILITY_FULLSCREEN: - m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.INVISIBLE; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; + insetsControl.setSystemBarsBehavior(sysBarsBehavior); + } + + + } else { + int systemUiVisibility; + + if (m_isFullScreen || m_expandedToCutout) { + systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + if (m_isFullScreen) { + systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } - break; - case SYSTEM_UI_VISIBILITY_TRANSLUCENT: - m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - m_activity.getWindow().getAttributes().layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - } - break; + } else { + systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + } + + decorView.setSystemUiVisibility(systemUiVisibility); + } + + // Handle transparent status and navigation bars + if (m_expandedToCutout) { + window.setStatusBarColor(Color.TRANSPARENT); + window.setNavigationBarColor(Color.TRANSPARENT); + } else { + // Restore theme's system bars colors + Theme theme = m_activity.getTheme(); + TypedValue typedValue = new TypedValue(); + + theme.resolveAttribute(android.R.attr.statusBarColor, typedValue, true); + int defaultStatusBarColor = typedValue.data; + window.setStatusBarColor(defaultStatusBarColor); + + theme.resolveAttribute(android.R.attr.navigationBarColor, typedValue, true); + int defaultNavigationBarColor = typedValue.data; + window.setNavigationBarColor(defaultNavigationBarColor); } - m_activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags); } - int systemUiVisibility() + boolean isFullScreen() { - return m_systemUiVisibility; + return m_isFullScreen; } - void updateFullScreen() + boolean expandedToCutout() { - if (m_systemUiVisibility == SYSTEM_UI_VISIBILITY_FULLSCREEN) { - m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL; - setSystemUiVisibility(SYSTEM_UI_VISIBILITY_FULLSCREEN); + return m_expandedToCutout; + } + + void reinstateFullScreen() + { + if (m_isFullScreen) { + m_isFullScreen = false; + setSystemUiVisibility(true, m_expandedToCutout); } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java index 1fb312786ff..d42b3e6821e 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java @@ -7,5 +7,5 @@ interface QtWindowInterface { default void removeTopLevelWindow(final int id) { } default void bringChildToFront(final int id) { } default void bringChildToBack(int id) { } - default void setSystemUiVisibility(int systemUiVisibility) { } + default void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) { } } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 00e82f93bbd..eb1fdab20d1 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qandroidplatformwindow.h" +#include "androidbackendregister.h" #include "qandroidplatformopenglcontext.h" #include "qandroidplatformscreen.h" @@ -255,19 +256,13 @@ void QAndroidPlatformWindow::requestActivateWindow() void QAndroidPlatformWindow::updateSystemUiVisibility() { - Qt::WindowFlags flags = window()->flags(); - bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; + const int flags = window()->flags(); + const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; if (!isNonRegularWindow) { - SystemUiVisibility visibility; - if (m_windowState & Qt::WindowFullScreen) - visibility = SYSTEM_UI_VISIBILITY_FULLSCREEN; - else if (flags & Qt::MaximizeUsingFullscreenGeometryHint) - visibility = SYSTEM_UI_VISIBILITY_TRANSLUCENT; - else - visibility = SYSTEM_UI_VISIBILITY_NORMAL; - + const bool isFullScreen = (m_windowState & Qt::WindowFullScreen); + const bool expandedToCutout = (flags & Qt::MaximizeUsingFullscreenGeometryHint); QtAndroid::backendRegister()->callInterface( - "setSystemUiVisibility", jint(visibility)); + "setSystemUiVisibility", isFullScreen, expandedToCutout); } } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 4c6fb1f1c3c..c282adb9b0e 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -31,13 +31,6 @@ public: TextureView }; - // Keep synchronized with flags in ActivityDelegate.java - enum SystemUiVisibility { - SYSTEM_UI_VISIBILITY_NORMAL = 0, - SYSTEM_UI_VISIBILITY_FULLSCREEN = 1, - SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2 - }; - explicit QAndroidPlatformWindow(QWindow *window); void initialize() override;