Skip to content

Commit 6a723f7

Browse files
authored
Fix keyboard-awareness bug (#8087)
1 parent 37717da commit 6a723f7

4 files changed

Lines changed: 112 additions & 23 deletions

File tree

e2e/Keyboard.test.js

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,78 @@
11
import { default as TestIDs, default as testIDs } from '../playground/src/testIDs';
2-
import Android from './AndroidUtils';
2+
import { device } from 'detox';
33
import Utils from './Utils';
44

55
const { elementByLabel, elementById } = Utils;
66

7+
const KBD_OBSCURED_TEXT = 'Keyboard Demo';
8+
9+
const androidDriver = {
10+
async init() {
11+
if (device.getPlatform() !== 'android') {
12+
return;
13+
}
14+
15+
const { id: adbName } = device;
16+
const { adb } = device.deviceDriver;
17+
18+
if (!adb || !adbName) {
19+
throw new Error(`Keyboard driver init failed (id=${adbName}, hasADB=${!!adb})`);
20+
}
21+
22+
this.adb = adb;
23+
this.adbName = adbName;
24+
this.kbdEnabled = await adb.shell(adbName, 'settings get Secure show_ime_with_hard_keyboard');
25+
26+
if (!(this.kbdEnabled === '0' || this.kbdEnabled === '1')) {
27+
console.warn('[KbdDriver] Unable to get on-screen KBD setting, defaulting to false');
28+
this.kbdEnabled = '0';
29+
}
30+
},
31+
32+
async enableOnScreenKeyboard() {
33+
if (!this.adb) {
34+
// Not initialized
35+
return;
36+
}
37+
await this.adb.shell(this.adbName, 'settings put Secure show_ime_with_hard_keyboard 1');
38+
},
39+
40+
async restoreOnScreenKeyboard() {
41+
if (!this.adb) {
42+
// Not initialized
43+
return;
44+
}
45+
await this.adb.shell(this.adbName, `settings put Secure show_ime_with_hard_keyboard ${this.kbdEnabled}`);
46+
},
47+
}
48+
749
describe.e2e('Keyboard', () => {
50+
beforeAll(async () => {
51+
await androidDriver.init();
52+
await androidDriver.enableOnScreenKeyboard();
53+
});
54+
55+
afterAll(async () => {
56+
await androidDriver.restoreOnScreenKeyboard();
57+
});
58+
859
beforeEach(async () => {
960
await device.launchApp({ newInstance: true });
1061
await elementById(TestIDs.KEYBOARD_SCREEN_BTN).tap();
1162
});
1263

1364
it('Push - should close keyboard when Back clicked', async () => {
65+
await expect(elementByLabel(KBD_OBSCURED_TEXT)).toBeVisible();
1466
await elementById(TestIDs.TEXT_INPUT1).tap();
15-
await expect(elementByLabel('Keyboard Demo')).not.toBeVisible();
67+
await expect(elementByLabel(KBD_OBSCURED_TEXT)).not.toBeVisible();
1668
await elementById(TestIDs.BACK_BUTTON).tap();
1769
await expect(elementById(testIDs.MAIN_BOTTOM_TABS)).toBeVisible();
1870
});
1971

2072
it('Modal - should close keyboard when close clicked', async () => {
2173
await elementById(TestIDs.MODAL_BTN).tap();
2274
await elementById(TestIDs.TEXT_INPUT1).tap();
23-
await expect(elementByLabel('Keyboard Demo')).not.toBeVisible();
75+
await expect(elementByLabel(KBD_OBSCURED_TEXT)).not.toBeVisible();
2476
await elementById(TestIDs.DISMISS_MODAL_TOPBAR_BTN).tap();
2577
await expect(elementById(testIDs.MAIN_BOTTOM_TABS)).toBeVisible();
2678
});
@@ -46,4 +98,10 @@ describe.e2e('Keyboard', () => {
4698
await elementById(TestIDs.MODAL_BTN).tap();
4799
await expect(elementById(TestIDs.TEXT_INPUT1)).not.toBeFocused();
48100
});
101+
102+
it(':android: should respect UI with keyboard awareness', async () => {
103+
await elementById(TestIDs.PUSH_KEYBOARD_SCREEN_STICKY_FOOTER).tap();
104+
await elementById(TestIDs.TEXT_INPUT2).tap();
105+
await expect(elementByLabel(KBD_OBSCURED_TEXT)).toBeVisible();
106+
});
49107
});

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,12 @@ public Animator getPopAnimation(Options appearingOptions, Options disappearingOp
316316
@Override
317317
protected WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
318318
Insets sysInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
319-
view.setPaddingRelative(0, 0, 0, sysInsets.bottom);
320-
return WindowInsetsCompat.CONSUMED;
321-
}
319+
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
322320

321+
int bottomInset = (imeInsets.bottom > 0) ? 0 : sysInsets.bottom;
322+
view.setPaddingRelative(0, 0, 0, bottomInset);
323+
return insets;
324+
}
323325

324326
@RestrictTo(RestrictTo.Scope.TESTS)
325327
public BottomTabs getBottomTabs() {

playground/src/screens/KeyboardScreen.tsx

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import React from 'react';
2-
import { View, ScrollView, Dimensions, StyleSheet, Image, TextInput, Text } from 'react-native';
2+
import {
3+
SafeAreaView,
4+
View,
5+
ScrollView,
6+
Dimensions,
7+
StyleSheet,
8+
Image,
9+
TextInput,
10+
Text,
11+
KeyboardAvoidingView,
12+
} from 'react-native';
313
import {
414
NavigationProps,
515
NavigationComponent,
@@ -15,6 +25,7 @@ const KEYBOARD_LABEL = 'Keyboard Demo';
1525
interface Props extends NavigationProps {
1626
title?: string;
1727
autoFocus?: boolean;
28+
stickyFooter?: boolean;
1829
}
1930

2031
export default class KeyboardScreen extends NavigationComponent<Props> {
@@ -34,6 +45,7 @@ export default class KeyboardScreen extends NavigationComponent<Props> {
3445
},
3546
};
3647
}
48+
3749
constructor(props: Props) {
3850
super(props);
3951
Navigation.events().bindComponent(this);
@@ -50,30 +62,41 @@ export default class KeyboardScreen extends NavigationComponent<Props> {
5062
}
5163

5264
render() {
65+
const FooterRoot = this.props.stickyFooter === true ? KeyboardAvoidingView : View;
5366
return (
54-
<View style={styles.root}>
67+
<SafeAreaView style={styles.root}>
5568
<ScrollView>
5669
<Image style={styles.image} source={require('../../img/2048.jpeg')} />
5770
<View style={{ alignItems: 'center' }}>
5871
<Button
5972
style={styles.button}
60-
label={'Modal Keyboard Screen'}
73+
label={'Modal screen'}
6174
testID={testIDs.MODAL_BTN}
6275
onPress={async () => {
6376
await this.openModalKeyboard(undefined);
6477
}}
6578
/>
79+
<View style={styles.row}>
80+
<Button
81+
style={styles.button}
82+
label={'Push screen w/ focus'}
83+
testID={testIDs.PUSH_FOCUSED_KEYBOARD_SCREEN}
84+
onPress={async () => {
85+
await this.openPushedKeyboard(undefined, true);
86+
}}
87+
/>
88+
<Button
89+
style={styles.button}
90+
label={'w/ sticky-footer'}
91+
testID={testIDs.PUSH_KEYBOARD_SCREEN_STICKY_FOOTER}
92+
onPress={async () => {
93+
await this.openPushedKeyboard(undefined, undefined, true);
94+
}}
95+
/>
96+
</View>
6697
<Button
6798
style={styles.button}
68-
label={'Push Focused Keyboard Screen'}
69-
testID={testIDs.PUSH_FOCUSED_KEYBOARD_SCREEN}
70-
onPress={async () => {
71-
await this.openPushedKeyboard(undefined, true);
72-
}}
73-
/>
74-
<Button
75-
style={styles.button}
76-
label={'Show Focused Keyboard Screen Modal'}
99+
label={'Modal screen w/ focus'}
77100
testID={testIDs.MODAL_FOCUSED_KEYBOARD_SCREEN}
78101
onPress={async () => {
79102
await this.openModalKeyboard(undefined, true);
@@ -104,20 +127,21 @@ export default class KeyboardScreen extends NavigationComponent<Props> {
104127
/>
105128
</View>
106129
</ScrollView>
107-
<View style={styles.footer}>
108-
<Text style={styles.input}> {KEYBOARD_LABEL}</Text>
109-
</View>
110-
</View>
130+
<FooterRoot behavior="height" style={styles.footer}>
131+
<Text style={styles.input}>{KEYBOARD_LABEL}</Text>
132+
</FooterRoot>
133+
</SafeAreaView>
111134
);
112135
}
113136

114-
openPushedKeyboard = async (text?: string, autoFocus?: boolean) => {
137+
openPushedKeyboard = async (text?: string, autoFocus?: boolean, stickyFooter?: boolean) => {
115138
await Navigation.push(this.props.componentId, {
116139
component: {
117140
name: Screens.KeyboardScreen,
118141
passProps: {
119142
title: text,
120143
autoFocus,
144+
stickyFooter,
121145
},
122146
},
123147
});
@@ -169,4 +193,8 @@ const styles = StyleSheet.create({
169193
width: screenWidth,
170194
resizeMode: 'cover',
171195
},
196+
row: {
197+
flexDirection: 'row',
198+
justifyContent: 'space-between',
199+
},
172200
});

playground/src/testIDs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const testIDs = {
2828
MODAL_DISABLED_BACK_BTN: 'SHOW_MODAL_DISABLED_BACK_BTN',
2929
PUSH_FOCUSED_KEYBOARD_SCREEN: 'PUSH_FOCUSED_KEYBOARD_SCREEN',
3030
MODAL_FOCUSED_KEYBOARD_SCREEN: 'MODAL_FOCUSED_KEYBOARD_SCREEN',
31+
PUSH_KEYBOARD_SCREEN_STICKY_FOOTER: 'PUSH_KEYBOARD_SCREEN_STICKY_FOOTER',
3132
PAGE_SHEET_MODAL_BTN: 'SHOW_PAGE_SHEET_MODAL_BUTTON',
3233
DISMISS_MODAL_BTN: 'DISMISS_MODAL_BUTTON',
3334
DISMISS_REACT_MODAL_BTN: 'DISMISS_REACT_MODAL_BUTTON',

0 commit comments

Comments
 (0)