Android: improve fullscreen and maximized states handling

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ø <tor.arne.vestbo@qt.io>
(cherry picked from commit 0abcb9bef8a7cb85df006adfed51bc9258868ed2)
Reviewed-by: Petri Virkkunen <petri.virkkunen@qt.io>
This commit is contained in:
Assam Boudjelthia 2024-11-28 21:51:04 +02:00
parent b9c61b0f68
commit 6e32a2e065
7 changed files with 103 additions and 75 deletions

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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) { }
}

View File

@ -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<QtJniTypes::QtWindowInterface, void>(
"setSystemUiVisibility", jint(visibility));
"setSystemUiVisibility", isFullScreen, expandedToCutout);
}
}

View File

@ -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;