diff --git a/android/src/main/java/com/capacitorjs/plugins/keyboard/Keyboard.java b/android/src/main/java/com/capacitorjs/plugins/keyboard/Keyboard.java index 7cf87fc..7c42c39 100644 --- a/android/src/main/java/com/capacitorjs/plugins/keyboard/Keyboard.java +++ b/android/src/main/java/com/capacitorjs/plugins/keyboard/Keyboard.java @@ -2,9 +2,7 @@ import android.content.Context; import android.graphics.Rect; -import android.os.Build; import android.util.DisplayMetrics; -import android.util.TypedValue; import android.view.View; import android.view.Window; import android.view.inputmethod.InputMethodManager; @@ -31,6 +29,7 @@ interface KeyboardEventListener { private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private View mChildOfContent; + private boolean resizeEnabled; public void setKeyboardEventListener(@Nullable KeyboardEventListener keyboardEventListener) { this.keyboardEventListener = keyboardEventListener; @@ -53,19 +52,31 @@ public Keyboard(Bridge bridge, boolean resizeOnFullScreen) { // We may want to deprecate this constructor in the future, but we are keeping it now to keep backward compatibility with cap 7 public Keyboard(AppCompatActivity activity, boolean resizeOnFullScreen) { this.activity = activity; + this.resizeEnabled = resizeOnFullScreen; //http://stackoverflow.com/a/4737265/1091751 detect if keyboard is showing FrameLayout content = activity.getWindow().getDecorView().findViewById(android.R.id.content); rootView = content.getRootView(); - ViewCompat.setOnApplyWindowInsetsListener(content, (v, insets) -> { - boolean showingKeyboard = ViewCompat.getRootWindowInsets(rootView).isVisible(WindowInsetsCompat.Type.ime()); + ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> { + WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(rootView); + if (rootInsets == null) { + return insets; + } + boolean showingKeyboard = rootInsets.isVisible(WindowInsetsCompat.Type.ime()); - if (showingKeyboard && resizeOnFullScreen) { + if (showingKeyboard && resizeEnabled) { possiblyResizeChildOfContent(true); } - v.onApplyWindowInsets(insets.toWindowInsets()); + // When resize is disabled, consume IME insets so that the WebView + // (and any other child) does not reflow its content when the keyboard + // appears (counteracts Capacitor bridge inset handling added in 8.3.0+). + if (!resizeEnabled && showingKeyboard) { + return new WindowInsetsCompat.Builder(insets) + .setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE) + .build(); + } return insets; }); @@ -88,20 +99,25 @@ public WindowInsetsAnimationCompat.BoundsCompat onStart( @NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds ) { - boolean showingKeyboard = ViewCompat.getRootWindowInsets(rootView).isVisible(WindowInsetsCompat.Type.ime()); WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(rootView); + if (insets == null) { + return super.onStart(animation, bounds); + } + boolean showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom; DisplayMetrics dm = activity.getResources().getDisplayMetrics(); final float density = dm.density; - if (resizeOnFullScreen) { + if (Keyboard.this.resizeEnabled) { possiblyResizeChildOfContent(showingKeyboard); } - if (showingKeyboard) { - keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_SHOW, Math.round(imeHeight / density)); - } else { - keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_HIDE, 0); + if (keyboardEventListener != null) { + if (showingKeyboard) { + keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_SHOW, Math.round(imeHeight / density)); + } else { + keyboardEventListener.onKeyboardEvent(EVENT_KB_WILL_HIDE, 0); + } } return super.onStart(animation, bounds); } @@ -109,16 +125,21 @@ public WindowInsetsAnimationCompat.BoundsCompat onStart( @Override public void onEnd(@NonNull WindowInsetsAnimationCompat animation) { super.onEnd(animation); - boolean showingKeyboard = ViewCompat.getRootWindowInsets(rootView).isVisible(WindowInsetsCompat.Type.ime()); WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(rootView); + if (insets == null) { + return; + } + boolean showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom; DisplayMetrics dm = activity.getResources().getDisplayMetrics(); final float density = dm.density; - if (showingKeyboard) { - keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_SHOW, Math.round(imeHeight / density)); - } else { - keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_HIDE, 0); + if (keyboardEventListener != null) { + if (showingKeyboard) { + keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_SHOW, Math.round(imeHeight / density)); + } else { + keyboardEventListener.onKeyboardEvent(EVENT_KB_DID_HIDE, 0); + } } } } @@ -128,6 +149,14 @@ public void onEnd(@NonNull WindowInsetsAnimationCompat animation) { frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } + public void setResizeEnabled(boolean enabled) { + this.resizeEnabled = enabled; + } + + public boolean isResizeEnabled() { + return this.resizeEnabled; + } + public void show() { ((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(activity.getCurrentFocus(), 0); } diff --git a/android/src/main/java/com/capacitorjs/plugins/keyboard/KeyboardPlugin.java b/android/src/main/java/com/capacitorjs/plugins/keyboard/KeyboardPlugin.java index bf6735a..69aa28b 100644 --- a/android/src/main/java/com/capacitorjs/plugins/keyboard/KeyboardPlugin.java +++ b/android/src/main/java/com/capacitorjs/plugins/keyboard/KeyboardPlugin.java @@ -12,11 +12,13 @@ public class KeyboardPlugin extends Plugin { private Keyboard implementation; + private volatile String currentResizeMode = "none"; @Override public void load() { execute(() -> { boolean resizeOnFullScreen = getConfig().getBoolean("resizeOnFullScreen", false); + currentResizeMode = resizeOnFullScreen ? "native" : "none"; implementation = new Keyboard(getBridge(), resizeOnFullScreen); implementation.setKeyboardEventListener(this::onKeyboardEvent); @@ -59,12 +61,20 @@ public void setStyle(PluginCall call) { @PluginMethod public void setResizeMode(PluginCall call) { - call.unimplemented(); + String mode = call.getString("mode", "none"); + execute(() -> { + boolean resizeEnabled = !"none".equalsIgnoreCase(mode); + implementation.setResizeEnabled(resizeEnabled); + currentResizeMode = mode; + call.resolve(); + }); } @PluginMethod public void getResizeMode(PluginCall call) { - call.unimplemented(); + JSObject result = new JSObject(); + result.put("mode", currentResizeMode); + call.resolve(result); } @PluginMethod diff --git a/ios/Sources/KeyboardPlugin/Keyboard.m b/ios/Sources/KeyboardPlugin/Keyboard.m index 29c0708..9f8ad1f 100644 --- a/ios/Sources/KeyboardPlugin/Keyboard.m +++ b/ios/Sources/KeyboardPlugin/Keyboard.m @@ -240,6 +240,20 @@ - (void)_updateFrame [self.webView setFrame:CGRectMake(wf.origin.x, wf.origin.y, f.size.width - wf.origin.x, f.size.height - wf.origin.y - self.paddingBottom)]; break; } + case ResizeNone: + { + // Actively restore the webview to its full-screen frame to counteract any + // keyboard-induced resize that Capacitor's bridge may apply (8.3.0+). + [self.webView setFrame:CGRectMake(wf.origin.x, wf.origin.y, f.size.width - wf.origin.x, f.size.height - wf.origin.y)]; + // Also reset additionalSafeAreaInsets.bottom in case the bridge raised it + // to push content above the keyboard. + UIEdgeInsets safeInsets = self.bridge.viewController.additionalSafeAreaInsets; + if (safeInsets.bottom != 0) { + safeInsets.bottom = 0; + self.bridge.viewController.additionalSafeAreaInsets = safeInsets; + } + break; + } default: break; }