Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: edge-to-edge mode on Android #52392

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<style name="BaseAppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:colorEdgeEffect">@color/gray4</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="colorAccent">@color/accent</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="popupTheme">@style/AppTheme.Popup</item>
Expand Down Expand Up @@ -59,6 +61,8 @@

<style name="BootTheme" parent="Theme.SplashScreen">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/bootsplash_logo</item>
<item name="windowSplashScreenBackground">@color/bootsplash_background</item>
</style>
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"expo-av": "14.0.7",
"expo-image": "1.12.15",
"expo-image-manipulator": "12.0.5",
"expo-navigation-bar": "~3.0.7",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add this library again, because we need to manage navigation bar button style (dark/light).

Since user can customize the theme of app independently to device settings we also have to manage it dynamically from JS code.

I checked and one method that we use (setButtonStyleAsync) doesn't use deprecated API. However I remember, that this particular library wasn't accepted in the first round of fixing the nav bar color management, so i'm happy to discuss further steps here 🙌

"fast-equals": "^4.0.3",
"focus-trap-react": "^10.2.3",
"howler": "^2.2.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
diff --git a/node_modules/react-native/Libraries/Modal/Modal.d.ts b/node_modules/react-native/Libraries/Modal/Modal.d.ts
index 4cc2df2..a501b27 100644
--- a/node_modules/react-native/Libraries/Modal/Modal.d.ts
+++ b/node_modules/react-native/Libraries/Modal/Modal.d.ts
@@ -94,6 +94,11 @@ export interface ModalPropsAndroid {
* Determines whether your modal should go under the system statusbar.
*/
statusBarTranslucent?: boolean | undefined;
+
+ /**
+ * Determines whether your modal should go under the system navigationbar.
+ */
+ navigationBarTranslucent?: boolean | undefined;
}

export type ModalProps = ModalBaseProps &
diff --git a/node_modules/react-native/Libraries/Modal/Modal.js b/node_modules/react-native/Libraries/Modal/Modal.js
index 1942d9e..1ffbe4c 100644
--- a/node_modules/react-native/Libraries/Modal/Modal.js
+++ b/node_modules/react-native/Libraries/Modal/Modal.js
@@ -95,6 +95,14 @@ export type Props = $ReadOnly<{|
*/
statusBarTranslucent?: ?boolean,

+ /**
+ * The `navigationBarTranslucent` prop determines whether your modal should go under
+ * the system navigationbar.
+ *
+ * See https://reactnative.dev/docs/modal.html#navigationbartranslucent-android
+ */
+ navigationBarTranslucent?: ?boolean,
+
/**
* The `hardwareAccelerated` prop controls whether to force hardware
* acceleration for the underlying window.
@@ -170,6 +178,14 @@ function confirmProps(props: Props) {
`Modal with '${props.presentationStyle}' presentation style and 'transparent' value is not supported.`,
);
}
+ if (
+ props.navigationBarTranslucent === true &&
+ props.statusBarTranslucent !== true
+ ) {
+ console.warn(
+ 'Modal with translucent navigation bar and without translucent status bar is not supported.',
+ );
+ }
}
}

@@ -291,6 +307,7 @@ class Modal extends React.Component<Props, State> {
onDismiss={onDismiss}
visible={this.props.visible}
statusBarTranslucent={this.props.statusBarTranslucent}
+ navigationBarTranslucent={this.props.navigationBarTranslucent}
identifier={this._identifier}
style={styles.modal}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
diff --git a/node_modules/react-native/React/Views/RCTModalHostView.h b/node_modules/react-native/React/Views/RCTModalHostView.h
index 2fcdcae..0469c23 100644
--- a/node_modules/react-native/React/Views/RCTModalHostView.h
+++ b/node_modules/react-native/React/Views/RCTModalHostView.h
@@ -27,6 +27,7 @@

// Android only
@property (nonatomic, assign) BOOL statusBarTranslucent;
+@property (nonatomic, assign) BOOL navigationBarTranslucent;
@property (nonatomic, assign) BOOL hardwareAccelerated;
@property (nonatomic, assign) BOOL animated;

diff --git a/node_modules/react-native/React/Views/RCTModalHostViewManager.m b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
index e2ae7e2..a694008 100644
--- a/node_modules/react-native/React/Views/RCTModalHostViewManager.m
+++ b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
@@ -118,6 +118,7 @@ - (void)invalidate
RCT_EXPORT_VIEW_PROPERTY(presentationStyle, UIModalPresentationStyle)
RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(statusBarTranslucent, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(navigationBarTranslucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hardwareAccelerated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(animated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock)
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.kt b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.kt
index d5e053c..fddda45 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.kt
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.kt
@@ -59,6 +59,15 @@ public class ReactModalHostManager :
view.statusBarTranslucent = statusBarTranslucent
}

+
+ @ReactProp(name = "navigationBarTranslucent")
+ public override fun setNavigationBarTranslucent(
+ view: ReactModalHostView,
+ navigationBarTranslucent: Boolean
+ ) {
+ view.navigationBarTranslucent = navigationBarTranslucent
+ }
+
@ReactProp(name = "hardwareAccelerated")
public override fun setHardwareAccelerated(
view: ReactModalHostView,
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt
index f6e0d82..03380cb 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt
@@ -46,6 +46,7 @@ import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.common.ContextUtils
import com.facebook.react.views.view.ReactViewGroup
+import com.facebook.react.views.view.setSystemBarsTranslucency
import java.util.Objects
import kotlin.math.abs

@@ -78,6 +79,12 @@ public class ReactModalHostView(context: ThemedReactContext) :
createNewDialog = true
}

+ public var navigationBarTranslucent: Boolean = false
+ set(value) {
+ field = value
+ createNewDialog = true
+ }
+
public var animationType: String? = null
set(value) {
field = value
@@ -296,6 +303,7 @@ public class ReactModalHostView(context: ThemedReactContext) :
} else {
frameLayout.fitsSystemWindows = true
}
+ dialog?.window?.setSystemBarsTranslucency(navigationBarTranslucent)
return frameLayout
}

diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt
new file mode 100644
index 0000000..24057c4
--- /dev/null
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.facebook.react.views.view
+
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Build
+import android.view.Window
+import android.view.WindowManager
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsControllerCompat
+
+@Suppress("DEPRECATION")
+public fun Window.setSystemBarsTranslucency(isTranslucent: Boolean) {
+ WindowCompat.setDecorFitsSystemWindows(this, !isTranslucent)
+
+ if (isTranslucent) {
+ val isDarkMode =
+ context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ isStatusBarContrastEnforced = false
+ isNavigationBarContrastEnforced = true
+ }
+
+ statusBarColor = Color.TRANSPARENT
+ navigationBarColor = when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && !isDarkMode ->
+ Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
+ else -> Color.argb(0x80, 0x1b, 0x1b, 0x1b)
+ }
+
+ WindowInsetsControllerCompat(this, this.decorView).run {
+ isAppearanceLightNavigationBars = !isDarkMode
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ attributes.layoutInDisplayCutoutMode = when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/node_modules/react-native/src/private/specs/components/RCTModalHostViewNativeComponent.js b/node_modules/react-native/src/private/specs/components/RCTModalHostViewNativeComponent.js
index 86bf895..58ec294 100644
--- a/node_modules/react-native/src/private/specs/components/RCTModalHostViewNativeComponent.js
+++ b/node_modules/react-native/src/private/specs/components/RCTModalHostViewNativeComponent.js
@@ -58,6 +58,14 @@ type NativeProps = $ReadOnly<{|
*/
statusBarTranslucent?: WithDefault<boolean, false>,

+ /**
+ * The `navigationBarTranslucent` prop determines whether your modal should go under
+ * the system navigationbar.
+ *
+ * See https://reactnative.dev/docs/modal#navigationBarTranslucent
+ */
+ navigationBarTranslucent?: WithDefault<boolean, false>,
+
/**
* The `hardwareAccelerated` prop controls whether to force hardware
* acceleration for the underlying window.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
diff --git a/node_modules/react-native-modal/dist/modal.d.ts b/node_modules/react-native-modal/dist/modal.d.ts
index bd6419e..029762c 100644
--- a/node_modules/react-native-modal/dist/modal.d.ts
+++ b/node_modules/react-native-modal/dist/modal.d.ts
@@ -46,6 +46,7 @@ declare const defaultProps: {
scrollOffsetMax: number;
scrollHorizontal: boolean;
statusBarTranslucent: boolean;
+ navigationBarTranslucent: boolean;
supportedOrientations: ("landscape" | "portrait" | "portrait-upside-down" | "landscape-left" | "landscape-right")[];
};
export declare type ModalProps = ViewProps & {
@@ -137,6 +138,7 @@ export declare class ReactNativeModal extends React.Component<ModalProps, State>
scrollOffsetMax: number;
scrollHorizontal: boolean;
statusBarTranslucent: boolean;
+ navigationBarTranslucent: boolean;
supportedOrientations: ("landscape" | "portrait" | "portrait-upside-down" | "landscape-left" | "landscape-right")[];
};
state: State;
diff --git a/node_modules/react-native-modal/dist/modal.js b/node_modules/react-native-modal/dist/modal.js
index 46277ea..feec991 100644
--- a/node_modules/react-native-modal/dist/modal.js
+++ b/node_modules/react-native-modal/dist/modal.js
@@ -38,6 +38,7 @@ const defaultProps = {
scrollOffsetMax: 0,
scrollHorizontal: false,
statusBarTranslucent: false,
+ navigationBarTranslucent: false,
supportedOrientations: ['portrait', 'landscape'],
};
const extractAnimationFromProps = (props) => ({
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {PortalProvider} from '@gorhom/portal';
import React from 'react';
import {LogBox} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {KeyboardProvider} from 'react-native-keyboard-controller';
import {PickerStateProvider} from 'react-native-picker-select';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import '../wdyr';
Expand All @@ -15,6 +14,7 @@ import CustomStatusBarAndBackgroundContextProvider from './components/CustomStat
import ErrorBoundary from './components/ErrorBoundary';
import HTMLEngineProvider from './components/HTMLEngineProvider';
import InitialURLContextProvider from './components/InitialURLContextProvider';
import KeyboardProvider from './components/KeyboardProvider';
import {LocaleContextProvider} from './components/LocaleContextProvider';
import OnyxProvider from './components/OnyxProvider';
import PopoverContextProvider from './components/PopoverProvider';
Expand Down
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,10 @@ const CONST = {
LIGHT_CONTENT: 'light-content',
DARK_CONTENT: 'dark-content',
},
NAVIGATION_BAR_BUTTONS_STYLE: {
LIGHT: 'light',
DARK: 'dark',
},
TRANSACTION: {
DEFAULT_MERCHANT: 'Expense',
UNKNOWN_MERCHANT: 'Unknown Merchant',
Expand Down
Loading
Loading