Skip to content

Commit c4f9165

Browse files
feat!(keyboard): Improvements to keyboard show / hide events (#1980)
Co-authored-by: Mark Anderson <mark@ionic.io>
1 parent bc61f5c commit c4f9165

2 files changed

Lines changed: 75 additions & 107 deletions

File tree

Lines changed: 74 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
package com.capacitorjs.plugins.keyboard;
22

33
import android.content.Context;
4-
import android.graphics.Insets;
5-
import android.graphics.Point;
64
import android.graphics.Rect;
7-
import android.os.Build;
85
import android.util.DisplayMetrics;
9-
import android.view.Display;
106
import android.view.View;
11-
import android.view.ViewTreeObserver;
127
import android.view.Window;
13-
import android.view.WindowInsets;
148
import android.view.inputmethod.InputMethodManager;
159
import android.widget.FrameLayout;
10+
import androidx.annotation.NonNull;
1611
import androidx.annotation.Nullable;
1712
import androidx.appcompat.app.AppCompatActivity;
18-
import com.getcapacitor.Logger;
13+
import androidx.core.view.ViewCompat;
14+
import androidx.core.view.WindowInsetsAnimationCompat;
15+
import androidx.core.view.WindowInsetsCompat;
16+
import java.util.List;
1917

2018
public class Keyboard {
2119

@@ -24,16 +22,10 @@ interface KeyboardEventListener {
2422
}
2523

2624
private AppCompatActivity activity;
27-
private ViewTreeObserver.OnGlobalLayoutListener list;
2825
private View rootView;
29-
private View mChildOfContent;
3026
private int usableHeightPrevious;
3127
private FrameLayout.LayoutParams frameLayoutParams;
32-
33-
@Nullable
34-
public KeyboardEventListener getKeyboardEventListener() {
35-
return keyboardEventListener;
36-
}
28+
private View mChildOfContent;
3729

3830
public void setKeyboardEventListener(@Nullable KeyboardEventListener keyboardEventListener) {
3931
this.keyboardEventListener = keyboardEventListener;
@@ -49,117 +41,69 @@ public void setKeyboardEventListener(@Nullable KeyboardEventListener keyboardEve
4941

5042
public Keyboard(AppCompatActivity activity, boolean resizeOnFullScreen) {
5143
this.activity = activity;
52-
//calculate density-independent pixels (dp)
53-
//http://developer.android.com/guide/practices/screens_support.html
54-
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
55-
final float density = dm.density;
5644

5745
//http://stackoverflow.com/a/4737265/1091751 detect if keyboard is showing
5846
FrameLayout content = activity.getWindow().getDecorView().findViewById(android.R.id.content);
5947
rootView = content.getRootView();
60-
list =
61-
new ViewTreeObserver.OnGlobalLayoutListener() {
62-
int previousHeightDiff = 0;
6348

49+
ViewCompat.setWindowInsetsAnimationCallback(
50+
rootView,
51+
new WindowInsetsAnimationCompat.Callback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) {
52+
@NonNull
6453
@Override
65-
public void onGlobalLayout() {
66-
Rect r = new Rect();
67-
//r will be populated with the coordinates of your view that area still visible.
68-
rootView.getWindowVisibleDisplayFrame(r);
69-
70-
// cache properties for later use
71-
int rootViewHeight = rootView.getRootView().getHeight();
72-
int resultBottom = r.bottom;
73-
int screenHeight;
74-
75-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
76-
Insets windowInsets = rootView.getRootWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
77-
screenHeight = rootViewHeight;
78-
resultBottom = resultBottom + windowInsets.bottom;
79-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
80-
WindowInsets windowInsets = rootView.getRootWindowInsets();
81-
int stableInsetBottom = getLegacyStableInsetBottom(windowInsets);
82-
screenHeight = rootViewHeight;
83-
resultBottom = resultBottom + stableInsetBottom;
84-
} else {
85-
Point size = getLegacySizePoint();
86-
screenHeight = size.y;
87-
}
54+
public WindowInsetsCompat onProgress(
55+
@NonNull WindowInsetsCompat insets,
56+
@NonNull List<WindowInsetsAnimationCompat> runningAnimations
57+
) {
58+
return insets;
59+
}
8860

89-
int heightDiff = screenHeight - resultBottom;
90-
91-
int pixelHeightDiff = (int) (heightDiff / density);
92-
93-
if (pixelHeightDiff > 100 && pixelHeightDiff != previousHeightDiff) { // if more than 100 pixels, its probably a keyboard...
94-
if (resizeOnFullScreen) {
95-
possiblyResizeChildOfContent(true);
96-
}
97-
98-
if (keyboardEventListener != null) {
99-
keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_SHOW, pixelHeightDiff);
100-
keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_SHOW, pixelHeightDiff);
101-
} else {
102-
Logger.warn("Native Keyboard Event Listener not found");
103-
}
104-
} else if (pixelHeightDiff != previousHeightDiff && (previousHeightDiff - pixelHeightDiff) > 100) {
105-
if (resizeOnFullScreen) {
106-
possiblyResizeChildOfContent(false);
107-
}
108-
109-
if (keyboardEventListener != null) {
110-
keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_HIDE, 0);
111-
keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_HIDE, 0);
112-
} else {
113-
Logger.warn("Native Keyboard Event Listener not found");
114-
}
61+
@NonNull
62+
@Override
63+
public WindowInsetsAnimationCompat.BoundsCompat onStart(
64+
@NonNull WindowInsetsAnimationCompat animation,
65+
@NonNull WindowInsetsAnimationCompat.BoundsCompat bounds
66+
) {
67+
boolean showingKeyboard = ViewCompat.getRootWindowInsets(rootView).isVisible(WindowInsetsCompat.Type.ime());
68+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(rootView);
69+
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
70+
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
71+
final float density = dm.density;
72+
73+
if (resizeOnFullScreen) {
74+
possiblyResizeChildOfContent(showingKeyboard);
11575
}
116-
previousHeightDiff = pixelHeightDiff;
117-
}
11876

119-
private void possiblyResizeChildOfContent(boolean keyboardShown) {
120-
int usableHeightNow = keyboardShown ? computeUsableHeight() : -1;
121-
if (usableHeightPrevious != usableHeightNow) {
122-
frameLayoutParams.height = usableHeightNow;
123-
mChildOfContent.requestLayout();
124-
usableHeightPrevious = usableHeightNow;
77+
if (showingKeyboard) {
78+
keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_SHOW, Math.round(imeHeight / density));
79+
} else {
80+
keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_HIDE, 0);
12581
}
82+
return super.onStart(animation, bounds);
12683
}
12784

128-
private int computeUsableHeight() {
129-
Rect r = new Rect();
130-
mChildOfContent.getWindowVisibleDisplayFrame(r);
131-
return isOverlays() ? r.bottom : r.height();
85+
@Override
86+
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
87+
super.onEnd(animation);
88+
boolean showingKeyboard = ViewCompat.getRootWindowInsets(rootView).isVisible(WindowInsetsCompat.Type.ime());
89+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(rootView);
90+
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
91+
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
92+
final float density = dm.density;
93+
94+
if (showingKeyboard) {
95+
keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_SHOW, Math.round(imeHeight / density));
96+
} else {
97+
keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_HIDE, 0);
98+
}
13299
}
100+
}
101+
);
133102

134-
@SuppressWarnings("deprecation")
135-
private boolean isOverlays() {
136-
final Window window = activity.getWindow();
137-
return (
138-
(window.getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) ==
139-
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
140-
);
141-
}
142-
};
143103
mChildOfContent = content.getChildAt(0);
144-
rootView.getViewTreeObserver().addOnGlobalLayoutListener(list);
145104
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
146105
}
147106

148-
@SuppressWarnings("deprecation")
149-
private int getLegacyStableInsetBottom(WindowInsets windowInsets) {
150-
return windowInsets.getStableInsetBottom();
151-
}
152-
153-
@SuppressWarnings("deprecation")
154-
private Point getLegacySizePoint() {
155-
// calculate screen height differently for android versions <23: Lollipop 5.x, Marshmallow 6.x
156-
//http://stackoverflow.com/a/29257533/3642890 beware of nexus 5
157-
Display display = activity.getWindowManager().getDefaultDisplay();
158-
Point size = new Point();
159-
display.getSize(size);
160-
return size;
161-
}
162-
163107
public void show() {
164108
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(activity.getCurrentFocus(), 0);
165109
}
@@ -174,4 +118,27 @@ public boolean hide() {
174118
return true;
175119
}
176120
}
121+
122+
private void possiblyResizeChildOfContent(boolean keyboardShown) {
123+
int usableHeightNow = keyboardShown ? computeUsableHeight() : -1;
124+
if (usableHeightPrevious != usableHeightNow) {
125+
frameLayoutParams.height = usableHeightNow;
126+
mChildOfContent.requestLayout();
127+
usableHeightPrevious = usableHeightNow;
128+
}
129+
}
130+
131+
private int computeUsableHeight() {
132+
Rect r = new Rect();
133+
mChildOfContent.getWindowVisibleDisplayFrame(r);
134+
return isOverlays() ? r.bottom : r.height();
135+
}
136+
137+
@SuppressWarnings("deprecation")
138+
private boolean isOverlays() {
139+
final Window window = activity.getWindow();
140+
return (
141+
(window.getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
142+
);
143+
}
177144
}

keyboard/android/src/main/java/com/capacitorjs/plugins/keyboard/KeyboardPlugin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public void load() {
1919
() -> {
2020
boolean resizeOnFullScreen = getConfig().getBoolean("resizeOnFullScreen", false);
2121
implementation = new Keyboard(getActivity(), resizeOnFullScreen);
22+
2223
implementation.setKeyboardEventListener(this::onKeyboardEvent);
2324
}
2425
);

0 commit comments

Comments
 (0)