From 8e2906ae89660c977e66f2f8fae883d70acac9bf Mon Sep 17 00:00:00 2001 From: Rickard Ekman Date: Tue, 9 Aug 2016 06:08:44 -0700 Subject: [PATCH] Android: Implement cancelable option for Alerts Summary: **Motivation** In iOS you cannot dismiss alerts by clicking outside of their box, while on Android you can. This can create some inconsistency if you want to have identical behavior on both platforms. This change makes it possible for Android apps to have irremovable/required alert boxes just like in iOS. This adds an additional parameter to the Alert method. The way to use it is by providing an object with the cancelable property. The cancelable property accepts a boolean value. This utilizes the Android DialogFragment method [setCancelable](https://developer.android.com/reference/android/app/DialogFragment.html#setCancelable(boolean)) **Usage example** ```js Alert.alert( 'Alert Title', null, [ {text: 'OK', onPress: () => console.log('OK Pressed!')}, ], { cancelable: false } ); ``` **Test plan (required)** I added an additional alert to the UIExplorer project where it can be tested. I also added a part in the Dialog Module test to make sure setting canc Closes https://github.com/facebook/react-native/pull/8652 Differential Revision: D3690093 fbshipit-source-id: 4cf6cfc56f464b37ce88451acf33413393454721 --- Examples/UIExplorer/js/AlertExample.js | 15 +++++++++++++++ Libraries/Utilities/Alert.js | 12 +++++++++++- .../react/modules/dialog/DialogModule.java | 10 ++++++++++ .../react/modules/dialog/DialogModuleTest.java | 2 ++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Examples/UIExplorer/js/AlertExample.js b/Examples/UIExplorer/js/AlertExample.js index f8d6fe0ba..553bb0f36 100644 --- a/Examples/UIExplorer/js/AlertExample.js +++ b/Examples/UIExplorer/js/AlertExample.js @@ -106,6 +106,21 @@ class SimpleAlertExampleBlock extends React.Component { Alert with too many buttons + Alert.alert( + 'Alert Title', + null, + [ + {text: 'OK', onPress: () => console.log('OK Pressed!')}, + ], + { + cancelable: false + } + )}> + + Alert that cannot be dismissed + + ); } diff --git a/Libraries/Utilities/Alert.js b/Libraries/Utilities/Alert.js index 21b18adfa..9a38096ad 100644 --- a/Libraries/Utilities/Alert.js +++ b/Libraries/Utilities/Alert.js @@ -23,6 +23,10 @@ type Buttons = Array<{ style?: AlertButtonStyle; }>; +type Options = { + cancelable?: ?boolean; +}; + /** * Launches an alert dialog with the specified title and message. * @@ -67,6 +71,7 @@ class Alert { title: ?string, message?: ?string, buttons?: Buttons, + options?: Options, type?: AlertType, ): void { if (Platform.OS === 'ios') { @@ -77,7 +82,7 @@ class Alert { } AlertIOS.alert(title, message, buttons); } else if (Platform.OS === 'android') { - AlertAndroid.alert(title, message, buttons); + AlertAndroid.alert(title, message, buttons, options); } } } @@ -91,11 +96,16 @@ class AlertAndroid { title: ?string, message?: ?string, buttons?: Buttons, + options?: Options, ): void { var config = { title: title || '', message: message || '', }; + + if (options) { + config = {...config, cancelable: options.cancelable}; + } // At most three buttons (neutral, negative, positive). Ignore rest. // The text 'OK' should be probably localized. iOS Alert does that in native. var validButtons: Buttons = buttons ? buttons.slice(0, 3) : [{text: 'OK'}]; diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java index 4ed361ff6..a9bbedc14 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java @@ -46,6 +46,7 @@ public class DialogModule extends ReactContextBaseJavaModule implements Lifecycl /* package */ static final String KEY_BUTTON_NEGATIVE = "buttonNegative"; /* package */ static final String KEY_BUTTON_NEUTRAL = "buttonNeutral"; /* package */ static final String KEY_ITEMS = "items"; + /* package */ static final String KEY_CANCELABLE = "cancelable"; /* package */ static final Map CONSTANTS = MapBuilder.of( ACTION_BUTTON_CLICKED, ACTION_BUTTON_CLICKED, @@ -129,6 +130,9 @@ public class DialogModule extends ReactContextBaseJavaModule implements Lifecycl if (isUsingSupportLibrary()) { SupportAlertFragment alertFragment = new SupportAlertFragment(actionListener, arguments); if (isInForeground) { + if (arguments.containsKey(KEY_CANCELABLE)) { + alertFragment.setCancelable(arguments.getBoolean(KEY_CANCELABLE)); + } alertFragment.show(mSupportFragmentManager, FRAGMENT_TAG); } else { mFragmentToShow = alertFragment; @@ -136,6 +140,9 @@ public class DialogModule extends ReactContextBaseJavaModule implements Lifecycl } else { AlertFragment alertFragment = new AlertFragment(actionListener, arguments); if (isInForeground) { + if (arguments.containsKey(KEY_CANCELABLE)) { + alertFragment.setCancelable(arguments.getBoolean(KEY_CANCELABLE)); + } alertFragment.show(mFragmentManager, FRAGMENT_TAG); } else { mFragmentToShow = alertFragment; @@ -241,6 +248,9 @@ public class DialogModule extends ReactContextBaseJavaModule implements Lifecycl } args.putCharSequenceArray(AlertFragment.ARG_ITEMS, itemsArray); } + if (options.hasKey(KEY_CANCELABLE)) { + args.putBoolean(KEY_CANCELABLE, options.getBoolean(KEY_CANCELABLE)); + } fragmentManagerHelper.showNewAlert(mIsInForeground, args, actionCallback); } diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java index 3925adb26..0d5ff98fd 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java @@ -90,11 +90,13 @@ public class DialogModuleTest { options.putString("buttonPositive", "OK"); options.putString("buttonNegative", "Cancel"); options.putString("buttonNeutral", "Later"); + options.putBoolean("cancelable", false); mDialogModule.showAlert(options, null, null); final AlertFragment fragment = getFragment(); assertNotNull("Fragment was not displayed", fragment); + assertEquals(false, fragment.isCancelable()); final AlertDialog dialog = (AlertDialog) fragment.getDialog(); assertEquals("OK", dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText().toString());