Skip to content

Make work with transluscent StatusBar on Android #3958

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

Merged
merged 2 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 10 additions & 7 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,15 +654,18 @@ void NativeReanimatedModule::setNewestShadowNodesRegistry(

jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents(
jsi::Runtime &rt,
const jsi::Value &handlerWorklet) {
const jsi::Value &handlerWorklet,
const jsi::Value &isStatusBarTranslucent) {
auto shareableHandler = extractShareableOrThrow(rt, handlerWorklet);
auto uiRuntime = runtimeHelper->uiRuntime();
return subscribeForKeyboardEventsFunction([=](int keyboardState, int height) {
jsi::Runtime &rt = *uiRuntime;
auto handler = shareableHandler->getJSValue(rt);
handler.asObject(rt).asFunction(rt).call(
rt, jsi::Value(keyboardState), jsi::Value(height));
});
return subscribeForKeyboardEventsFunction(
[=](int keyboardState, int height) {
jsi::Runtime &rt = *uiRuntime;
auto handler = shareableHandler->getJSValue(rt);
handler.asObject(rt).asFunction(rt).call(
rt, jsi::Value(keyboardState), jsi::Value(height));
},
isStatusBarTranslucent.asBool());
}

void NativeReanimatedModule::unsubscribeFromKeyboardEvents(
Expand Down
3 changes: 2 additions & 1 deletion Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
void unregisterSensor(jsi::Runtime &rt, const jsi::Value &sensorId) override;
jsi::Value subscribeForKeyboardEvents(
jsi::Runtime &rt,
const jsi::Value &keyboardEventContainer) override;
const jsi::Value &keyboardEventContainer,
const jsi::Value &isStatusBarTranslucent) override;
void unsubscribeFromKeyboardEvents(
jsi::Runtime &rt,
const jsi::Value &listenerId) override;
Expand Down
4 changes: 2 additions & 2 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ static jsi::Value SPEC_PREFIX(subscribeForKeyboardEvents)(
const jsi::Value *args,
size_t count) {
return static_cast<NativeReanimatedModuleSpec *>(&turboModule)
->subscribeForKeyboardEvents(rt, std::move(args[0]));
->subscribeForKeyboardEvents(rt, std::move(args[0]), std::move(args[1]));
}

static jsi::Value SPEC_PREFIX(unsubscribeFromKeyboardEvents)(
Expand Down Expand Up @@ -187,7 +187,7 @@ NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
MethodMetadata{1, SPEC_PREFIX(unregisterSensor)};
methodMap_["configureProps"] = MethodMetadata{2, SPEC_PREFIX(configureProps)};
methodMap_["subscribeForKeyboardEvents"] =
MethodMetadata{1, SPEC_PREFIX(subscribeForKeyboardEvents)};
MethodMetadata{2, SPEC_PREFIX(subscribeForKeyboardEvents)};
methodMap_["unsubscribeFromKeyboardEvents"] =
MethodMetadata{1, SPEC_PREFIX(unsubscribeFromKeyboardEvents)};

Expand Down
3 changes: 2 additions & 1 deletion Common/cpp/NativeModules/NativeReanimatedModuleSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule {
// keyboard
virtual jsi::Value subscribeForKeyboardEvents(
jsi::Runtime &rt,
const jsi::Value &keyboardEventContainer) = 0;
const jsi::Value &keyboardEventContainer,
const jsi::Value &isStatusBarTranslucent) = 0;
virtual void unsubscribeFromKeyboardEvents(
jsi::Runtime &rt,
const jsi::Value &listenerId) = 0;
Expand Down
2 changes: 1 addition & 1 deletion Common/cpp/Tools/PlatformDepMethodsHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ using ConfigurePropsFunction = std::function<void(
const jsi::Value &uiProps,
const jsi::Value &nativeProps)>;
using KeyboardEventSubscribeFunction =
std::function<int(std::function<void(int, int)>)>;
std::function<int(std::function<void(int, int)>, bool)>;
using KeyboardEventUnsubscribeFunction = std::function<void(int)>;

struct PlatformDepMethodsHolder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ private void unregisterSensor(int sensorId) {
}

@DoNotStrip
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater) {
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater);
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater, boolean isStatusBarTranslucent) {
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater, isStatusBarTranslucent);
}

@DoNotStrip
Expand Down
20 changes: 13 additions & 7 deletions android/src/main/cpp/NativeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,11 @@ void NativeProxy::installJSIBindings(
};

auto subscribeForKeyboardEventsFunction =
[this](std::function<void(int, int)> keyboardEventDataUpdater) -> int {
return subscribeForKeyboardEvents(std::move(keyboardEventDataUpdater));
[this](
std::function<void(int, int)> keyboardEventDataUpdater,
bool isStatusBarTranslucent) -> int {
return subscribeForKeyboardEvents(
std::move(keyboardEventDataUpdater), isStatusBarTranslucent);
};

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

int NativeProxy::subscribeForKeyboardEvents(
std::function<void(int, int)> keyboardEventDataUpdater) {
auto method = javaPart_->getClass()
->getMethod<int(KeyboardEventDataUpdater::javaobject)>(
"subscribeForKeyboardEvents");
std::function<void(int, int)> keyboardEventDataUpdater,
bool isStatusBarTranslucent) {
auto method =
javaPart_->getClass()
->getMethod<int(KeyboardEventDataUpdater::javaobject, bool)>(
"subscribeForKeyboardEvents");
return method(
javaPart_.get(),
KeyboardEventDataUpdater::newObjectCxxArgs(
std::move(keyboardEventDataUpdater))
.get());
.get(),
isStatusBarTranslucent);
}

void NativeProxy::unsubscribeFromKeyboardEvents(int listenerId) {
Expand Down
3 changes: 2 additions & 1 deletion android/src/main/cpp/NativeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ class NativeProxy : public jni::HybridClass<NativeProxy> {
const jsi::Value &uiProps,
const jsi::Value &nativeProps);
int subscribeForKeyboardEvents(
std::function<void(int, int)> keyboardEventDataUpdater);
std::function<void(int, int)> keyboardEventDataUpdater,
bool isStatusBarTranslucent);
void unsubscribeFromKeyboardEvents(int listenerId);
#ifdef RCT_NEW_ARCH_ENABLED
// nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public int asInt() {
private int nextListenerId = 0;
private KeyboardState state;
private final HashMap<Integer, KeyboardEventDataUpdater> listeners = new HashMap<>();
private boolean isStatusBarTranslucent = false;

public ReanimatedKeyboardEventListener(WeakReference<ReactApplicationContext> reactContext) {
this.reactContext = reactContext;
Expand Down Expand Up @@ -68,7 +69,11 @@ private void setupWindowInsets() {
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
params.setMargins(0, paddingTop, 0, paddingBottom);
if (isStatusBarTranslucent) {
params.setMargins(0, 0, 0, 0);
} else {
params.setMargins(0, paddingTop, 0, paddingBottom);
}
content.setLayoutParams(params);
return insets;
});
Expand Down Expand Up @@ -127,9 +132,11 @@ private void setUpCallbacks() {
ViewCompat.setWindowInsetsAnimationCallback(rootView, new WindowInsetsCallback());
}

public int subscribeForKeyboardEvents(KeyboardEventDataUpdater updater) {
public int subscribeForKeyboardEvents(
KeyboardEventDataUpdater updater, boolean isStatusBarTranslucent) {
int listenerId = nextListenerId++;
if (listeners.isEmpty()) {
this.isStatusBarTranslucent = isStatusBarTranslucent;
setUpCallbacks();
}
listeners.put(listenerId, updater);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ private void unregisterSensor(int sensorId) {
}

@DoNotStrip
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater) {
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater);
private int subscribeForKeyboardEvents(KeyboardEventDataUpdater keyboardEventDataUpdater, boolean isStatusBarTranslucent) {
return reanimatedKeyboardEventListener.subscribeForKeyboardEvents(keyboardEventDataUpdater, isStatusBarTranslucent);
}

@DoNotStrip
Expand Down
13 changes: 10 additions & 3 deletions docs/docs/api/hooks/useAnimatedKeyboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ With the `useAnimatedKeyboard` hook, you can create animations based on current
On Android, make sure to set `android:windowSoftInputMode` in your `AndroidMainfest.xml` to `adjustResize`. Then, using the `useAnimatedKeyboard` hook disables
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.

```js
useAnimatedKeyboard() -> [AnimatedKeyboardInfo]
```

### Arguments

#### `options` [AnimatedKeyboardOptions]
Optional object containing additional configuration.

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

#### `AnimatedKeyboardOptions: [object]`
Properties:
* `isStatusBarTranslucentAndroid`[bool] - if you want to use transluscent status bar on Android, set this option to `true`. Defaults to `false`. Ignored on iOS.


### Example
```js
function AnimatedKeyboardExample() {
Expand Down
3 changes: 2 additions & 1 deletion ios/native/NativeProxy.mm
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ static id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &v

static REAKeyboardEventObserver *keyboardObserver = [[REAKeyboardEventObserver alloc] init];
auto subscribeForKeyboardEventsFunction =
[](std::function<void(int keyboardState, int height)> keyboardEventDataUpdater) {
[](std::function<void(int keyboardState, int height)> keyboardEventDataUpdater, bool isStatusBarTranslucent) {
// ignore isStatusBarTranslucent - it's Android only
return [keyboardObserver subscribeForKeyboardEvents:^(int keyboardState, int height) {
keyboardEventDataUpdater(keyboardState, height);
}];
Expand Down
9 changes: 8 additions & 1 deletion react-native-reanimated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,14 @@ declare module 'react-native-reanimated' {
height: SharedValue<number>;
state: SharedValue<KeyboardState>;
};
export function useAnimatedKeyboard(): AnimatedKeyboardInfo;

export interface AnimatedKeyboardOptions {
isStatusBarTranslucentAndroid?: boolean;
}

export function useAnimatedKeyboard(
options?: AnimatedKeyboardOptions
): AnimatedKeyboardInfo;

export function useScrollViewOffset(
aref: RefObject<Animated.ScrollView>
Expand Down
10 changes: 8 additions & 2 deletions src/reanimated2/NativeReanimated/NativeReanimated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,14 @@ export class NativeReanimated {
this.InnerNativeModule.configureProps(uiProps, nativeProps);
}

subscribeForKeyboardEvents(handler: ShareableRef<number>): number {
return this.InnerNativeModule.subscribeForKeyboardEvents(handler);
subscribeForKeyboardEvents(
handler: ShareableRef<number>,
isStatusBarTranslucent: boolean
): number {
return this.InnerNativeModule.subscribeForKeyboardEvents(
handler,
isStatusBarTranslucent
);
}

unsubscribeFromKeyboardEvents(listenerId: number): void {
Expand Down
4 changes: 4 additions & 0 deletions src/reanimated2/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,7 @@ export interface MeasuredDimensions {
pageX: number;
pageY: number;
}

export interface AnimatedKeyboardOptions {
isStatusBarTranslucentAndroid?: boolean;
}
13 changes: 10 additions & 3 deletions src/reanimated2/core.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import NativeReanimatedModule from './NativeReanimated';
import { nativeShouldBeMock, shouldBeUseWeb, isWeb } from './PlatformChecker';
import { BasicWorkletFunction, Value3D, ValueRotation } from './commonTypes';
import {
AnimatedKeyboardOptions,
BasicWorkletFunction,
Value3D,
ValueRotation,
} from './commonTypes';
import {
makeShareableCloneRecursive,
makeShareable as makeShareableUnwrapped,
Expand Down Expand Up @@ -121,10 +126,12 @@ export function unregisterEventHandler(id: string): void {
}

export function subscribeForKeyboardEvents(
eventHandler: (state: number, height: number) => void
eventHandler: (state: number, height: number) => void,
options: AnimatedKeyboardOptions
): number {
return NativeReanimatedModule.subscribeForKeyboardEvents(
makeShareableCloneRecursive(eventHandler)
makeShareableCloneRecursive(eventHandler),
options.isStatusBarTranslucentAndroid ?? false
);
}

Expand Down
14 changes: 10 additions & 4 deletions src/reanimated2/hook/useAnimatedKeyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import {
subscribeForKeyboardEvents,
unsubscribeFromKeyboardEvents,
} from '../core';
import { AnimatedKeyboardInfo, KeyboardState } from '../commonTypes';
import {
AnimatedKeyboardInfo,
AnimatedKeyboardOptions,
KeyboardState,
} from '../commonTypes';

export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
export function useAnimatedKeyboard(
options: AnimatedKeyboardOptions = { isStatusBarTranslucentAndroid: false }
): AnimatedKeyboardInfo {
const ref = useRef<AnimatedKeyboardInfo | null>(null);
const listenerId = useRef<number>(-1);
const isSubscribed = useRef<boolean>(false);
Expand All @@ -20,7 +26,7 @@ export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
'worklet';
keyboardEventData.state.value = state;
keyboardEventData.height.value = height;
});
}, options);
ref.current = keyboardEventData;
isSubscribed.current = true;
}
Expand All @@ -32,7 +38,7 @@ export function useAnimatedKeyboard(): AnimatedKeyboardInfo {
'worklet';
keyboardEventData.state.value = state;
keyboardEventData.height.value = height;
});
}, options);
isSubscribed.current = true;
}
return () => {
Expand Down