diff --git a/packages/docs-reanimated/docs/device/useAnimatedKeyboard.mdx b/packages/docs-reanimated/docs/device/useAnimatedKeyboard.mdx index 2e71e14e0db5..df572b4db3aa 100644 --- a/packages/docs-reanimated/docs/device/useAnimatedKeyboard.mdx +++ b/packages/docs-reanimated/docs/device/useAnimatedKeyboard.mdx @@ -101,9 +101,13 @@ import AnimatedKeyboardSrc from '!!raw-loader!@site/src/examples/AnimatedKeyboar - On Android, using the `useAnimatedKeyboard` hook expands root view to full screen ([immersive mode](https://developer.android.com/develop/ui/views/layout/immersive)) and takes control over insets management. - - When `isStatusBarTranslucentAndroid` is `false` it applies the top and bottom margins according to the insets. + - When `isStatusBarTranslucentAndroid` is `false` it applies the top margin according to the insets. - - When `isStatusBarTranslucentAndroid` is `true` it applies bottom padding according to the navigation inset and sets top margin to `0`. + - When `isStatusBarTranslucentAndroid` is `true` it sets top margin to `0`. + + - When `isNavigationBarTranslucentAndroid` is `false` it applies the bottom margin according to the insets. + + - When `isNavigationBarTranslucentAndroid` is `true` it sets bottom margin to `0`. - On Android, when using navigation with native header, `isStatusBarTranslucentAndroid` doesn't affect the top inset. diff --git a/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useAnimatedKeyboard.md b/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useAnimatedKeyboard.md index 12e634e96cba..60c6ef07b49f 100644 --- a/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useAnimatedKeyboard.md +++ b/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useAnimatedKeyboard.md @@ -48,6 +48,8 @@ Properties: - `isStatusBarTranslucentAndroid`[bool] - if you want to use translucent status bar on Android, set this option to `true`. Defaults to `false`. Ignored on iOS. +- `isNavigationBarTranslucentAndroid`[bool] - if you want to use translucent navigation bar on Android, set this option to `true`. Defaults to `false`. Ignored on iOS. + ### Example ```js diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp index af4dd15b47b5..48043a98d62a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp @@ -876,7 +876,8 @@ void NativeReanimatedModule::initializeLayoutAnimations() { jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents( jsi::Runtime &rt, const jsi::Value &handlerWorklet, - const jsi::Value &isStatusBarTranslucent) { + const jsi::Value &isStatusBarTranslucent, + const jsi::Value &isNavigationBarTranslucent) { auto shareableHandler = extractShareableOrThrow( rt, handlerWorklet, @@ -886,7 +887,8 @@ jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents( uiWorkletRuntime_->runGuarded( shareableHandler, jsi::Value(keyboardState), jsi::Value(height)); }, - isStatusBarTranslucent.getBool()); + isStatusBarTranslucent.getBool(), + isNavigationBarTranslucent.getBool()); } void NativeReanimatedModule::unsubscribeFromKeyboardEvents( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h index 45a6e387dbb2..71776f6b59fa 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h @@ -156,7 +156,8 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { jsi::Value subscribeForKeyboardEvents( jsi::Runtime &rt, const jsi::Value &keyboardEventContainer, - const jsi::Value &isStatusBarTranslucent) override; + const jsi::Value &isStatusBarTranslucent, + const jsi::Value &isNavigationBarTranslucent) override; void unsubscribeFromKeyboardEvents( jsi::Runtime &rt, const jsi::Value &listenerId) override; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp index 6df018d378ad..9390c51d5e1f 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp @@ -138,7 +138,8 @@ static jsi::Value SPEC_PREFIX(subscribeForKeyboardEvents)( const jsi::Value *args, size_t) { return static_cast(&turboModule) - ->subscribeForKeyboardEvents(rt, std::move(args[0]), std::move(args[1])); + ->subscribeForKeyboardEvents( + rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); } static jsi::Value SPEC_PREFIX(unsubscribeFromKeyboardEvents)( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h index b4505a0d4b9d..06b7269947ad 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h @@ -82,7 +82,8 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule { virtual jsi::Value subscribeForKeyboardEvents( jsi::Runtime &rt, const jsi::Value &keyboardEventContainer, - const jsi::Value &isStatusBarTranslucent) = 0; + const jsi::Value &isStatusBarTranslucent, + const jsi::Value &isNavigationBarTranslucent) = 0; virtual void unsubscribeFromKeyboardEvents( jsi::Runtime &rt, const jsi::Value &listenerId) = 0; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h b/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h index 542c0a2b9da5..f5aa89f6b2aa 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h @@ -72,7 +72,7 @@ using ConfigurePropsFunction = std::function; using KeyboardEventSubscribeFunction = - std::function, bool)>; + std::function, bool, bool)>; using KeyboardEventUnsubscribeFunction = std::function; using MaybeFlushUIUpdatesQueueFunction = std::function; diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.cpp b/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.cpp index 5b69f33926a1..728d5c765e43 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.cpp @@ -416,14 +416,16 @@ void NativeProxy::setGestureState(int handlerTag, int newState) { int NativeProxy::subscribeForKeyboardEvents( std::function callback, - bool isStatusBarTranslucent) { + bool isStatusBarTranslucent, + bool isNavigationBarTranslucent) { static const auto method = - getJniMethod( + getJniMethod( "subscribeForKeyboardEvents"); return method( javaPart_.get(), KeyboardWorkletWrapper::newObjectCxxArgs(std::move(callback)).get(), - isStatusBarTranslucent); + isStatusBarTranslucent, + isNavigationBarTranslucent); } void NativeProxy::unsubscribeFromKeyboardEvents(int listenerId) { diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.h b/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.h index 93ff6e0e76e9..07b8978e1e83 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.h +++ b/packages/react-native-reanimated/android/src/main/cpp/reanimated/NativeProxy.h @@ -221,7 +221,8 @@ class NativeProxy : public jni::HybridClass { void unregisterSensor(int sensorId); int subscribeForKeyboardEvents( std::function callback, - bool isStatusBarTranslucent); + bool isStatusBarTranslucent, + bool isNavigationBarTranslucent); void unsubscribeFromKeyboardEvents(int listenerId); #ifdef RCT_NEW_ARCH_ENABLED // nothing diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/Keyboard.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/Keyboard.java index 7aa497e2bc39..78c31b27df6d 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/Keyboard.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/Keyboard.java @@ -18,9 +18,10 @@ public int getHeight() { return mHeight; } - public void updateHeight(WindowInsetsCompat insets) { + public void updateHeight(WindowInsetsCompat insets, boolean isNavigationBarTranslucent) { int contentBottomInset = insets.getInsets(CONTENT_TYPE_MASK).bottom; - int systemBarBottomInset = insets.getInsets(SYSTEM_BAR_TYPE_MASK).bottom; + int systemBarBottomInset = + isNavigationBarTranslucent ? 0 : insets.getInsets(SYSTEM_BAR_TYPE_MASK).bottom; int keyboardHeightDip = contentBottomInset - systemBarBottomInset; int keyboardHeight = (int) PixelUtil.toDIPFromPixel(Math.max(0, keyboardHeightDip)); if (keyboardHeight <= 0 && mState == KeyboardState.OPEN) { diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java index 236448da9cb0..106d20f84ff9 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java @@ -9,11 +9,15 @@ public class KeyboardAnimationCallback extends WindowInsetsAnimationCompat.Callb private final Keyboard mKeyboard; private final NotifyAboutKeyboardChangeFunction mNotifyAboutKeyboardChange; private static final int CONTENT_TYPE_MASK = WindowInsetsCompat.Type.ime(); + private final boolean mIsNavigationBarTranslucent; public KeyboardAnimationCallback( - Keyboard keyboard, NotifyAboutKeyboardChangeFunction notifyAboutKeyboardChange) { + Keyboard keyboard, + NotifyAboutKeyboardChangeFunction notifyAboutKeyboardChange, + boolean isNavigationBarTranslucent) { super(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); mNotifyAboutKeyboardChange = notifyAboutKeyboardChange; + mIsNavigationBarTranslucent = isNavigationBarTranslucent; mKeyboard = keyboard; } @@ -43,7 +47,7 @@ public WindowInsetsCompat onProgress( } } if (isAnyKeyboardAnimationRunning) { - mKeyboard.updateHeight(insets); + mKeyboard.updateHeight(insets, mIsNavigationBarTranslucent); mNotifyAboutKeyboardChange.call(); } return insets; diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationManager.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationManager.java index ea389f0f4372..c4ab71cdcf1b 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationManager.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationManager.java @@ -22,13 +22,16 @@ public KeyboardAnimationManager(WeakReference reactCont } public int subscribeForKeyboardUpdates( - KeyboardWorkletWrapper callback, boolean isStatusBarTranslucent) { + KeyboardWorkletWrapper callback, + boolean isStatusBarTranslucent, + boolean isNavigationBarTranslucent) { int listenerId = mNextListenerId++; if (mListeners.isEmpty()) { KeyboardAnimationCallback keyboardAnimationCallback = - new KeyboardAnimationCallback(mKeyboard, this::notifyAboutKeyboardChange); + new KeyboardAnimationCallback( + mKeyboard, this::notifyAboutKeyboardChange, isNavigationBarTranslucent); mWindowsInsetsManager.startObservingChanges( - keyboardAnimationCallback, isStatusBarTranslucent); + keyboardAnimationCallback, isStatusBarTranslucent, isNavigationBarTranslucent); } mListeners.put(listenerId, callback); return listenerId; diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java index 29768a7af064..566094482583 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java @@ -15,6 +15,7 @@ public class WindowsInsetsManager { private boolean mIsStatusBarTranslucent = false; + private boolean mIsNavigationBarTranslucent = false; private final WeakReference mReactContext; private final Keyboard mKeyboard; private final NotifyAboutKeyboardChangeFunction mNotifyAboutKeyboardChange; @@ -35,8 +36,11 @@ private Activity getCurrentActivity() { } public void startObservingChanges( - KeyboardAnimationCallback keyboardAnimationCallback, boolean isStatusBarTranslucent) { + KeyboardAnimationCallback keyboardAnimationCallback, + boolean isStatusBarTranslucent, + boolean isNavigationBarTranslucent) { mIsStatusBarTranslucent = isStatusBarTranslucent; + mIsNavigationBarTranslucent = isNavigationBarTranslucent; updateWindowDecor(false); Activity currentActivity = getCurrentActivity(); @@ -51,7 +55,7 @@ public void startObservingChanges( } public void stopObservingChanges() { - updateWindowDecor(!mIsStatusBarTranslucent); + updateWindowDecor(!mIsStatusBarTranslucent && !mIsNavigationBarTranslucent); updateInsets(0, 0); Activity currentActivity = getCurrentActivity(); @@ -83,7 +87,7 @@ private void updateWindowDecor(boolean decorFitsSystemWindow) { private WindowInsetsCompat onApplyWindowInsetsListener(View view, WindowInsetsCompat insets) { WindowInsetsCompat defaultInsets = ViewCompat.onApplyWindowInsets(view, insets); if (mKeyboard.getState() == KeyboardState.OPEN) { - mKeyboard.updateHeight(insets); + mKeyboard.updateHeight(insets, mIsNavigationBarTranslucent); mNotifyAboutKeyboardChange.call(); } setWindowInsets(defaultInsets); @@ -120,11 +124,12 @@ private FrameLayout.LayoutParams getLayoutParams(int paddingTop, int paddingBott int matchParentFlag = FrameLayout.LayoutParams.MATCH_PARENT; FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(matchParentFlag, matchParentFlag); - if (mIsStatusBarTranslucent) { - params.setMargins(0, 0, 0, paddingBottom); - } else { - params.setMargins(0, paddingTop, 0, paddingBottom); - } + + params.setMargins( + 0, + mIsStatusBarTranslucent ? 0 : paddingTop, + 0, + mIsNavigationBarTranslucent ? 0 : paddingBottom); return params; } } diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java index 2e874ae0b731..070f1fe2a6d7 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java @@ -205,9 +205,11 @@ public void unregisterSensor(int sensorId) { @DoNotStrip public int subscribeForKeyboardEvents( - KeyboardWorkletWrapper keyboardWorkletWrapper, boolean isStatusBarTranslucent) { + KeyboardWorkletWrapper keyboardWorkletWrapper, + boolean isStatusBarTranslucent, + boolean isNavigationBarTranslucent) { return keyboardAnimationManager.subscribeForKeyboardUpdates( - keyboardWorkletWrapper, isStatusBarTranslucent); + keyboardWorkletWrapper, isStatusBarTranslucent, isNavigationBarTranslucent); } @DoNotStrip diff --git a/packages/react-native-reanimated/apple/reanimated/native/PlatformDepMethodsHolderImpl.mm b/packages/react-native-reanimated/apple/reanimated/native/PlatformDepMethodsHolderImpl.mm index b8e458f12da1..9b1e055ac255 100644 --- a/packages/react-native-reanimated/apple/reanimated/native/PlatformDepMethodsHolderImpl.mm +++ b/packages/react-native-reanimated/apple/reanimated/native/PlatformDepMethodsHolderImpl.mm @@ -269,8 +269,10 @@ UnregisterSensorFunction makeUnregisterSensorFunction(ReanimatedSensorContainer KeyboardEventSubscribeFunction makeSubscribeForKeyboardEventsFunction(REAKeyboardEventObserver *keyboardObserver) { auto subscribeForKeyboardEventsFunction = - [=](std::function keyboardEventDataUpdater, bool isStatusBarTranslucent) { - // ignore isStatusBarTranslucent - it's Android only + [=](std::function keyboardEventDataUpdater, + bool isStatusBarTranslucent, + bool isNavigationBarTranslucent) { + // ignore isStatusBarTranslucent and isNavigationBarTranslucent - those are Android only return [keyboardObserver subscribeForKeyboardEvents:^(int keyboardState, int height) { keyboardEventDataUpdater(keyboardState, height); }]; diff --git a/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts b/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts index 4119474c5afc..38e24690d2ec 100644 --- a/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts +++ b/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts @@ -54,7 +54,8 @@ export interface NativeReanimatedModule { configureProps(uiProps: string[], nativeProps: string[]): void; subscribeForKeyboardEvents( handler: ShareableRef, - isStatusBarTranslucent: boolean + isStatusBarTranslucent: boolean, + isNavigationBarTranslucent: boolean ): number; unsubscribeFromKeyboardEvents(listenerId: number): void; configureLayoutAnimationBatch( @@ -212,11 +213,13 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti subscribeForKeyboardEvents( handler: ShareableRef, - isStatusBarTranslucent: boolean + isStatusBarTranslucent: boolean, + isNavigationBarTranslucent: boolean ) { return this.InnerNativeModule.subscribeForKeyboardEvents( handler, - isStatusBarTranslucent + isStatusBarTranslucent, + isNavigationBarTranslucent ); } diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index 757281e3e625..45a4bdc40d7f 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -298,6 +298,7 @@ export interface MeasuredDimensions { export interface AnimatedKeyboardOptions { isStatusBarTranslucentAndroid?: boolean; + isNavigationBarTranslucentAndroid?: boolean; } /** diff --git a/packages/react-native-reanimated/src/core.ts b/packages/react-native-reanimated/src/core.ts index 0cc50a6ef8c8..b427ca4984bb 100644 --- a/packages/react-native-reanimated/src/core.ts +++ b/packages/react-native-reanimated/src/core.ts @@ -120,7 +120,8 @@ export function subscribeForKeyboardEvents( } return NativeReanimatedModule.subscribeForKeyboardEvents( makeShareableCloneRecursive(handleAndFlushAnimationFrame), - options.isStatusBarTranslucentAndroid ?? false + options.isStatusBarTranslucentAndroid ?? false, + options.isNavigationBarTranslucentAndroid ?? false ); } diff --git a/packages/react-native-reanimated/src/hook/useAnimatedKeyboard.ts b/packages/react-native-reanimated/src/hook/useAnimatedKeyboard.ts index 33315d1fc430..65381f75dc5d 100644 --- a/packages/react-native-reanimated/src/hook/useAnimatedKeyboard.ts +++ b/packages/react-native-reanimated/src/hook/useAnimatedKeyboard.ts @@ -19,7 +19,10 @@ import { KeyboardState } from '../commonTypes'; * @see https://docs.swmansion.com/react-native-reanimated/docs/device/useAnimatedKeyboard */ export function useAnimatedKeyboard( - options: AnimatedKeyboardOptions = { isStatusBarTranslucentAndroid: false } + options: AnimatedKeyboardOptions = { + isStatusBarTranslucentAndroid: false, + isNavigationBarTranslucentAndroid: false, + } ): AnimatedKeyboardInfo { const ref = useRef(null); const listenerId = useRef(-1);