Skip to content

Commit

Permalink
Android: Implement cancelable option for Alerts
Browse files Browse the repository at this point in the history
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 #8652

Differential Revision: D3690093

fbshipit-source-id: 4cf6cfc56f464b37ce88451acf33413393454721
  • Loading branch information
kraffslol authored and Facebook Github Bot 8 committed Aug 9, 2016
1 parent 44b3cfe commit 8e2906a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 1 deletion.
15 changes: 15 additions & 0 deletions Examples/UIExplorer/js/AlertExample.js
Expand Up @@ -106,6 +106,21 @@ class SimpleAlertExampleBlock extends React.Component {
<Text>Alert with too many buttons</Text>
</View>
</TouchableHighlight>
<TouchableHighlight style={styles.wrapper}
onPress={() => Alert.alert(
'Alert Title',
null,
[
{text: 'OK', onPress: () => console.log('OK Pressed!')},
],
{
cancelable: false
}
)}>
<View style={styles.button}>
<Text>Alert that cannot be dismissed</Text>
</View>
</TouchableHighlight>
</View>
);
}
Expand Down
12 changes: 11 additions & 1 deletion Libraries/Utilities/Alert.js
Expand Up @@ -23,6 +23,10 @@ type Buttons = Array<{
style?: AlertButtonStyle;
}>;

type Options = {
cancelable?: ?boolean;
};

/**
* Launches an alert dialog with the specified title and message.
*
Expand Down Expand Up @@ -67,6 +71,7 @@ class Alert {
title: ?string,
message?: ?string,
buttons?: Buttons,
options?: Options,
type?: AlertType,
): void {
if (Platform.OS === 'ios') {
Expand All @@ -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);
}
}
}
Expand All @@ -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'}];
Expand Down
Expand Up @@ -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<String, Object> CONSTANTS = MapBuilder.<String, Object>of(
ACTION_BUTTON_CLICKED, ACTION_BUTTON_CLICKED,
Expand Down Expand Up @@ -129,13 +130,19 @@ public void showNewAlert(boolean isInForeground, Bundle arguments, Callback acti
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;
}
} 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;
Expand Down Expand Up @@ -241,6 +248,9 @@ public void showAlert(
}
args.putCharSequenceArray(AlertFragment.ARG_ITEMS, itemsArray);
}
if (options.hasKey(KEY_CANCELABLE)) {
args.putBoolean(KEY_CANCELABLE, options.getBoolean(KEY_CANCELABLE));
}

fragmentManagerHelper.showNewAlert(mIsInForeground, args, actionCallback);
}
Expand Down
Expand Up @@ -90,11 +90,13 @@ public void testAllOptions() {
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());
Expand Down

0 comments on commit 8e2906a

Please sign in to comment.