Move BackAndroid -> BackHandler, add Apple TV support for back nav
Summary: Enable back navigation on Apple TV (with the remote's menu button) in code making use of BackAndroid. The module is renamed to BackHandler. BackAndroid is still exported to ReactNative for now, until external projects switch to using the new name for the module. The navigation in https://github.com/react-community/react-navigation makes use of this module. **Test plan**: Manual testing with an example app (https://github.com/dlowder-salesforce/react-nav-example). Closes https://github.com/facebook/react-native/pull/12571 Differential Revision: D4665152 Pulled By: ericvicenti fbshipit-source-id: 925400ce216379267e014457be6f5eedbe4453ec
This commit is contained in:
Родитель
9325496d46
Коммит
b7e9374c64
|
@ -25,7 +25,7 @@
|
|||
|
||||
const AppRegistry = require('AppRegistry');
|
||||
const AsyncStorage = require('AsyncStorage');
|
||||
const BackAndroid = require('BackAndroid');
|
||||
const BackHandler = require('BackHandler');
|
||||
const Dimensions = require('Dimensions');
|
||||
const DrawerLayoutAndroid = require('DrawerLayoutAndroid');
|
||||
const Linking = require('Linking');
|
||||
|
@ -73,7 +73,7 @@ class UIExplorerApp extends React.Component {
|
|||
state: UIExplorerNavigationState;
|
||||
|
||||
componentWillMount() {
|
||||
BackAndroid.addEventListener('hardwareBackPress', this._handleBackButtonPress);
|
||||
BackHandler.addEventListener('hardwareBackPress', this._handleBackButtonPress);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
'use strict';
|
||||
|
||||
const AsyncStorage = require('AsyncStorage');
|
||||
const BackHandler = require('BackHandler');
|
||||
const Linking = require('Linking');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
@ -68,6 +69,10 @@ class UIExplorerApp extends React.Component {
|
|||
props: Props;
|
||||
state: UIExplorerNavigationState;
|
||||
|
||||
componentWillMount() {
|
||||
BackHandler.addEventListener('hardwareBackPress', this._handleBack);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => {
|
||||
|
|
|
@ -678,6 +678,7 @@ const ScrollView = React.createClass({
|
|||
}
|
||||
|
||||
const refreshControl = this.props.refreshControl;
|
||||
|
||||
if (refreshControl) {
|
||||
if (Platform.OS === 'ios') {
|
||||
// On iOS the RefreshControl is a child of the ScrollView.
|
||||
|
|
|
@ -18,8 +18,8 @@ var ReactNative = require('ReactNative');
|
|||
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
|
||||
// require BackAndroid so it sets the default handler that exits the app if no listeners respond
|
||||
require('BackAndroid');
|
||||
// require BackHandler so it sets the default handler that exits the app if no listeners respond
|
||||
require('BackHandler');
|
||||
|
||||
function renderApplication<Props: Object>(
|
||||
RootComponent: ReactClass<Props>,
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* iOS stub for BackAndroid.android.js
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function emptyFunction() {}
|
||||
|
||||
const BackAndroid = {
|
||||
exitApp: emptyFunction,
|
||||
addEventListener() {
|
||||
return {
|
||||
remove: emptyFunction,
|
||||
};
|
||||
},
|
||||
removeEventListener: emptyFunction,
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* BackAndroid has been moved to BackHandler. This stub calls BackHandler methods
|
||||
* after generating a warning to remind users to move to the new BackHandler module.
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var BackHandler = require('BackHandler');
|
||||
|
||||
var warning = require('fbjs/lib/warning');
|
||||
|
||||
/**
|
||||
* Deprecated. Use BackHandler instead.
|
||||
*/
|
||||
var BackAndroid = {
|
||||
|
||||
exitApp: function() {
|
||||
warning(false, 'BackAndroid is deprecated. Please use BackHandler instead.');
|
||||
BackHandler.exitApp();
|
||||
},
|
||||
|
||||
addEventListener: function (
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): {remove: () => void} {
|
||||
warning(false, 'BackAndroid is deprecated. Please use BackHandler instead.');
|
||||
return BackHandler.addEventListener(eventName, handler);
|
||||
},
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
warning(false, 'BackAndroid is deprecated. Please use BackHandler instead.');
|
||||
BackHandler.removeEventListener(eventName, handler);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
|
@ -6,7 +6,7 @@
|
|||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
* @providesModule BackHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
@ -34,20 +34,29 @@ RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
|
|||
}
|
||||
|
||||
if (invokeDefault) {
|
||||
BackAndroid.exitApp();
|
||||
BackHandler.exitApp();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Detect hardware back button presses, and programmatically invoke the default back button
|
||||
* Detect hardware button presses for back navigation.
|
||||
*
|
||||
* Android: Detect hardware back button presses, and programmatically invoke the default back button
|
||||
* functionality to exit the app if there are no listeners or if none of the listeners return true.
|
||||
*
|
||||
* tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented:
|
||||
* programmatically disable menu button handling
|
||||
* functionality to exit the app if there are no listeners or if none of the listeners return true.)
|
||||
*
|
||||
* iOS: Not applicable.
|
||||
*
|
||||
* The event subscriptions are called in reverse order (i.e. last registered subscription first),
|
||||
* and if one subscription returns true then subscriptions registered earlier will not be called.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* BackAndroid.addEventListener('hardwareBackPress', function() {
|
||||
* BackHandler.addEventListener('hardwareBackPress', function() {
|
||||
* // this.onMainScreen and this.goBack are just examples, you need to use your own implementation here
|
||||
* // Typically you would use the navigator here to go to the last state.
|
||||
*
|
||||
|
@ -59,7 +68,7 @@ RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
|
|||
* });
|
||||
* ```
|
||||
*/
|
||||
var BackAndroid = {
|
||||
var BackHandler = {
|
||||
|
||||
exitApp: function() {
|
||||
DeviceEventManager.invokeDefaultBackPressHandler();
|
||||
|
@ -71,7 +80,7 @@ var BackAndroid = {
|
|||
): {remove: () => void} {
|
||||
_backPressSubscriptions.add(handler);
|
||||
return {
|
||||
remove: () => BackAndroid.removeEventListener(eventName, handler),
|
||||
remove: () => BackHandler.removeEventListener(eventName, handler),
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -84,4 +93,4 @@ var BackAndroid = {
|
|||
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
||||
module.exports = BackHandler;
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* On Apple TV, this implements back navigation using the TV remote's menu button.
|
||||
* On iOS, this just implements a stub.
|
||||
*
|
||||
* @providesModule BackHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const TVEventHandler = require('TVEventHandler');
|
||||
|
||||
type BackPressEventName = $Enum<{
|
||||
backPress: string,
|
||||
}>;
|
||||
|
||||
function emptyFunction() {}
|
||||
|
||||
/**
|
||||
* Detect hardware button presses for back navigation.
|
||||
*
|
||||
* Android: Detect hardware back button presses, and programmatically invoke the default back button
|
||||
* functionality to exit the app if there are no listeners or if none of the listeners return true.
|
||||
*
|
||||
* tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented:
|
||||
* programmatically disable menu button handling
|
||||
* functionality to exit the app if there are no listeners or if none of the listeners return true.)
|
||||
*
|
||||
* iOS: Not applicable.
|
||||
*
|
||||
* The event subscriptions are called in reverse order (i.e. last registered subscription first),
|
||||
* and if one subscription returns true then subscriptions registered earlier will not be called.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* BackHandler.addEventListener('hardwareBackPress', function() {
|
||||
* // this.onMainScreen and this.goBack are just examples, you need to use your own implementation here
|
||||
* // Typically you would use the navigator here to go to the last state.
|
||||
*
|
||||
* if (!this.onMainScreen()) {
|
||||
* this.goBack();
|
||||
* return true;
|
||||
* }
|
||||
* return false;
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
let BackHandler;
|
||||
|
||||
if (Platform.isTVOS) {
|
||||
const _tvEventHandler = new TVEventHandler();
|
||||
var _backPressSubscriptions = new Set();
|
||||
|
||||
_tvEventHandler.enable(this, function(cmp, evt) {
|
||||
if (evt && evt.eventType === 'menu') {
|
||||
var backPressSubscriptions = new Set(_backPressSubscriptions);
|
||||
var invokeDefault = true;
|
||||
var subscriptions = [...backPressSubscriptions].reverse();
|
||||
for (var i = 0; i < subscriptions.length; ++i) {
|
||||
if (subscriptions[i]()) {
|
||||
invokeDefault = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invokeDefault) {
|
||||
BackHandler.exitApp();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BackHandler = {
|
||||
exitApp: emptyFunction,
|
||||
|
||||
addEventListener: function (
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): {remove: () => void} {
|
||||
_backPressSubscriptions.add(handler);
|
||||
return {
|
||||
remove: () => BackHandler.removeEventListener(eventName, handler),
|
||||
};
|
||||
},
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_backPressSubscriptions.delete(handler);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
BackHandler = {
|
||||
exitApp: emptyFunction,
|
||||
addEventListener() {
|
||||
return {
|
||||
remove: emptyFunction,
|
||||
};
|
||||
},
|
||||
removeEventListener: emptyFunction,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = BackHandler;
|
|
@ -81,7 +81,8 @@ const ReactNative = {
|
|||
get AppRegistry() { return require('AppRegistry'); },
|
||||
get AppState() { return require('AppState'); },
|
||||
get AsyncStorage() { return require('AsyncStorage'); },
|
||||
get BackAndroid() { return require('BackAndroid'); },
|
||||
get BackAndroid() { return require('BackAndroid'); }, // deprecated: use BackHandler instead
|
||||
get BackHandler() { return require('BackHandler'); },
|
||||
get CameraRoll() { return require('CameraRoll'); },
|
||||
get Clipboard() { return require('Clipboard'); },
|
||||
get DatePickerAndroid() { return require('DatePickerAndroid'); },
|
||||
|
@ -106,6 +107,7 @@ const ReactNative = {
|
|||
get StyleSheet() { return require('StyleSheet'); },
|
||||
get Systrace() { return require('Systrace'); },
|
||||
get TimePickerAndroid() { return require('TimePickerAndroid'); },
|
||||
get TVEventHandler() { return require('TVEventHandler'); },
|
||||
get UIManager() { return require('UIManager'); },
|
||||
get Vibration() { return require('Vibration'); },
|
||||
get VibrationIOS() { return require('VibrationIOS'); },
|
||||
|
|
|
@ -59,7 +59,9 @@ const apis = [
|
|||
'../Libraries/ReactNative/AppRegistry.js',
|
||||
'../Libraries/AppState/AppState.js',
|
||||
'../Libraries/Storage/AsyncStorage.js',
|
||||
'../Libraries/Utilities/BackAndroid.android.js',
|
||||
'../Libraries/Utilities/BackAndroid.js',
|
||||
'../Libraries/Utilities/BackHandler.ios.js',
|
||||
'../Libraries/Utilities/BackHandler.android.js',
|
||||
'../Libraries/CameraRoll/CameraRoll.js',
|
||||
'../Libraries/Components/Clipboard/Clipboard.js',
|
||||
'../Libraries/Components/DatePickerAndroid/DatePickerAndroid.android.js',
|
||||
|
|
Загрузка…
Ссылка в новой задаче