Skip to content

Commit aa8d00b

Browse files
authored
[RNKC-054] - detect keyboard size changes (post fix) (#62)
1 parent f8dfe7c commit aa8d00b

File tree

1 file changed

+31
-30
lines changed

1 file changed

+31
-30
lines changed

android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.reactnativekeyboardcontroller
22

3+
import android.animation.ValueAnimator
34
import android.content.Context
5+
import android.os.Build
46
import android.util.Log
57
import android.view.View
8+
import androidx.core.animation.doOnEnd
69
import androidx.core.graphics.Insets
710
import androidx.core.view.OnApplyWindowInsetsListener
811
import androidx.core.view.ViewCompat
@@ -45,42 +48,40 @@ class KeyboardAnimationCallback(
4548
}
4649

4750
/**
48-
* This method is called everytime when keyboard appears or hides (*)
49-
* and the call happens before `onStart` (in `onStart` we update `this.isKeyboardVisible` field)
50-
*
51-
* *in fact it's getting called much more times, but here for the simplicity we are talking only about keyboard
51+
* When keyboard changes its size we have different behavior per APIs.
52+
* On 21<=API<30 - WindowInsetsAnimationCompat dispatches onStart/onProgress/onEnd events.
53+
* On API>=30 - WindowInsetsAnimationCompat doesn't dispatch anything. As a result behavior
54+
* between different Android versions is not consistent. On old Android versions we have a
55+
* reaction, on newer versions - not. In my understanding it's a bug in core library and the
56+
* behavior should be consistent across all versions of platform. To level the difference we
57+
* have to implement `onApplyWindowInsets` listener and simulate onStart/onProgress/onEnd
58+
* events when keyboard changes its size.
59+
* In the method below we fully recreate the logic that is implemented on old android versions:
60+
* - we dispatch `keyboardWillShow` (onStart);
61+
* - we dispatch change height/progress as animated values (onProgress);
62+
* - we dispatch `keyboardDidShow` (onEnd).
5263
*/
53-
override fun onApplyWindowInsets(v: View?, insets: WindowInsetsCompat): WindowInsetsCompat {
64+
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
5465
// when keyboard appears values will be (false && true)
5566
// when keyboard disappears values will be (true && false)
5667
// having such check allows us not to dispatch unnecessary incorrect events
5768
// the condition will be executed only when keyboard is opened and changes its size
5869
// (for example it happens when user changes keyboard type from 'text' to 'emoji' input
59-
if (isKeyboardVisible && isKeyboardVisible()) {
70+
if (isKeyboardVisible && isKeyboardVisible() && Build.VERSION.SDK_INT >= 30) {
6071
val keyboardHeight = getCurrentKeyboardHeight()
61-
/**
62-
* By default it's up to OS whether to animate keyboard changes or not.
63-
* For example my Xiaomi Redmi Note 5 Pro (Android 9) applies layout animation
64-
* whereas Pixel 3 (Android 12) is not applying layout animation and view changes
65-
* its position instantly. We stick to the default behavior and rely on it.
66-
* Though if we decide to animate always (any animation looks better than instant transition)
67-
* we can use the code below:
68-
*
69-
* <pre>
70-
* val animation = ValueAnimator.ofInt(-this.persistentKeyboardHeight, -keyboardHeight)
71-
*
72-
* animation.addUpdateListener { animator ->
73-
* val toValue = animator.animatedValue as Int
74-
* this.sendEventToJS(KeyboardTransitionEvent(view.id, toValue, 1.0))
75-
* }
76-
* animation.setDuration(250).startDelay = 0
77-
* animation.start()
78-
* </pre>
79-
*
80-
* But for now let's rely on OS preferences.
81-
*/
82-
this.sendEventToJS(KeyboardTransitionEvent(view.id, -keyboardHeight, 1.0))
83-
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
72+
73+
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
74+
75+
val animation = ValueAnimator.ofInt(-this.persistentKeyboardHeight, -keyboardHeight)
76+
animation.addUpdateListener { animator ->
77+
val toValue = animator.animatedValue as Int
78+
this.sendEventToJS(KeyboardTransitionEvent(view.id, toValue, -toValue.toDouble() / keyboardHeight))
79+
}
80+
animation.doOnEnd {
81+
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
82+
}
83+
animation.setDuration(250).startDelay = 0
84+
animation.start()
8485

8586
this.persistentKeyboardHeight = keyboardHeight
8687
}
@@ -132,7 +133,7 @@ class KeyboardAnimationCallback(
132133
} catch (e: ArithmeticException) {
133134
// do nothing, send progress as 0
134135
}
135-
Log.i(TAG, "DiffY: $diffY $height")
136+
Log.i(TAG, "DiffY: $diffY $height $progress")
136137

137138
this.sendEventToJS(KeyboardTransitionEvent(view.id, height, progress))
138139

0 commit comments

Comments
 (0)