Mobile: unfiy transparent system-bars handling between iOS and Android

Also refactored the dark/light theme switching to use Qt's API, now
that these are available in 6.5
This commit is contained in:
Robert Griebl
2024-04-23 12:50:00 +02:00
parent fd92611a3e
commit 2ca640e95c
6 changed files with 102 additions and 131 deletions
+3 -5
View File
@@ -2,11 +2,9 @@
<resources>
<style name="splashScreenTheme" parent="@android:style/Theme.Material.NoActionBar">
<item name="android:windowBackground">@drawable/splashscreen</item>
<!-- Qt switches to a black status bar while displaying the internal splash screen, making it
flicker blue -> black -> blue. We better keep the default color, until this is fixed in Qt.
<item name="android:colorPrimaryDark">@color/blue_500</item>
<item name="android:colorPrimary">@color/blue_500</item>
<item name="android:windowLightStatusBar">false</item> -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>
@@ -11,20 +11,31 @@ import android.util.Log;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import androidx.core.view.WindowCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.core.graphics.Insets;
import io.sentry.SentryLevel;
import io.sentry.android.core.SentryAndroid;
public class ExtendedQtActivity extends QtActivity
{
public static native void changeUiTheme(boolean isDark);
public static native void changeScreenMargins(int left, int top, int right, int bottom);
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
boolean isDark = ((this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES);
changeUiTheme(isDark);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
// ViewCompat.getWindowInsetsController(getWindow().getDecorView()).setAppearanceLightNavigationBars(false);
ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView(), (view, windowInsets) -> {
int insetsTypes = WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars();
Insets insets = windowInsets.getInsets(insetsTypes);
changeScreenMargins(insets.left, insets.top, insets.right, insets.bottom);
return WindowInsetsCompat.CONSUMED;
});
Intent intent = getIntent();
if ((intent != null) && (intent.getAction() == Intent.ACTION_VIEW))
@@ -35,9 +46,6 @@ public class ExtendedQtActivity extends QtActivity
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
boolean isDark = ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES);
changeUiTheme(isDark);
}
public static native void openUrl(String url);
+2 -2
View File
@@ -12,8 +12,8 @@ ApplicationWindow {
width: 1280
height: 720
// ios: colored statusbar background
flags: Qt.Window | (Style.isIOS ? Qt.MaximizeUsingFullscreenGeometryHint : 0)
// colored statusbar background
flags: Qt.Window | ((Style.isAndroid || Style.isIOS) ? Qt.MaximizeUsingFullscreenGeometryHint : 0)
Binding { // used to apply the dark/light theme for the complete app (Style is a singleton)
target: Style
+1 -1
View File
@@ -22,7 +22,7 @@ Control {
}
Popup {
// iOS: black bar over the notch in portrait mode
// show a black bar over the notch / camera cutout
modal: false
enabled: false
closePolicy: Popup.NoAutoClose
+68 -112
View File
@@ -1,11 +1,13 @@
// Copyright (C) 2004-2024 Robert Griebl
// SPDX-License-Identifier: GPL-3.0-only
#include <QGuiApplication>
#include <QScreen>
#include <QFontDatabase>
#include <QQmlProperty>
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlProperty>
#include <QScreen>
#include <QStyleHints>
#include <QQmlApplicationEngine>
#include "common/config.h"
#include "common/application.h"
#include "qmlstyle.h"
@@ -14,71 +16,23 @@
# include <jni.h>
# include <QJniObject>
// WindowManager.LayoutParams
# define FLAG_TRANSLUCENT_STATUS 0x04000000
# define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000
// View
# define SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 0x00002000
static QMargins staticScreenMargins;
static bool darkThemeOS_value;
void androidSetScreenMargins(const QMargins &margins)
{
QmlStyle *inst = nullptr;
if (Application::inst() && Application::inst()->qmlEngine())
inst = Application::inst()->qmlEngine()->singletonInstance<QmlStyle *>("Mobile", "Style");
if (inst)
inst->setScreenMargins(margins);
else
staticScreenMargins = margins;
}
extern "C" JNIEXPORT void JNICALL
Java_de_brickforge_brickstore_ExtendedQtActivity_changeUiTheme(JNIEnv *, jobject, jboolean jisDark)
Java_de_brickforge_brickstore_ExtendedQtActivity_changeScreenMargins(JNIEnv *, jobject, jint left, jint top, jint right, jint bottom)
{
darkThemeOS_value = jisDark;
}
static bool darkThemeOS()
{
return darkThemeOS_value;
}
static void setStatusBarColor(const QColor &color)
{
if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
return;
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
QJniObject activity = QNativeInterface::QAndroidApplication::context();
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
window.callMethod<void>("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.callMethod<void>("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS);
QJniObject view = window.callObjectMethod("getDecorView", "()Landroid/view/View;");
int visibility = view.callMethod<int>("getSystemUiVisibility", "()I");
visibility &= ~SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
view.callMethod<void>("setSystemUiVisibility", "(I)V", visibility);
window.callMethod<void>("setStatusBarColor", "(I)V", color.rgba());
});
}
#else
# if defined(Q_CC_MSVC)
# pragma warning(push)
# pragma warning(disable: 4458)
# pragma warning(disable: 4201)
# endif
# include <QtGui/private/qguiapplication_p.h>
# if defined(Q_CC_MSVC)
# pragma warning(pop)
# endif
# include <QtGui/qpa/qplatformtheme.h>
static bool darkThemeOS()
{
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
return (theme->appearance() == QPlatformTheme::Appearance::Dark);
#else
return (theme->colorScheme() == Qt::ColorScheme::Dark);
#endif
}
return false;
}
static void setStatusBarColor(const QColor &color)
{
Q_UNUSED(color)
androidSetScreenMargins({ left, top, right, bottom });
}
#endif
@@ -104,12 +58,33 @@ QmlStyle::QmlStyle(QObject *parent)
}
});
connect(QGuiApplication::primaryScreen(), &QScreen::geometryChanged,
this, &QmlStyle::screenMarginsChanged);
#if defined(Q_OS_IOS)
static auto iosScreenMargins = []() -> QMargins {
auto screen = QGuiApplication::primaryScreen();
const auto available = screen->availableGeometry();
const auto full = screen->geometry();
return { available.left() - full.left(), available.top() - full.top(),
full.right() - available.right(), full.bottom() - available.bottom() };
};
m_screenMargins = iosScreenMargins();
qWarning() << "IOS SCREEN MARGINS CHANGED" << m_screenMargins;
connect(QGuiApplication::primaryScreen(), &QScreen::availableGeometryChanged,
this, &QmlStyle::screenMarginsChanged);
connect(QGuiApplication::primaryScreen(), &QScreen::orientationChanged,
this, &QmlStyle::screenMarginsChanged);
this, [this]() {
setScreenMargins(iosScreenMargins());
});
#elif defined(Q_OS_ANDROID)
m_screenDpr = QGuiApplication::primaryScreen()->devicePixelRatio();
m_screenMargins = staticScreenMargins;
#endif
}
void QmlStyle::setScreenMargins(const QMargins &newMargins)
{
if (m_screenMargins != newMargins) {
m_screenMargins = newMargins;
qWarning() << "SCREEN MARGINS CHANGED" << m_screenMargins;
emit screenMarginsChanged();
}
}
QSizeF QmlStyle::physicalScreenSize() const
@@ -127,8 +102,7 @@ bool QmlStyle::smallSize() const
bool QmlStyle::darkTheme() const
{
// 0: light, 1: dark, 2: system (see qquickmaterialstyle_p.h)
return m_theme.isValid() ? (m_theme.read().toInt() == 1) : false;
return m_theme == Application::DarkTheme;
}
QFont QmlStyle::monospaceFont() const
@@ -196,48 +170,22 @@ bool QmlStyle::isAndroid() const
int QmlStyle::topScreenMargin() const
{
if (isIOS()) {
const auto scr = QGuiApplication::primaryScreen();
return scr->availableGeometry().y();
} else {
return 0;
}
return m_screenMargins.top() / m_screenDpr;
}
int QmlStyle::bottomScreenMargin() const
{
if (isIOS()) {
const auto scr = QGuiApplication::primaryScreen();
return scr->geometry().bottom() - scr->availableGeometry().bottom();
} else {
return 0;
}
return m_screenMargins.bottom() / m_screenDpr;
}
int QmlStyle::leftScreenMargin() const
{
int lsm = 0;
if (isIOS()) {
const auto scr = QGuiApplication::primaryScreen();
lsm = (scr->orientation() == Qt::InvertedLandscapeOrientation)
? 0 : scr->availableGeometry().x();
}
//qWarning() << "Left screen margin:" << lsm;
return lsm;
return m_screenMargins.left() / m_screenDpr;
}
int QmlStyle::rightScreenMargin() const
{
int rsm = 0;
if (isIOS()) {
const auto scr = QGuiApplication::primaryScreen();
rsm = (scr->orientation() == Qt::LandscapeOrientation)
? 0 : (scr->geometry().right() - scr->availableGeometry().right());
}
//qWarning() << "Right screen margin:" << rsm;
return rsm;
return m_screenMargins.right() / m_screenDpr;
}
QObject *QmlStyle::rootWindow() const
@@ -259,34 +207,42 @@ void QmlStyle::setRootWindow(QObject *root)
m_backgroundColor = QQmlProperty(root, u"Material.backgroundColor"_qs, qmlContext(root));
m_accentTextColor = QQmlProperty(root, u"Material.primaryHighlightedTextColor"_qs, qmlContext(root));
m_accentColor = QQmlProperty(root, u"Material.accentColor"_qs, qmlContext(root));
m_theme = QQmlProperty(root, u"Material.theme"_qs, qmlContext(root));
m_materialTheme = QQmlProperty(root, u"Material.theme"_qs, qmlContext(root));
emit styleColorChanged();
m_theme.connectNotifySignal(this, SLOT(updateTheme()));
//m_materialTheme.connectNotifySignal(this, SLOT(updateTheme()));
connect(Config::inst(), &Config::uiThemeChanged,
this, &QmlStyle::updateTheme);
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged,
this, &QmlStyle::updateTheme);
updateTheme();
}
void QmlStyle::updateTheme()
{
// 0: light, 1: dark, 2: system (see qquickmaterialstyle_p.h)
int materialTheme;
Application::Theme theme;
bool systemIsDark = (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
switch (Config::inst()->uiTheme()) {
case Config::UITheme::Light: materialTheme = 0; break;
case Config::UITheme::Dark: materialTheme = 1; break;
case Config::UITheme::Light: theme = Application::LightTheme; break;
case Config::UITheme::Dark: theme = Application::DarkTheme; break;
default:
case Config::UITheme::SystemDefault: materialTheme = (darkThemeOS() ? 1 : 0); break;
case Config::UITheme::SystemDefault: theme = systemIsDark ? Application::DarkTheme
: Application::LightTheme; break;
}
setStatusBarColor(m_primaryColor.read().value<QColor>());
// 0: light, 1: dark, 2: system (see qquickmaterialstyle_p.h)
int materialThemeEnum = (theme == Application::DarkTheme) ? 1 : 0;
if (m_materialTheme.read().toInt() != materialThemeEnum)
m_materialTheme.write(materialThemeEnum);
if (m_theme.read().toInt() != materialTheme)
m_theme.write(materialTheme);
Application::inst()->setIconTheme(theme);
Application::inst()->setIconTheme(darkTheme() ? Application::DarkTheme : Application::LightTheme);
if (theme != m_theme) {
m_theme = theme;
emit darkThemeChanged(theme == Application::DarkTheme);
}
}
QColor QmlStyle::colorProperty(const QQmlProperty &property, const char *fallbackColor) const
+14 -5
View File
@@ -11,6 +11,10 @@
#include <QtQml/QQmlProperty>
#include <QtQml/QQmlEngine>
#include "common/application.h"
class QmlStylePrivate;
class QmlStyle : public QObject
{
@@ -76,12 +80,12 @@ signals:
void screenMarginsChanged();
void rootWindowChanged(); // dummy, never emitted
private slots:
void updateTheme();
private:
void updateTheme();
void setScreenMargins(const QMargins &newMargins);
QColor colorProperty(const QQmlProperty &property, const char *fallbackColor) const;
Application::Theme m_theme;
QPointer<QObject> m_root;
bool m_smallSize = false;
@@ -93,5 +97,10 @@ private:
QQmlProperty m_backgroundColor;
QQmlProperty m_accentTextColor;
QQmlProperty m_accentColor;
QQmlProperty m_theme;
};;
QQmlProperty m_materialTheme;
QMargins m_screenMargins;
qreal m_screenDpr = qreal(1);
friend void androidSetScreenMargins(const QMargins &margins);
};