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

Merged
merged 26 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6f13505
feat: edge-to-edge mode on Android
kirillzyusko Nov 12, 2024
70cfe6f
fix: proper kav implementation for remaining platforms
kirillzyusko Nov 12, 2024
c6cb0b3
fix: eslint
kirillzyusko Nov 12, 2024
4cb3e3e
fix: prettier
kirillzyusko Nov 12, 2024
372d05a
fix: navigationBarTranslucent property for Modal
kirillzyusko Nov 13, 2024
7f4dfad
fix: small bottom insets on Android
kirillzyusko Nov 13, 2024
5704b07
fix: do not use contrast enforcement for nav bar
kirillzyusko Nov 14, 2024
862fffe
fix: splash screen jump on Android
kirillzyusko Nov 14, 2024
b50083e
fix: nav bar manual theme management
kirillzyusko Nov 14, 2024
af30e2f
chore: rephrase a comment to avoid double wording
kirillzyusko Nov 14, 2024
2100beb
fix: incorrect window metrics on Android
kirillzyusko Nov 14, 2024
ec2a2e1
fix: prettier
kirillzyusko Nov 14, 2024
14037ea
fix: apply padding correction when keyboard gets shown on Android
kirillzyusko Nov 14, 2024
a194a59
fix: changes after review
kirillzyusko Nov 15, 2024
cd63c66
fix: removed unused import
kirillzyusko Nov 15, 2024
e718209
fix: crash on web
kirillzyusko Nov 15, 2024
842ac00
fix: keyboard-avoiding-view in semi opened state
kirillzyusko Nov 18, 2024
691262c
Merge branch 'main' into feat/edge-to-edge
kirillzyusko Nov 20, 2024
995c9be
fix: prettier
kirillzyusko Nov 20, 2024
50db625
fix: use static padding
kirillzyusko Nov 21, 2024
d82af0d
Merge branch 'main' into feat/edge-to-edge
kirillzyusko Nov 22, 2024
b176c58
refactor: remove expo dependency
kirillzyusko Nov 25, 2024
6128e01
fix: ci
kirillzyusko Nov 25, 2024
f95db58
fix: jest tests
kirillzyusko Nov 25, 2024
983c325
fix: crashes on all platforms except android
kirillzyusko Nov 25, 2024
84175de
fix: empty lines in end of file
kirillzyusko Nov 25, 2024
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
8 changes: 7 additions & 1 deletion __mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as ReactNative from 'react-native';
import type StartupTimer from '@libs/StartupTimer/types';

const {BootSplash} = ReactNative.NativeModules;
const {BootSplash, RNNavBarManager} = ReactNative.NativeModules;

jest.doMock('react-native', () => {
let url = 'https://new.expensify.com/';
Expand Down Expand Up @@ -31,6 +31,9 @@ jest.doMock('react-native', () => {
navigationBarHeight: number;
};
StartupTimer: StartupTimer;
RNNavBarManager: typeof ReactNative.NativeModules & {
setButtonStyle: typeof RNNavBarManager.setButtonStyle;
};
};
Linking: typeof ReactNative.Linking & {
setInitialURL: (newUrl: string) => void;
Expand All @@ -50,6 +53,9 @@ jest.doMock('react-native', () => {
navigationBarHeight: 0,
},
StartupTimer: {stop: jest.fn()},
RNNavBarManager: {
setButtonStyle: jest.fn(),
},
},
Linking: {
...ReactNative.Linking,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.database.CursorWindow
import android.os.Process
import androidx.multidex.MultiDexApplication
import com.expensify.chat.bootsplash.BootSplashPackage
import com.expensify.chat.navbar.NavBarManagerPackage
import com.expensify.chat.shortcutManagerModule.ShortcutManagerPackage
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
Expand Down Expand Up @@ -36,6 +37,7 @@ class MainApplication : MultiDexApplication(), ReactApplication {
add(BootSplashPackage())
add(ExpensifyAppPackage())
add(RNTextInputResetPackage())
add(NavBarManagerPackage())
}

override fun getJSMainModuleName() = ".expo/.virtual-metro-entry"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.expensify.chat.navbar

import androidx.core.view.WindowInsetsControllerCompat
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.UiThreadUtil;

class NavBarManagerModule(
private val mReactContext: ReactApplicationContext,
) : ReactContextBaseJavaModule(mReactContext) {
override fun getName(): String = "RNNavBarManager"

@ReactMethod
fun setButtonStyle(style: String) {
UiThreadUtil.runOnUiThread {
mReactContext.currentActivity?.window?.let {
WindowInsetsControllerCompat(it, it.decorView).let { controller ->
when (style) {
"light" -> controller.isAppearanceLightNavigationBars = false
"dark" -> controller.isAppearanceLightNavigationBars = true
}
}
}
}
}
}
kirillzyusko marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.expensify.chat.navbar

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class NavBarManagerPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}

override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules: MutableList<NativeModule> = ArrayList()
modules.add(NavBarManagerModule(reactContext))
return modules
}
}
kirillzyusko marked this conversation as resolved.
Show resolved Hide resolved
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
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.

Loading
Loading