BottomSheetDialog handles setCancelable
BottomSheetDialog now properly reacts to the standard Dialog API
setCancelable and setCanceledOnTouchOutside.
Bug: 29628657
Change-Id: I55dee42c4552630c678075150d36bf5671c0eea5
diff --git a/design/src/android/support/design/widget/BottomSheetDialog.java b/design/src/android/support/design/widget/BottomSheetDialog.java
index 55b5cbe..0473e54 100644
--- a/design/src/android/support/design/widget/BottomSheetDialog.java
+++ b/design/src/android/support/design/widget/BottomSheetDialog.java
@@ -17,6 +17,7 @@
package android.support.design.widget;
import android.content.Context;
+import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
@@ -35,6 +36,12 @@
*/
public class BottomSheetDialog extends AppCompatDialog {
+ private BottomSheetBehavior<FrameLayout> mBehavior;
+
+ private boolean mCancelable = true;
+ private boolean mCanceledOnTouchOutside = true;
+ private boolean mCanceledOnTouchOutsideSet;
+
public BottomSheetDialog(@NonNull Context context) {
this(context, 0);
}
@@ -50,6 +57,7 @@
OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
+ mCancelable = cancelable;
}
@Override
@@ -74,6 +82,27 @@
super.setContentView(wrapInBottomSheet(0, view, params));
}
+ @Override
+ public void setCancelable(boolean cancelable) {
+ super.setCancelable(cancelable);
+ if (mCancelable != cancelable) {
+ mCancelable = cancelable;
+ if (mBehavior != null) {
+ mBehavior.setHideable(cancelable);
+ }
+ }
+ }
+
+ @Override
+ public void setCanceledOnTouchOutside(boolean cancel) {
+ super.setCanceledOnTouchOutside(cancel);
+ if (cancel && !mCancelable) {
+ mCancelable = true;
+ }
+ mCanceledOnTouchOutside = cancel;
+ mCanceledOnTouchOutsideSet = true;
+ }
+
private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
R.layout.design_bottom_sheet_dialog, null);
@@ -81,38 +110,39 @@
view = getLayoutInflater().inflate(layoutResId, coordinator, false);
}
FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
- BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback);
+ mBehavior = BottomSheetBehavior.from(bottomSheet);
+ mBehavior.setBottomSheetCallback(mBottomSheetCallback);
+ mBehavior.setHideable(mCancelable);
if (params == null) {
bottomSheet.addView(view);
} else {
bottomSheet.addView(view, params);
}
// We treat the CoordinatorLayout as outside the dialog though it is technically inside
- if (shouldWindowCloseOnTouchOutside()) {
- coordinator.findViewById(R.id.touch_outside).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isShowing()) {
- cancel();
- }
- }
- });
- }
+ coordinator.findViewById(R.id.touch_outside).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mCancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
+ cancel();
+ }
+ }
+ });
return coordinator;
}
private boolean shouldWindowCloseOnTouchOutside() {
- if (Build.VERSION.SDK_INT < 11) {
- return true;
+ if (!mCanceledOnTouchOutsideSet) {
+ if (Build.VERSION.SDK_INT < 11) {
+ mCanceledOnTouchOutside = true;
+ } else {
+ TypedArray a = getContext().obtainStyledAttributes(
+ new int[]{android.R.attr.windowCloseOnTouchOutside});
+ mCanceledOnTouchOutside = a.getBoolean(0, true);
+ a.recycle();
+ }
+ mCanceledOnTouchOutsideSet = true;
}
- TypedValue value = new TypedValue();
- //noinspection SimplifiableIfStatement
- if (getContext().getTheme()
- .resolveAttribute(android.R.attr.windowCloseOnTouchOutside, value, true)) {
- return value.data != 0;
- }
- return false;
+ return mCanceledOnTouchOutside;
}
private static int getThemeResId(Context context, int themeId) {
diff --git a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
index 54a71f0..4223b44 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
@@ -21,8 +21,6 @@
import android.content.Context;
import android.support.design.test.R;
-import android.support.design.widget.BottomSheetBehavior;
-import android.support.design.widget.BottomSheetDialog;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.UiController;
@@ -54,20 +52,14 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- Context context = mActivityTestRule.getActivity();
- mDialog = new BottomSheetDialog(context);
- AppCompatTextView text = new AppCompatTextView(context);
- StringBuilder builder = new StringBuilder();
- builder.append("It is fine today. ");
- text.setText(builder);
- mDialog.setContentView(text);
- mDialog.show();
+ showDialog();
// Confirms that the dialog is shown
assertThat(mDialog.isShowing(), is(true));
FrameLayout bottomSheet = (FrameLayout) mDialog
.findViewById(R.id.design_bottom_sheet);
assertThat(bottomSheet, is(notNullValue()));
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+ assertThat(behavior.isHideable(), is(true));
assertThat(behavior, is(notNullValue()));
// Modal bottom sheets have auto peek height by default.
assertThat(behavior.getPeekHeight(), is(BottomSheetBehavior.PEEK_HEIGHT_AUTO));
@@ -91,14 +83,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- Context context = mActivityTestRule.getActivity();
- mDialog = new BottomSheetDialog(context);
- AppCompatTextView text = new AppCompatTextView(context);
- StringBuilder builder = new StringBuilder();
- builder.append("It is fine today. ");
- text.setText(builder);
- mDialog.setContentView(text);
- mDialog.show();
+ showDialog();
}
});
// This ensures that the views are laid out before assertions below
@@ -124,6 +109,43 @@
});
}
+ @Test
+ public void testNonCancelableDialog() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ showDialog();
+ mDialog.setCancelable(false);
+ }
+ });
+ // Click outside the bottom sheet
+ Espresso.onView(ViewMatchers.withId(R.id.touch_outside))
+ .perform(ViewActions.click());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ FrameLayout bottomSheet = (FrameLayout) mDialog
+ .findViewById(R.id.design_bottom_sheet);
+ BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+ assertThat(behavior.isHideable(), is(false));
+ assertThat(mDialog.isShowing(), is(true));
+ mDialog.cancel();
+ assertThat(mDialog.isShowing(), is(false));
+ }
+ });
+ }
+
+ private void showDialog() {
+ Context context = mActivityTestRule.getActivity();
+ mDialog = new BottomSheetDialog(context);
+ AppCompatTextView text = new AppCompatTextView(context);
+ StringBuilder builder = new StringBuilder();
+ builder.append("It is fine today. ");
+ text.setText(builder);
+ mDialog.setContentView(text);
+ mDialog.show();
+ }
+
private static ViewAction setTallPeekHeight() {
return new ViewAction() {
@Override