Skip to content

Commit

Permalink
[Mobile] - Android - Bring the Keyboard back when closing modals (#57069
Browse files Browse the repository at this point in the history
)

* React Native Bridge - Android - Introduces showAndroidSoftKeyboard to show the keyboard if there's a focused TextInput

* Mobile - Bottom Sheet - Adds usage of showAndroidSoftKeyboard when closing the Modal so it shows the Keyboard on Android for focused TextInputs

* React Native Bridge - Android - Introduces hideAndroidSoftKeyboard to hide the keyboard without triggering blur events

* React Native Bridge - Remove console warnings for unsupported methods, as their names are self-explanatory.

* Update showAndroidSoftKeyboard to take into account when the window focus changed, when we show the Modals these are shown on top of the editor activity.

It also adds an option to delay this for full screen modals

* Mobile - BottomSheet - Enable hardwareAccelerated and useNativeDriverForBackdrop props to improve performance on Android

* Update snapshots

* Removes hasWindowFocus condition as it is not being called hence not needed

* Refactor showAndroidSoftKeyboard to split into several functions, it also removes the delay functionality as it is no longer needed. It fixes an issue where mKeyboardRunnable was not being set.

It removes the delay logic from the Bottom Sheet component and the bridge.

* Updates createShowKeyboardRunnable to get the activity within the runnable instead of getting it as an param

* Remove unneeded check
  • Loading branch information
Gerardo Pacheco authored Dec 18, 2023
1 parent 17dcda5 commit 71b3409
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 2 deletions.
16 changes: 15 additions & 1 deletion packages/components/src/mobile/bottom-sheet/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import SafeArea from 'react-native-safe-area';
/**
* WordPress dependencies
*/
import { subscribeAndroidModalClosed } from '@wordpress/react-native-bridge';
import {
subscribeAndroidModalClosed,
showAndroidSoftKeyboard,
} from '@wordpress/react-native-bridge';
import { Component } from '@wordpress/element';
import { withPreferredColorScheme } from '@wordpress/compose';

Expand Down Expand Up @@ -215,6 +218,11 @@ class BottomSheet extends Component {
if ( this.androidModalClosedSubscription ) {
this.androidModalClosedSubscription.remove();
}

if ( this.props.isVisible ) {
showAndroidSoftKeyboard();
}

if ( this.safeAreaEventSubscription === null ) {
return;
}
Expand Down Expand Up @@ -315,6 +323,9 @@ class BottomSheet extends Component {
onDismiss() {
const { onDismiss } = this.props;

// Restore Keyboard Visibility
showAndroidSoftKeyboard();

if ( onDismiss ) {
onDismiss();
}
Expand Down Expand Up @@ -368,6 +379,7 @@ class BottomSheet extends Component {
onHardwareButtonPress() {
const { onClose } = this.props;
const { handleHardwareButtonPress } = this.state;

if ( handleHardwareButtonPress && handleHardwareButtonPress() ) {
return;
}
Expand Down Expand Up @@ -528,6 +540,8 @@ class BottomSheet extends Component {
}
onAccessibilityEscape={ this.onCloseBottomSheet }
testID="bottom-sheet"
hardwareAccelerated={ true }
useNativeDriverForBackdrop={ true }
{ ...rest }
>
<KeyboardAvoidingView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports[`LinksUI LinksUI renders 1`] = `
backdropOpacity={0.2}
backdropTransitionInTiming={50}
backdropTransitionOutTiming={50}
hardwareAccelerated={true}
isVisible={true}
onAccessibilityEscape={[Function]}
onBackButtonPress={[Function]}
Expand All @@ -18,6 +19,7 @@ exports[`LinksUI LinksUI renders 1`] = `
preferredColorScheme="light"
swipeDirection="down"
testID="link-settings-modal"
useNativeDriverForBackdrop={true}
>
<View
behavior={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;

import androidx.annotation.Nullable;
Expand Down Expand Up @@ -44,6 +45,7 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu
DeferredEventEmitter.JSEventEmitter {
private final ReactApplicationContext mReactContext;
private final GutenbergBridgeJS2Parent mGutenbergBridgeJS2Parent;
private Runnable mKeyboardRunnable;

private static final String EVENT_NAME_REQUEST_GET_HTML = "requestGetHtml";
private static final String EVENT_NAME_UPDATE_HTML = "updateHtml";
Expand Down Expand Up @@ -554,14 +556,69 @@ private ConnectionStatusCallback requestConnectionStatusCallback(final Callback
};
}

@ReactMethod
public void showAndroidSoftKeyboard() {
Activity currentActivity = mReactContext.getCurrentActivity();
if (isAnyViewFocused()) {
// Cancel any previously scheduled Runnable
if (mKeyboardRunnable != null) {
currentActivity.getWindow().getDecorView().removeCallbacks(mKeyboardRunnable);
}

View currentFocusedView = getCurrentFocusedView();
currentFocusedView.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
mKeyboardRunnable = createShowKeyboardRunnable();
currentActivity.getWindow().getDecorView().post(mKeyboardRunnable);
currentFocusedView.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
}
}
});
}
}

private Runnable createShowKeyboardRunnable() {
return new Runnable() {
@Override
public void run() {
try {
Activity activity = mReactContext.getCurrentActivity();
View activeFocusedView = getCurrentFocusedView();
if (activeFocusedView != null && activity.getWindow().getDecorView().isShown()) {
InputMethodManager imm =
(InputMethodManager) mReactContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(activeFocusedView, InputMethodManager.SHOW_IMPLICIT);
}
} catch (Exception e) {
// Noop
}
}
};
}

private View getCurrentFocusedView() {
Activity activity = mReactContext.getCurrentActivity();
if (activity == null) {
return null;
}
return activity.getCurrentFocus();
}

private boolean isAnyViewFocused() {
View getCurrentFocusedView = getCurrentFocusedView();
return getCurrentFocusedView != null;
}

@ReactMethod
public void hideAndroidSoftKeyboard() {
Activity currentActivity = mReactContext.getCurrentActivity();
if (currentActivity != null) {
View currentFocusedView = currentActivity.getCurrentFocus();
if (currentFocusedView != null) {
InputMethodManager imm =
(InputMethodManager) mReactContext.getSystemService(Context.INPUT_METHOD_SERVICE);
(InputMethodManager) mReactContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(currentFocusedView.getWindowToken(), 0);
}
}
Expand Down
14 changes: 14 additions & 0 deletions packages/react-native-bridge/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,20 @@ export function sendEventToHost( eventName, properties ) {
);
}

/**
* Shows Android's soft keyboard if there's a TextInput focused and
* the keyboard is hidden.
*
* @return {void}
*/
export function showAndroidSoftKeyboard() {
if ( isIOS ) {
return;
}

RNReactNativeGutenbergBridge.showAndroidSoftKeyboard();
}

/**
* Hides Android's soft keyboard.
*
Expand Down
1 change: 1 addition & 0 deletions test/native/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ jest.mock( '@wordpress/react-native-bridge', () => {
subscribeOnRedoPressed: jest.fn(),
useIsConnected: jest.fn( () => ( { isConnected: true } ) ),
editorDidMount: jest.fn(),
showAndroidSoftKeyboard: jest.fn(),
hideAndroidSoftKeyboard: jest.fn(),
editorDidAutosave: jest.fn(),
subscribeMediaUpload: jest.fn(),
Expand Down

0 comments on commit 71b3409

Please sign in to comment.