Browse Source

Android back handler (#2282)

* Reorder dependencies

Keep vs code happy

* Make markdown files readable on MacDown

* BackHandler compatibility

Give precedence to react's BackHandler when hardware back button is pressed

* f

* Update test

* Restore deps
Guy Carmeli 6 years ago
parent
commit
5971384ab3
No account linked to committer's email address

+ 22
- 0
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/BackHandlerTest.java View File

1
+package com.reactnativenavigation.e2e.androide2e;
2
+
3
+import android.support.test.uiautomator.By;
4
+
5
+import org.junit.Test;
6
+
7
+public class BackHandlerTest extends BaseTest {
8
+    @Test
9
+    public void overrideHardwareBackButton() throws Exception {
10
+        elementByText("BACK HANDLER").click();
11
+        assertExists(By.text("Back Handler Screen"));
12
+
13
+        elementByText("ADD BACK HANDLER").click();
14
+        device().pressBack();
15
+        assertExists(By.text("Back Handler Screen"));
16
+
17
+
18
+        elementByText("REMOVE BACK HANDLER").click();
19
+        device().pressBack();
20
+        assertMainShown();
21
+    }
22
+}

+ 2
- 2
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/BaseTest.java View File

24
 
24
 
25
 @RunWith(AndroidJUnit4.class)
25
 @RunWith(AndroidJUnit4.class)
26
 public abstract class BaseTest {
26
 public abstract class BaseTest {
27
-	public static final String PACKAGE_NAME = "com.reactnativenavigation.playground";
28
-	public static final long TIMEOUT = 60000;
27
+	static final String PACKAGE_NAME = "com.reactnativenavigation.playground";
28
+	private static final long TIMEOUT = 60000;
29
 
29
 
30
 	@Before
30
 	@Before
31
 	public void beforeEach() throws Exception {
31
 	public void beforeEach() throws Exception {

+ 8
- 0
docs/docs/usage.md View File

21
 
21
 
22
 ### registerContainer(screenID, generator)
22
 ### registerContainer(screenID, generator)
23
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
23
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
24
+
24
 ```js
25
 ```js
25
 Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
26
 Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
26
 ```
27
 ```
27
 
28
 
28
 ### setRoot({params})
29
 ### setRoot({params})
29
 Start a Single page app with two side menus:
30
 Start a Single page app with two side menus:
31
+
30
 ```js
32
 ```js
31
 Navigation.setRoot({
33
 Navigation.setRoot({
32
   container: {
34
   container: {
53
 });
55
 });
54
 ```
56
 ```
55
 Start a tab based app:
57
 Start a tab based app:
58
+
56
 ```js
59
 ```js
57
 Navigation.setRoot({
60
 Navigation.setRoot({
58
   bottomTabs: [
61
   bottomTabs: [
80
 
83
 
81
 ### push(params)
84
 ### push(params)
82
 Push a new screen into this screen's navigation stack.
85
 Push a new screen into this screen's navigation stack.
86
+
83
 ```js
87
 ```js
84
 Navigation.push(this.props.containerId, {
88
 Navigation.push(this.props.containerId, {
85
   name: 'navigation.playground.PushedScreen',
89
   name: 'navigation.playground.PushedScreen',
88
 ```
92
 ```
89
 ### pop(containerId)
93
 ### pop(containerId)
90
 Pop the top screen from this screen's navigation stack.
94
 Pop the top screen from this screen's navigation stack.
95
+
91
 ```js
96
 ```js
92
 Navigation.pop(this.props.containerId);
97
 Navigation.pop(this.props.containerId);
93
 ```
98
 ```
97
 ```
102
 ```
98
 ### popToRoot()
103
 ### popToRoot()
99
 Pop all the screens until the root from this screen's navigation stack
104
 Pop all the screens until the root from this screen's navigation stack
105
+
100
 ```js
106
 ```js
101
 Navigation.popToRoot(this.props.containerId);
107
 Navigation.popToRoot(this.props.containerId);
102
 ```
108
 ```
103
 ### showModal(params = {})
109
 ### showModal(params = {})
104
 Show a screen as a modal.
110
 Show a screen as a modal.
111
+
105
 ```js
112
 ```js
106
 Navigation.showModal({
113
 Navigation.showModal({
107
   container: {
114
   container: {
114
 ```
121
 ```
115
 ### dismissModal(containerId)
122
 ### dismissModal(containerId)
116
 Dismiss modal.
123
 Dismiss modal.
124
+
117
 ```js
125
 ```js
118
 Navigation.dismissModal(this.props.containerId);
126
 Navigation.dismissModal(this.props.containerId);
119
 ```
127
 ```

+ 10
- 0
docs/docs/v1tov2diff.md View File

188
 
188
 
189
 #### registerContainer(screenID, generator)
189
 #### registerContainer(screenID, generator)
190
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
190
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
191
+
191
 ```js
192
 ```js
192
 Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
193
 Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
193
 ```
194
 ```
194
 
195
 
195
 #### setRoot({params})
196
 #### setRoot({params})
196
 Start a Single page app with two side menus:
197
 Start a Single page app with two side menus:
198
+
197
 ```js
199
 ```js
198
 Navigation.setRoot({
200
 Navigation.setRoot({
199
       container: {
201
       container: {
220
     });
222
     });
221
 ```
223
 ```
222
 Start a tab based app:
224
 Start a tab based app:
225
+
223
 ```js
226
 ```js
224
 Navigation.setRoot({
227
 Navigation.setRoot({
225
       tabs: [
228
       tabs: [
247
 
250
 
248
 #### push(params)
251
 #### push(params)
249
 Push a new screen into this screen's navigation stack.
252
 Push a new screen into this screen's navigation stack.
253
+
250
 ```js
254
 ```js
251
 Navigation.push(this.props.containerId, {
255
 Navigation.push(this.props.containerId, {
252
       name: 'navigation.playground.PushedScreen',
256
       name: 'navigation.playground.PushedScreen',
255
 ```
259
 ```
256
 #### pop(containerId)
260
 #### pop(containerId)
257
 Pop the top screen from this screen's navigation stack.
261
 Pop the top screen from this screen's navigation stack.
262
+
258
 ```js
263
 ```js
259
 Navigation.pop(this.props.containerId);
264
 Navigation.pop(this.props.containerId);
260
 ```
265
 ```
261
 #### popTo(params)
266
 #### popTo(params)
267
+
262
 ```js
268
 ```js
263
 Navigation.popTo(this.props.containerId, this.props.previousScreenIds[0]);
269
 Navigation.popTo(this.props.containerId, this.props.previousScreenIds[0]);
264
 ```
270
 ```
265
 #### popToRoot()
271
 #### popToRoot()
266
 Pop all the screens until the root from this screen's navigation stack
272
 Pop all the screens until the root from this screen's navigation stack
273
+
267
 ```js
274
 ```js
268
 Navigation.popToRoot(this.props.containerId);
275
 Navigation.popToRoot(this.props.containerId);
269
 ```
276
 ```
270
 #### showModal(params = {})
277
 #### showModal(params = {})
271
 Show a screen as a modal.
278
 Show a screen as a modal.
279
+
272
 ```js
280
 ```js
273
 Navigation.showModal({
281
 Navigation.showModal({
274
       container: {
282
       container: {
281
 ```
289
 ```
282
 #### dismissModal(containerId)
290
 #### dismissModal(containerId)
283
 Dismiss modal.
291
 Dismiss modal.
292
+
284
 ```js
293
 ```js
285
 Navigation.dismissModal(this.props.containerId);
294
 Navigation.dismissModal(this.props.containerId);
286
 ```
295
 ```
287
 #### dismissAllModals()
296
 #### dismissAllModals()
288
 Dismiss all the current modals at the same time.
297
 Dismiss all the current modals at the same time.
298
+
289
 ```js
299
 ```js
290
 Navigation.dismissAllModals();
300
 Navigation.dismissAllModals();
291
 ```
301
 ```

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java View File

41
 
41
 
42
 	@Override
42
 	@Override
43
 	public void invokeDefaultOnBackPressed() {
43
 	public void invokeDefaultOnBackPressed() {
44
-		onBackPressed();
44
+        if (!navigator.handleBack()) {
45
+		    super.onBackPressed();
46
+        }
45
 	}
47
 	}
46
 
48
 
47
 	@Override
49
 	@Override
48
 	public void onBackPressed() {
50
 	public void onBackPressed() {
49
-		if (!navigator.handleBack()) {
50
-			super.onBackPressed();
51
-		}
51
+        app().getReactGateway().onBackPressed();
52
 	}
52
 	}
53
 
53
 
54
 	@Override
54
 	@Override

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactGateway.java View File

46
 	public boolean onKeyUp(final int keyCode) {
46
 	public boolean onKeyUp(final int keyCode) {
47
 		return jsDevReloadHandler.onKeyUp(keyCode);
47
 		return jsDevReloadHandler.onKeyUp(keyCode);
48
 	}
48
 	}
49
+
50
+    public void onBackPressed() {
51
+	    reactNativeHost.getReactInstanceManager().onBackPressed();
52
+    }
49
 }
53
 }

+ 75
- 0
playground/src/containers/BackHandlerScreen.js View File

1
+const React = require('react');
2
+const { Component } = require('react');
3
+
4
+const { View, Text, Button, BackHandler } = require('react-native');
5
+
6
+class BackHandlerScreen extends Component {
7
+  static get navigationOptions() {
8
+    return {
9
+      topBar: {
10
+        title: 'Back Handler',
11
+        textColor: 'black',
12
+        textFontSize: 16
13
+      }
14
+    };
15
+  }
16
+
17
+  constructor(props) {
18
+    super(props);
19
+    this.addBackHandler = this.addBackHandler.bind(this);
20
+    this.removeBackHandler = this.removeBackHandler.bind(this);
21
+    this.backHandler = () => {
22
+      this.setState({
23
+        backPress: 'Back button pressed!'
24
+      });
25
+      return true;
26
+    };
27
+    this.state = {
28
+      backPress: ''
29
+    };
30
+  }
31
+
32
+  render() {
33
+    return (
34
+      <View style={styles.root}>
35
+        <Text style={styles.h1}>{`Back Handler Screen`}</Text>
36
+        <Text style={styles.h2}>{this.state.backPress}</Text>
37
+        <Button title="add back handler" onPress={this.addBackHandler} />
38
+        <Button title="remove back handler" onPress={this.removeBackHandler} />
39
+      </View>
40
+    );
41
+  }
42
+
43
+  addBackHandler() {
44
+    BackHandler.addEventListener('hardwareBackPress', this.backHandler);
45
+  }
46
+
47
+  removeBackHandler() {
48
+    BackHandler.removeEventListener('hardwareBackPress', this.backHandler);
49
+  }
50
+
51
+  componentWillUnmount() {
52
+    BackHandler.removeEventListener('hardwareBackPress', this.backHandler);
53
+  }
54
+}
55
+
56
+const styles = {
57
+  root: {
58
+    flex: 1,
59
+    backgroundColor: 'white',
60
+    justifyContent: 'center',
61
+    alignItems: 'center'
62
+  },
63
+  h2: {
64
+    fontSize: 12,
65
+    textAlign: 'center',
66
+    margin: 10
67
+  },
68
+  footer: {
69
+    fontSize: 10,
70
+    color: '#888',
71
+    marginTop: 10
72
+  }
73
+};
74
+
75
+module.exports = BackHandlerScreen;

+ 8
- 0
playground/src/containers/WelcomeScreen.js View File

12
     this.onClickLifecycleScreen = this.onClickLifecycleScreen.bind(this);
12
     this.onClickLifecycleScreen = this.onClickLifecycleScreen.bind(this);
13
     this.onClickPushOptionsScreen = this.onClickPushOptionsScreen.bind(this);
13
     this.onClickPushOptionsScreen = this.onClickPushOptionsScreen.bind(this);
14
     this.onClickPushOrientationMenuScreen = this.onClickPushOrientationMenuScreen.bind(this);
14
     this.onClickPushOrientationMenuScreen = this.onClickPushOrientationMenuScreen.bind(this);
15
+    this.onClickBackHandler = this.onClickBackHandler.bind(this);
15
   }
16
   }
16
 
17
 
17
   render() {
18
   render() {
23
         <Button title="Push Lifecycle Screen" onPress={this.onClickLifecycleScreen} />
24
         <Button title="Push Lifecycle Screen" onPress={this.onClickLifecycleScreen} />
24
         <Button title="Push" onPress={this.onClickPush} />
25
         <Button title="Push" onPress={this.onClickPush} />
25
         <Button title="Push Options Screen" onPress={this.onClickPushOptionsScreen} />
26
         <Button title="Push Options Screen" onPress={this.onClickPushOptionsScreen} />
27
+        <Button title="Back Handler" onPress={this.onClickBackHandler} />
26
         <Button title="Show Modal" onPress={this.onClickShowModal} />
28
         <Button title="Show Modal" onPress={this.onClickShowModal} />
27
         <Button title="Show Redbox" onPress={this.onClickShowRedbox} />
29
         <Button title="Show Redbox" onPress={this.onClickShowRedbox} />
28
         <Button title="Orientation" onPress={this.onClickPushOrientationMenuScreen} />
30
         <Button title="Orientation" onPress={this.onClickPushOrientationMenuScreen} />
134
     });
136
     });
135
   }
137
   }
136
 
138
 
139
+  onClickBackHandler() {
140
+    Navigation.push(this.props.containerId, {
141
+      name: 'navigation.playground.BackHandlerScreen'
142
+    });
143
+  }
144
+
137
   onClickPushOrientationMenuScreen() {
145
   onClickPushOrientationMenuScreen() {
138
     Navigation.push(this.props.containerId, {
146
     Navigation.push(this.props.containerId, {
139
       name: 'navigation.playground.OrientationSelectScreen'
147
       name: 'navigation.playground.OrientationSelectScreen'

+ 2
- 0
playground/src/containers/index.js View File

9
 const OrientationDetectScreen = require('./OrientationDetectScreen');
9
 const OrientationDetectScreen = require('./OrientationDetectScreen');
10
 const ScrollViewScreen = require('./ScrollViewScreen');
10
 const ScrollViewScreen = require('./ScrollViewScreen');
11
 const CustomDialog = require('./CustomDialog');
11
 const CustomDialog = require('./CustomDialog');
12
+const BandHandlerScreen = require('./BackHandlerScreen');
12
 
13
 
13
 function registerContainers() {
14
 function registerContainers() {
14
   Navigation.registerContainer(`navigation.playground.ScrollViewScreen`, () => ScrollViewScreen);
15
   Navigation.registerContainer(`navigation.playground.ScrollViewScreen`, () => ScrollViewScreen);
21
   Navigation.registerContainer(`navigation.playground.OrientationSelectScreen`, () => OrientationSelectScreen);
22
   Navigation.registerContainer(`navigation.playground.OrientationSelectScreen`, () => OrientationSelectScreen);
22
   Navigation.registerContainer(`navigation.playground.OrientationDetectScreen`, () => OrientationDetectScreen);
23
   Navigation.registerContainer(`navigation.playground.OrientationDetectScreen`, () => OrientationDetectScreen);
23
   Navigation.registerContainer('navigation.playground.CustomDialog', () => CustomDialog);
24
   Navigation.registerContainer('navigation.playground.CustomDialog', () => CustomDialog);
25
+  Navigation.registerContainer('navigation.playground.BackHandlerScreen', () => BandHandlerScreen);
24
 }
26
 }
25
 
27
 
26
 module.exports = {
28
 module.exports = {