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

Proposal: Merge with react-native-bars project #259

Open
zoontek opened this issue Oct 20, 2023 · 11 comments
Open

Proposal: Merge with react-native-bars project #259

zoontek opened this issue Oct 20, 2023 · 11 comments
Assignees
Labels
feature request 👀 Request new functionality of the library

Comments

@zoontek
Copy link
Contributor

zoontek commented Oct 20, 2023

Hi 👋

First, thanks for your work on this library, this is very much needed in the ecosystem!

I'm currently maintaining react-native-bars, a library for fully transparent (and fallback to translucent) system bars. Compared to KeyboardProvider statusBarTranslucent and navigationBarTranslucent, it provides an android theme that avoid the initial blink (before the React app kicks in) + provides some useful components to handle barStyle (similarly as the builtin StatusBar component).

As keyboard handling is super related to system bars, I think it could be great to merge those libs? (I even think that it should enforce fully transparent UI without any choice, exactly like react-native-bars or iOS + recommend react-native-safe-area-context for dealing with safe areas in a similar way on both OS, but it's my personal opinion 😅)

@zoontek zoontek changed the title Merge with react-native-bars project Proposal: Merge with react-native-bars project Oct 20, 2023
@kirillzyusko kirillzyusko added the feature request 👀 Request new functionality of the library label Oct 23, 2023
@kirillzyusko
Copy link
Owner

Hello @zoontek 👋

I understand the motivation to merge these projects (because of native code in Android).

But I'm just thinking, that people who wants to control NavigationBar may not understand why do they need to install library that helps you to deal with keyboard? 🤔 What do you think about it?

@zoontek
Copy link
Contributor Author

zoontek commented Oct 23, 2023

It's more the opposite IMHO: when installing react-native-keyboard-controller, it enforces edge to edge mode (which I totally get, since keyboard management is different when enabling it - as WindowCompat.setDecorFitsSystemWindows(window, false) changes the way keyboard is handled, cf zoontek/react-native-bars#18)

Which means if system bars became transparent (not translucent), there's no way to control their content without another lib (here react-native-bars). For me, keyboard handler should absorb bars, not the other way around 🙂 (or at least, they should work perfectly together)

@kirillzyusko
Copy link
Owner

Yes @zoontek it makes sense what you are saying! I just was confused about SEO search (obviously name react-native-keyboard-controller doesn't give a confidence that it's a right library for managing Navigation and System bar 😅).

In order to merge projects we need:

  • to have init method for overriding a theme to avoid a blink;
  • new NativeModule for managing NavigationBar and StatusBar properties;
  • components in JS (NavigationBar, StatusBar).

Did I miss anything?

@zoontek
Copy link
Contributor Author

zoontek commented Oct 25, 2023

@kirillzyusko I think there's no need for the init method in react-native-keyboard-controller, as setting a parent style (Theme.EdgeToEdge) + waiting for KeyboardProvider to kicks in should do the job.

For the rest, that's it, yeah. Maybe I could experiment on a branch if you are OK with that?

@kirillzyusko
Copy link
Owner

@zoontek yes, sure, feel free to experiment with it 👍 😊

@zoontek
Copy link
Contributor Author

zoontek commented Dec 10, 2023

I noticed something: navigationBarTranslucent currently doesn't seem to enable edge-to-edge layout.
Navigation bar stays dark and the UI is not drawn under:

Screenshot 2023-12-10 at 13 37 42

So, I enforce edge-to-edge like this:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(null);

  // set up the edge-to-edge display
  final boolean darkContentBars = true;
  final Window window = getWindow();
  final View view = window.getDecorView();

  final WindowInsetsControllerCompat insetsController =
    new WindowInsetsControllerCompat(window, view);

  WindowCompat.setDecorFitsSystemWindows(window, false);

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
    window.clearFlags(
      WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
        WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
    );

    window.setStatusBarColor(Color.TRANSPARENT);
    window.setNavigationBarColor(Color.TRANSPARENT);

    insetsController.setAppearanceLightStatusBars(darkContentBars);
    insetsController.setAppearanceLightNavigationBars(darkContentBars);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      window.setStatusBarContrastEnforced(false);
      window.setNavigationBarContrastEnforced(false);
    }
  } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

    window.setStatusBarColor(Color.TRANSPARENT);

    insetsController.setAppearanceLightStatusBars(darkContentBars);
  } else {
    window.addFlags(
      WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
        WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
    );
  }
}

By doing so, this creates a shift that must be handled for each react-native-keyboard-controller component / hook, like, for example (chat example):

const { height: telegram } = useTelegramTransitions();
const { height: platform } = useReanimatedKeyboardAnimation();
const { bottom: bottomInset } = useSafeAreaInsets();

const height = useDerivedValue(
  () => (isTGTransition ? telegram.value : platform.value) - bottomInset,
  [isTGTransition, bottomInset]
);

In this video, it's not handled on the first screen, but it is on the second:

Screen.Recording.2023-12-10.at.14.08.45.mp4

To handle it on the first screen, we need an Animated.subtract(height, bottomInset). But shouldn't it be handled as navigationBarTranslucent is true?

WIP code: 3080d97

@kirillzyusko
Copy link
Owner

Hi @zoontek

I think you also need to set <item name="android:windowTranslucentNavigation">true</item> and only then it will stretch to edge-to-edge

And I think you are right, current implementation of RNKC has a bug - when I calculate keyboard height I always subtract navigationBar height: https://github.com/kirillzyusko/react-native-keyboard-controller/blob/main/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt#L356

And you are right, it seems like we don't need to subtract if navigationBarTranslucent=true

Let me know what do you think :)

@zoontek
Copy link
Contributor Author

zoontek commented Dec 10, 2023

<item name="android:windowTranslucentNavigation">true</item> set the navigation bar to semi-transparent (in such cases, there's no need for an additional <NavigationBar /> component, indeed), this code is used to make it fully transparent (like what's possible with <StatusBar />)

Just tried it, removing - navigationBar does not seems to have an impact here 😕

@kirillzyusko
Copy link
Owner

Just tried it, removing - navigationBar does not seems to have an impact here 😕

Hm, interesting, I thought it should have an impact 🤔 I'll try to test your code tomorrow 👀

@kirillzyusko
Copy link
Owner

Hello @zoontek

You'll need to change:

val diff = Insets.subtract(typesInset, Insets.NONE).let {
  Insets.max(it, Insets.NONE)
}

Then the result will be (don't pay an attention to blue circle - it's replica of keyboard animation and was part of very early experiments):

image

So my assumption is that we can pass persistentInsetTypes as optional param (and if it's optional then use Insets.NONE in onProgress callback).

Also as I said earlier similar changes should be reflected in getCurrentKeyboardHeight - if you switch keyboard input type (emoji, gifs) then circle will be cut off again:

image

Also you can reach out to me via discord (nickname is kiryl.ziusko) - I'm responding there much faster 👀

@zoontek
Copy link
Contributor Author

zoontek commented Dec 11, 2023

@kirillzyusko That's it! We can even simplify this to:

// We coerce the insets to be >= 0, to make sure we don't use negative insets.
val diff = Insets.max(typesInset, Insets.NONE)

I never actually realized that you shouldn't subtract system bars insets in edge-to-edge mode, given the fact that I blindly followed this page of their documentation (which say that edge-to-edge is a prerequisite) and their app examples (here and here).

But this makes no sense at all when you draw under the system bars 🤔
Dropping systemBars insets substraction makes the JS code much more simpler to think about.

Screen.Recording.2023-12-11.at.10.06.21.mp4

I'm still in favor of having a proper edge-to-edge layout with fully transparent bars by default (and not really having a choice), as this kind of possibilities will be hard to compose with, given the fact that a user might have one of these 4 config (+ non transparent system bars does not exist on iOS, so this would actually reduce the fragmentation between the 2 platforms 😄):

Transparent status bar Transparent navigation bar
Case 1
Case 2
Case 3
Case 4

Anyway, I will reach you on Discord next time, thanks for your help 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request 👀 Request new functionality of the library
Projects
None yet
Development

No branches or pull requests

2 participants