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

[Android][Keyboard] More consistent inequality check to compute keyboard state #5874

Merged

Conversation

antFrancon
Copy link
Contributor

@antFrancon antFrancon commented Apr 8, 2024

Summary

I have found the useAnimatedKeyboard hook to be particularly useful. Beyond the height value, I often use the state value too, which allows me to show or hide elements based on the keyboard's state. In this example, I combine it with a regular React Native KeyboardAvoidingView to display a toolbar above my keyboard.

The issue I encountered is that the computed Android keyboard state becomes invalid after opening and then closing the keyboard for the first time. After some investigation, it appears that the keyboard gets a negative height upon closing, which disrupts some logic inside keyboard.java:

// onAnimationStart
mState = mHeight == 0 ? KeyboardState.OPENING : KeyboardState.CLOSING

// onAnimationEnd
mState = mHeight == 0 ? KeyboardState.CLOSED : KeyboardState.OPEN;

As a result, the keyboard state cycle appears to be 0 (UNKNOWN) > 1 (OPENING) > 2 (OPEN) > 3 (CLOSING) > 2 (OPEN) instead of the expected 0 (UNKNOWN) > 1 (OPENING) > 2 (OPEN) > 3 (CLOSING) > 4 (CLOSED).

Ios Android
ios-keyboard android-keyboard

There may be an issue with the fact that the height gets a negative value, but this PR does not intend to address it. Instead, I would like to make the code more robust regarding keyboard state computation by using an inequality check, specifically mHeight <= 0, instead of a strict equality check mHeight == 0.

Test plan

I have added a ready-to-play repository.

It essentially implements the code described above. I've added some logs to illustrate that the keyboard state is incorrect on Android when opening and closing the keyboard. You can also observe that the keyboard gets a negative height after closing.

import React from 'react';
import {
  KeyboardAvoidingView,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  TextInput,
  View,
  useColorScheme,
} from 'react-native';
import Animated, {
  KeyboardState,
  SharedValue,
  runOnJS,
  useAnimatedKeyboard,
  useAnimatedProps,
  useAnimatedStyle,
} from 'react-native-reanimated';
import {
  SafeAreaProvider,
  SafeAreaView,
  initialWindowMetrics,
  useSafeAreaInsets,
} from 'react-native-safe-area-context';

import {Colors} from 'react-native/Libraries/NewAppScreen';

function App(): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  const backgroundColor = isDarkMode ? Colors.darker : Colors.lighter;

  const styles = StyleSheet.create({
    root: {flex: 1, backgroundColor},
    container: {flex: 1},
  });

  return (
    <SafeAreaProvider style={styles.root} initialMetrics={initialWindowMetrics}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundColor}
        translucent
      />
      <SafeAreaView style={styles.container}>
        <AppContent isDarkMode={isDarkMode} />
      </SafeAreaView>
    </SafeAreaProvider>
  );
}

interface AppContentProps {
  isDarkMode: boolean;
}

const AppContent: React.FC<AppContentProps> = ({isDarkMode}) => {
  const styles = StyleSheet.create({
    container: {
      flex: 1,
    },
    content: {
      flex: 1,
      justifyContent: 'flex-end',
      backgroundColor: isDarkMode ? Colors.black : Colors.white,
      padding: 16,
    },
    input: {
      height: 40,
      paddingHorizontal: 8,
      borderRadius: 4,
      borderColor: 'gray',
      borderWidth: 1,
    },
  });

  const topInset = useSafeAreaInsets().top;
  const keyboardVerticalOffset = topInset + 8;

  return (
    <ScrollView
      contentInsetAdjustmentBehavior="automatic"
      contentContainerStyle={styles.container}>
      <View style={styles.content}>
        <KeyboardAvoidingView
          behavior="padding"
          keyboardVerticalOffset={keyboardVerticalOffset}>
          <Toolbar />
          <TextInput placeholder="Type here..." style={styles.input} />
        </KeyboardAvoidingView>
      </View>
    </ScrollView>
  );
};

const Toolbar: React.FC = () => {
  const styles = StyleSheet.create({
    toolbar: {
      position: 'absolute',
      top: 0,
      left: 0,
      right: 0,
      height: 100,
      backgroundColor: 'red',
      transform: [{translateY: -120}],
      justifyContent: 'center',
      alignItems: 'center',
    },
  });

  const keyboard = useAnimatedKeyboard({isStatusBarTranslucentAndroid: true});
  const animatedProps = useAnimatedProps(() => ({
    pointerEvents: isKeyboardOpen(keyboard.state, keyboard.height)
      ? ('box-none' as const)
      : ('none' as const),
  }));
  const animatedStyle = useAnimatedStyle(() => ({
    opacity: isKeyboardOpen(keyboard.state, keyboard.height) ? 1 : 0,
  }));

  return (
    <Animated.View
      style={[styles.toolbar, animatedStyle]}
      animatedProps={animatedProps}>
      <Text>Toolbar visible when keyboard is open</Text>
    </Animated.View>
  );
};

/* export */
export default App;

/* utils */
function isKeyboardOpen(
  state: SharedValue<KeyboardState>,
  height: SharedValue<number>,
): boolean {
  'worklet';
  runOnJS(debug)('Keyboard State', state.value);
  runOnJS(debug)('Keyboard Height', height.value);
  return (
    state.value === KeyboardState.OPEN || state.value === KeyboardState.OPENING
  );
}

/* debug */
function debug(label: string, value: string | number): void {
  console.log('🐞', label, ':', value);
}

@piaskowyk piaskowyk self-requested a review April 8, 2024 19:24
@piaskowyk
Copy link
Member

Hey @antFrancon Thank you for you PR and detailed description! 😍

@piaskowyk piaskowyk added this pull request to the merge queue May 10, 2024
Merged via the queue into software-mansion:main with commit 021e751 May 10, 2024
9 checks passed
@antFrancon antFrancon deleted the fix/android-keyboard-state branch May 11, 2024 09:33
renovate bot referenced this pull request in GSTJ/react-native-magic-modal Jun 3, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[react-native-reanimated](https://togithub.com/software-mansion/react-native-reanimated)
| [`~3.11.0` ->
`~3.12.0`](https://renovatebot.com/diffs/npm/react-native-reanimated/3.11.0/3.12.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/react-native-reanimated/3.12.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-native-reanimated/3.12.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-native-reanimated/3.11.0/3.12.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-native-reanimated/3.11.0/3.12.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>software-mansion/react-native-reanimated
(react-native-reanimated)</summary>

###
[`v3.12.0`](https://togithub.com/software-mansion/react-native-reanimated/releases/tag/3.12.0)

[Compare
Source](https://togithub.com/software-mansion/react-native-reanimated/compare/3.11.0...3.12.0)

#### What's Changed

- Give more meaningful warning when modifying a Shareable by
[@&#8203;tjzel](https://togithub.com/tjzel) in
[https://github.com/software-mansion/react-native-reanimated/pull/5548](https://togithub.com/software-mansion/react-native-reanimated/pull/5548)
- Make animated components use different tags for events by
[@&#8203;szydlovsky](https://togithub.com/szydlovsky) in
[https://github.com/software-mansion/react-native-reanimated/pull/5960](https://togithub.com/software-mansion/react-native-reanimated/pull/5960)
- Add `warning` and `failing` test decorators by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/5929](https://togithub.com/software-mansion/react-native-reanimated/pull/5929)
- Update useAnimatedKeyboard docs by
[@&#8203;maciekstosio](https://togithub.com/maciekstosio) in
[https://github.com/software-mansion/react-native-reanimated/pull/5866](https://togithub.com/software-mansion/react-native-reanimated/pull/5866)
- Change the docs to mention, that `.springify()` works with
`.duration()` by [@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/5990](https://togithub.com/software-mansion/react-native-reanimated/pull/5990)
- \[Android]\[Keyboard] More consistent inequality check to compute
keyboard state by [@&#8203;antFrancon](https://togithub.com/antFrancon)
in
[https://github.com/software-mansion/react-native-reanimated/pull/5874](https://togithub.com/software-mansion/react-native-reanimated/pull/5874)
- fix typo on object key for `targetValues` in custom-animations.mdx by
[@&#8203;JDMathew](https://togithub.com/JDMathew) in
[https://github.com/software-mansion/react-native-reanimated/pull/5994](https://togithub.com/software-mansion/react-native-reanimated/pull/5994)
- Remove outdated code for unsupported React Native versions by
[@&#8203;tomekzaw](https://togithub.com/tomekzaw) in
[https://github.com/software-mansion/react-native-reanimated/pull/5979](https://togithub.com/software-mansion/react-native-reanimated/pull/5979)
- Remove REAInitializer by
[@&#8203;tomekzaw](https://togithub.com/tomekzaw) in
[https://github.com/software-mansion/react-native-reanimated/pull/5681](https://togithub.com/software-mansion/react-native-reanimated/pull/5681)
- Prevent crash on non-existent view updates in Android by
[@&#8203;thomas-rx](https://togithub.com/thomas-rx) in
[https://github.com/software-mansion/react-native-reanimated/pull/5767](https://togithub.com/software-mansion/react-native-reanimated/pull/5767)
- Fix location after shared element transition by
[@&#8203;piaskowyk](https://togithub.com/piaskowyk) in
[https://github.com/software-mansion/react-native-reanimated/pull/6010](https://togithub.com/software-mansion/react-native-reanimated/pull/6010)
- Make `useScrollviewOffset` ref nullable and simplify its code by
[@&#8203;szydlovsky](https://togithub.com/szydlovsky) in
[https://github.com/software-mansion/react-native-reanimated/pull/6009](https://togithub.com/software-mansion/react-native-reanimated/pull/6009)
- fix: use proper classes for bridgeless by
[@&#8203;WoLewicki](https://togithub.com/WoLewicki) in
[https://github.com/software-mansion/react-native-reanimated/pull/5997](https://togithub.com/software-mansion/react-native-reanimated/pull/5997)
- docs: add
[@&#8203;swmansion/t-rex-ui](https://togithub.com/swmansion/t-rex-ui) by
[@&#8203;patrycjakalinska](https://togithub.com/patrycjakalinska) in
[https://github.com/software-mansion/react-native-reanimated/pull/6015](https://togithub.com/software-mansion/react-native-reanimated/pull/6015)
- Remove `CellRendererComponent` from Animated.FlatList props by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/5951](https://togithub.com/software-mansion/react-native-reanimated/pull/5951)
- Add `useComposedEventHandler` hook by
[@&#8203;szydlovsky](https://togithub.com/szydlovsky) in
[https://github.com/software-mansion/react-native-reanimated/pull/5890](https://togithub.com/software-mansion/react-native-reanimated/pull/5890)
- Align handling colors with RN by
[@&#8203;maciekstosio](https://togithub.com/maciekstosio) in
[https://github.com/software-mansion/react-native-reanimated/pull/5825](https://togithub.com/software-mansion/react-native-reanimated/pull/5825)
- Add more tests - useSharedValue, useAnimatedStyle , useDerivedValue by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/5981](https://togithub.com/software-mansion/react-native-reanimated/pull/5981)
- Make animation RollInLeft work with modifers by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/6039](https://togithub.com/software-mansion/react-native-reanimated/pull/6039)
- Test predefined entering animation by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/5995](https://togithub.com/software-mansion/react-native-reanimated/pull/5995)
- Tests: cancelAnimation, useFrameCallback, measure, withDecay by
[@&#8203;Latropos](https://togithub.com/Latropos) in
[https://github.com/software-mansion/react-native-reanimated/pull/6016](https://togithub.com/software-mansion/react-native-reanimated/pull/6016)
- docs: fix useAnimatedKeyboard page crash by
[@&#8203;patrycjakalinska](https://togithub.com/patrycjakalinska) in
[https://github.com/software-mansion/react-native-reanimated/pull/6056](https://togithub.com/software-mansion/react-native-reanimated/pull/6056)
- \[Web LA] Remove `existingTransform` by
[@&#8203;m-bert](https://togithub.com/m-bert) in
[https://github.com/software-mansion/react-native-reanimated/pull/6060](https://togithub.com/software-mansion/react-native-reanimated/pull/6060)

#### New Contributors

[@&#8203;antFrancon](https://togithub.com/antFrancon),
[@&#8203;JDMathew](https://togithub.com/JDMathew),
[@&#8203;thomas-rx](https://togithub.com/thomas-rx),
[@&#8203;exploIF](https://togithub.com/exploIF)

#### 🙌 Thank you for your contributions!

**Full Changelog**:
software-mansion/react-native-reanimated@3.11.0...3.12.0

Package build:
https://github.com/software-mansion/react-native-reanimated/actions/runs/9287839734

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/GSTJ/react-native-magic-modal).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zNzcuOCIsInVwZGF0ZWRJblZlciI6IjM3LjM3Ny44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants