Skip to content

Commit 8e792aa

Browse files
authored
Make work with transluscent StatusBar on Android (#3958)
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect. --> ## Summary Fixes #3631 Fixes the issue with translucent StatusBar on Android by adding the option `isStatusBarTranslucentAndroid` in `useAnimatedKeyboard`. It is the same solution as the one mentioned in the issue: kirillzyusko/react-native-keyboard-controller@2a7c92f ## Test plan There is an excellent example in the issue that reproduces the problem: https://github.com/ChildishForces/reanimated-bug-repro I tested on real Android device with Android 12 and RN 71. I think it would be nice to also test on RN < 70 because the bottom padding may behave differently.
1 parent 82ebfb3 commit 8e792aa

File tree

17 files changed

+97
-40
lines changed

17 files changed

+97
-40
lines changed

Common/cpp/NativeModules/NativeReanimatedModule.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -664,15 +664,18 @@ void NativeReanimatedModule::setNewestShadowNodesRegistry(
664664

665665
jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents(
666666
jsi::Runtime &rt,
667-
const jsi::Value &handlerWorklet) {
667+
const jsi::Value &handlerWorklet,
668+
const jsi::Value &isStatusBarTranslucent) {
668669
auto shareableHandler = extractShareableOrThrow(rt, handlerWorklet);
669670
auto uiRuntime = runtimeHelper->uiRuntime();
670-
return subscribeForKeyboardEventsFunction([=](int keyboardState, int height) {
671-
jsi::Runtime &rt = *uiRuntime;
672-
auto handler = shareableHandler->getJSValue(rt);
673-
handler.asObject(rt).asFunction(rt).call(
674-
rt, jsi::Value(keyboardState), jsi::Value(height));
675-
});
671+
return subscribeForKeyboardEventsFunction(
672+
[=](int keyboardState, int height) {
673+
jsi::Runtime &rt = *uiRuntime;
674+
auto handler = shareableHandler->getJSValue(rt);
675+
handler.asObject(rt).asFunction(rt).call(
676+
rt, jsi::Value(keyboardState), jsi::Value(height));
677+
},
678+
isStatusBarTranslucent.asBool());
676679
}
677680

678681
void NativeReanimatedModule::unsubscribeFromKeyboardEvents(

Common/cpp/NativeModules/NativeReanimatedModule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
148148
void unregisterSensor(jsi::Runtime &rt, const jsi::Value &sensorId) override;
149149
jsi::Value subscribeForKeyboardEvents(
150150
jsi::Runtime &rt,
151-
const jsi::Value &keyboardEventContainer) override;
151+
const jsi::Value &keyboardEventContainer,
152+
const jsi::Value &isStatusBarTranslucent) override;
152153
void unsubscribeFromKeyboardEvents(
153154
jsi::Runtime &rt,
154155
const jsi::Value &listenerId) override;

Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ static jsi::Value SPEC_PREFIX(subscribeForKeyboardEvents)(
135135
const jsi::Value *args,
136136
size_t count) {
137137
return static_cast<NativeReanimatedModuleSpec *>(&turboModule)
138-
->subscribeForKeyboardEvents(rt, std::move(args[0]));
138+
->subscribeForKeyboardEvents(rt, std::move(args[0]), std::move(args[1]));
139139
}
140140

141141
static jsi::Value SPEC_PREFIX(unsubscribeFromKeyboardEvents)(
@@ -187,7 +187,7 @@ NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
187187
MethodMetadata{1, SPEC_PREFIX(unregisterSensor)};
188188
methodMap_["configureProps"] = MethodMetadata{2, SPEC_PREFIX(configureProps)};
189189
methodMap_["subscribeForKeyboardEvents"] =
190-
MethodMetadata{1, SPEC_PREFIX(subscribeForKeyboardEvents)};
190+
MethodMetadata{2, SPEC_PREFIX(subscribeForKeyboardEvents)};
191191
methodMap_["unsubscribeFromKeyboardEvents"] =
192192
MethodMetadata{1, SPEC_PREFIX(unsubscribeFromKeyboardEvents)};
193193

Common/cpp/NativeModules/NativeReanimatedModuleSpec.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule {
7373
// keyboard
7474
virtual jsi::Value subscribeForKeyboardEvents(
7575
jsi::Runtime &rt,
76-
const jsi::Value &keyboardEventContainer) = 0;
76+
const jsi::Value &keyboardEventContainer,
77+
const jsi::Value &isStatusBarTranslucent) = 0;
7778
virtual void unsubscribeFromKeyboardEvents(
7879
jsi::Runtime &rt,
7980
const jsi::Value &listenerId) = 0;

Common/cpp/Tools/PlatformDepMethodsHolder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ using ConfigurePropsFunction = std::function<void(
6666
const jsi::Value &uiProps,
6767
const jsi::Value &nativeProps)>;
6868
using KeyboardEventSubscribeFunction =
69-
std::function<int(std::function<void(int, int)>)>;
69+
std::function<int(std::function<void(int, int)>, bool)>;
7070
using KeyboardEventUnsubscribeFunction = std::function<void(int)>;
7171

7272
struct PlatformDepMethodsHolder {

android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ private void unregisterSensor(int sensorId) {
274274
}
275275

276276
@DoNotStrip
277-
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater) {
278-
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater);
277+
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater, boolean isStatusBarTranslucent) {
278+
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater, isStatusBarTranslucent);
279279
}
280280

281281
@DoNotStrip

android/src/main/cpp/NativeProxy.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,11 @@ void NativeProxy::installJSIBindings(
193193
};
194194

195195
auto subscribeForKeyboardEventsFunction =
196-
[this](std::function<void(int, int)> keyboardEventDataUpdater) -> int {
197-
return subscribeForKeyboardEvents(std::move(keyboardEventDataUpdater));
196+
[this](
197+
std::function<void(int, int)> keyboardEventDataUpdater,
198+
bool isStatusBarTranslucent) -> int {
199+
return subscribeForKeyboardEvents(
200+
std::move(keyboardEventDataUpdater), isStatusBarTranslucent);
198201
};
199202

200203
auto unsubscribeFromKeyboardEventsFunction = [this](int listenerId) -> void {
@@ -520,15 +523,18 @@ void NativeProxy::configureProps(
520523
}
521524

522525
int NativeProxy::subscribeForKeyboardEvents(
523-
std::function<void(int, int)> keyboardEventDataUpdater) {
524-
auto method = javaPart_->getClass()
525-
->getMethod<int(KeyboardEventDataUpdater::javaobject)>(
526-
"subscribeForKeyboardEvents");
526+
std::function<void(int, int)> keyboardEventDataUpdater,
527+
bool isStatusBarTranslucent) {
528+
auto method =
529+
javaPart_->getClass()
530+
->getMethod<int(KeyboardEventDataUpdater::javaobject, bool)>(
531+
"subscribeForKeyboardEvents");
527532
return method(
528533
javaPart_.get(),
529534
KeyboardEventDataUpdater::newObjectCxxArgs(
530535
std::move(keyboardEventDataUpdater))
531-
.get());
536+
.get(),
537+
isStatusBarTranslucent);
532538
}
533539

534540
void NativeProxy::unsubscribeFromKeyboardEvents(int listenerId) {

android/src/main/cpp/NativeProxy.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ class NativeProxy : public jni::HybridClass<NativeProxy> {
212212
const jsi::Value &uiProps,
213213
const jsi::Value &nativeProps);
214214
int subscribeForKeyboardEvents(
215-
std::function<void(int, int)> keyboardEventDataUpdater);
215+
std::function<void(int, int)> keyboardEventDataUpdater,
216+
bool isStatusBarTranslucent);
216217
void unsubscribeFromKeyboardEvents(int listenerId);
217218
#ifdef RCT_NEW_ARCH_ENABLED
218219
// nothing

android/src/main/java/com/swmansion/reanimated/keyboardObserver/ReanimatedKeyboardEventListener.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public int asInt() {
4040
private int nextListenerId = 0;
4141
private KeyboardState state;
4242
private final HashMap<Integer, KeyboardEventDataUpdater> listeners = new HashMap<>();
43+
private boolean isStatusBarTranslucent = false;
4344

4445
public ReanimatedKeyboardEventListener(WeakReference<ReactApplicationContext> reactContext) {
4546
this.reactContext = reactContext;
@@ -68,7 +69,11 @@ private void setupWindowInsets() {
6869
FrameLayout.LayoutParams params =
6970
new FrameLayout.LayoutParams(
7071
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
71-
params.setMargins(0, paddingTop, 0, paddingBottom);
72+
if (isStatusBarTranslucent) {
73+
params.setMargins(0, 0, 0, 0);
74+
} else {
75+
params.setMargins(0, paddingTop, 0, paddingBottom);
76+
}
7277
content.setLayoutParams(params);
7378
return insets;
7479
});
@@ -127,9 +132,11 @@ private void setUpCallbacks() {
127132
ViewCompat.setWindowInsetsAnimationCallback(rootView, new WindowInsetsCallback());
128133
}
129134

130-
public int subscribeForKeyboardEvents(KeyboardEventDataUpdater updater) {
135+
public int subscribeForKeyboardEvents(
136+
KeyboardEventDataUpdater updater, boolean isStatusBarTranslucent) {
131137
int listenerId = nextListenerId++;
132138
if (listeners.isEmpty()) {
139+
this.isStatusBarTranslucent = isStatusBarTranslucent;
133140
setUpCallbacks();
134141
}
135142
listeners.put(listenerId, updater);

android/src/paper/java/com/swmansion/reanimated/NativeProxy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@ private void unregisterSensor(int sensorId) {
265265
}
266266

267267
@DoNotStrip
268-
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater) {
269-
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater);
268+
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater, boolean isStatusBarTranslucent) {
269+
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater, isStatusBarTranslucent);
270270
}
271271

272272
@DoNotStrip

docs/docs/api/hooks/useAnimatedKeyboard.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ With the `useAnimatedKeyboard` hook, you can create animations based on current
1515
On Android, make sure to set `android:windowSoftInputMode` in your `AndroidMainfest.xml` to `adjustResize`. Then, using the `useAnimatedKeyboard` hook disables
1616
the default Android behavior (resizing the view to accomodate keyboard) in the whole app. Using values from `useAnimatedKeyboard` hook you can handle the keyboard yourself. Unmounting all components that use `useAnimatedKeyboard` hook brings back the default Android behavior.
1717

18-
```js
19-
useAnimatedKeyboard() -> [AnimatedKeyboardInfo]
20-
```
18+
19+
### Arguments
20+
21+
#### `options` [AnimatedKeyboardOptions]
22+
Optional object containing additional configuration.
2123

2224
### Returns
2325
Hook `useAnimatedKeyboard` returns an instance of [[AnimatedKeyboardInfo](#animatedkeyboard-object)];
@@ -31,6 +33,11 @@ Properties:
3133
* `state`: [[SharedValue](../../api/hooks/useSharedValue)] contains `[enum]`
3234
contains current state of the keyboard. Possible states: `{ CLOSED, OPEN, CLOSING, OPENING }`
3335

36+
#### `AnimatedKeyboardOptions: [object]`
37+
Properties:
38+
* `isStatusBarTranslucentAndroid`[bool] - if you want to use transluscent status bar on Android, set this option to `true`. Defaults to `false`. Ignored on iOS.
39+
40+
3441
### Example
3542
```js
3643
function AnimatedKeyboardExample() {

ios/native/NativeProxy.mm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ static id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &v
292292

293293
static REAKeyboardEventObserver *keyboardObserver = [[REAKeyboardEventObserver alloc] init];
294294
auto subscribeForKeyboardEventsFunction =
295-
[](std::function<void(int keyboardState, int height)> keyboardEventDataUpdater) {
295+
[](std::function<void(int keyboardState, int height)> keyboardEventDataUpdater, bool isStatusBarTranslucent) {
296+
// ignore isStatusBarTranslucent - it's Android only
296297
return [keyboardObserver subscribeForKeyboardEvents:^(int keyboardState, int height) {
297298
keyboardEventDataUpdater(keyboardState, height);
298299
}];

react-native-reanimated.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,14 @@ declare module 'react-native-reanimated' {
566566
height: SharedValue<number>;
567567
state: SharedValue<KeyboardState>;
568568
};
569-
export function useAnimatedKeyboard(): AnimatedKeyboardInfo;
569+
570+
export interface AnimatedKeyboardOptions {
571+
isStatusBarTranslucentAndroid?: boolean;
572+
}
573+
574+
export function useAnimatedKeyboard(
575+
options?: AnimatedKeyboardOptions
576+
): AnimatedKeyboardInfo;
570577

571578
export function useScrollViewOffset(
572579
aref: RefObject<Animated.ScrollView>

src/reanimated2/NativeReanimated/NativeReanimated.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,14 @@ export class NativeReanimated {
119119
this.InnerNativeModule.configureProps(uiProps, nativeProps);
120120
}
121121

122-
subscribeForKeyboardEvents(handler: ShareableRef<number>): number {
123-
return this.InnerNativeModule.subscribeForKeyboardEvents(handler);
122+
subscribeForKeyboardEvents(
123+
handler: ShareableRef<number>,
124+
isStatusBarTranslucent: boolean
125+
): number {
126+
return this.InnerNativeModule.subscribeForKeyboardEvents(
127+
handler,
128+
isStatusBarTranslucent
129+
);
124130
}
125131

126132
unsubscribeFromKeyboardEvents(listenerId: number): void {

src/reanimated2/commonTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,7 @@ export interface MeasuredDimensions {
202202
pageX: number;
203203
pageY: number;
204204
}
205+
206+
export interface AnimatedKeyboardOptions {
207+
isStatusBarTranslucentAndroid?: boolean;
208+
}

src/reanimated2/core.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import NativeReanimatedModule from './NativeReanimated';
22
import { nativeShouldBeMock, shouldBeUseWeb, isWeb } from './PlatformChecker';
3-
import { BasicWorkletFunction, Value3D, ValueRotation } from './commonTypes';
3+
import {
4+
AnimatedKeyboardOptions,
5+
BasicWorkletFunction,
6+
Value3D,
7+
ValueRotation,
8+
} from './commonTypes';
49
import {
510
makeShareableCloneRecursive,
611
makeShareable as makeShareableUnwrapped,
@@ -121,10 +126,12 @@ export function unregisterEventHandler(id: string): void {
121126
}
122127

123128
export function subscribeForKeyboardEvents(
124-
eventHandler: (state: number, height: number) => void
129+
eventHandler: (state: number, height: number) => void,
130+
options: AnimatedKeyboardOptions
125131
): number {
126132
return NativeReanimatedModule.subscribeForKeyboardEvents(
127-
makeShareableCloneRecursive(eventHandler)
133+
makeShareableCloneRecursive(eventHandler),
134+
options.isStatusBarTranslucentAndroid ?? false
128135
);
129136
}
130137

src/reanimated2/hook/useAnimatedKeyboard.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import {
44
subscribeForKeyboardEvents,
55
unsubscribeFromKeyboardEvents,
66
} from '../core';
7-
import { AnimatedKeyboardInfo, KeyboardState } from '../commonTypes';
7+
import {
8+
AnimatedKeyboardInfo,
9+
AnimatedKeyboardOptions,
10+
KeyboardState,
11+
} from '../commonTypes';
812

9-
export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
13+
export function useAnimatedKeyboard(
14+
options: AnimatedKeyboardOptions = { isStatusBarTranslucentAndroid: false }
15+
): AnimatedKeyboardInfo {
1016
const ref = useRef<AnimatedKeyboardInfo | null>(null);
1117
const listenerId = useRef<number>(-1);
1218
const isSubscribed = useRef<boolean>(false);
@@ -20,7 +26,7 @@ export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
2026
'worklet';
2127
keyboardEventData.state.value = state;
2228
keyboardEventData.height.value = height;
23-
});
29+
}, options);
2430
ref.current = keyboardEventData;
2531
isSubscribed.current = true;
2632
}
@@ -32,7 +38,7 @@ export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
3238
'worklet';
3339
keyboardEventData.state.value = state;
3440
keyboardEventData.height.value = height;
35-
});
41+
}, options);
3642
isSubscribed.current = true;
3743
}
3844
return () => {

0 commit comments

Comments
 (0)