Skip to content

Commit

Permalink
[Adaptive] [Side Sheet] Cancel modal side sheet scrim on STATE_HIDDEN.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 493655992
(cherry picked from commit d481ddd)
  • Loading branch information
afohrman authored and dsn5ft committed Dec 9, 2022
1 parent 9c8ea58 commit 0c4e4a7
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 20 deletions.
16 changes: 15 additions & 1 deletion lib/java/com/google/android/material/sidesheet/Sheet.java
Expand Up @@ -27,7 +27,7 @@
* Interface for sheet constants and {@code IntDefs} to be shared between the different {@link
* Sheet} implementations.
*/
interface Sheet {
interface Sheet<C extends SheetCallback> {
/** The sheet is dragging. */
int STATE_DRAGGING = 1;

Expand Down Expand Up @@ -92,4 +92,18 @@ interface Sheet {

/** Sets the current state of the sheet. Must be one of {@link StableSheetState}. */
void setState(@StableSheetState int state);

/**
* Adds a callback to be notified of sheet events.
*
* @param callback The callback to notify when sheet events occur.
*/
void addCallback(C callback);

/**
* Removes a callback to be notified of sheet events.
*
* @param callback The callback to remove
*/
void removeCallback(C callback);
}
40 changes: 40 additions & 0 deletions lib/java/com/google/android/material/sidesheet/SheetCallback.java
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.android.material.sidesheet;

import android.view.View;
import androidx.annotation.NonNull;
import com.google.android.material.sidesheet.Sheet.SheetState;

interface SheetCallback {

/**
* Called when the sheet changes its state.
*
* @param sheet The sheet view.
* @param newState The new state.
*/
void onStateChanged(@NonNull View sheet, @SheetState int newState);

/**
* Called when the sheet is being dragged.
*
* @param sheet The sheet view.
* @param slideOffset The new offset of this sheet.
*/
void onSlide(@NonNull View sheet, float slideOffset);
}
13 changes: 8 additions & 5 deletions lib/java/com/google/android/material/sidesheet/SheetDialog.java
Expand Up @@ -46,12 +46,12 @@
* Base class for {@link android.app.Dialog}s styled as a sheet, to be used by sheet dialog
* implementations such as side sheets and bottom sheets.
*/
abstract class SheetDialog extends AppCompatDialog {
abstract class SheetDialog<C extends SheetCallback> extends AppCompatDialog {

private static final int COORDINATOR_LAYOUT_ID = R.id.coordinator;
private static final int TOUCH_OUTSIDE_ID = R.id.touch_outside;

@Nullable private Sheet behavior;
@Nullable private Sheet<C> behavior;
@Nullable private FrameLayout container;
@Nullable private FrameLayout sheet;

Expand Down Expand Up @@ -141,7 +141,7 @@ protected void onStart() {
*/
@Override
public void cancel() {
Sheet behavior = getBehavior();
Sheet<C> behavior = getBehavior();

if (!dismissWithAnimation || behavior.getState() == Sheet.STATE_HIDDEN) {
super.cancel();
Expand Down Expand Up @@ -184,9 +184,12 @@ private void ensureContainerAndBehavior() {
container = (FrameLayout) View.inflate(getContext(), getLayoutResId(), null);
sheet = container.findViewById(getDialogId());
behavior = getBehaviorFromSheet(sheet);
addSheetCancelOnHideCallback(behavior);
}
}

abstract void addSheetCancelOnHideCallback(Sheet<C> behavior);

@NonNull
private FrameLayout getContainer() {
if (this.container == null) {
Expand All @@ -204,7 +207,7 @@ private FrameLayout getSheet() {
}

@NonNull
Sheet getBehavior() {
Sheet<C> getBehavior() {
if (this.behavior == null) {
// The content hasn't been set, so the behavior doesn't exist yet. Let's create it.
ensureContainerAndBehavior();
Expand Down Expand Up @@ -302,7 +305,7 @@ private static int getThemeResId(
abstract int getDialogId();

@NonNull
abstract Sheet getBehaviorFromSheet(@NonNull FrameLayout sheet);
abstract Sheet<C> getBehaviorFromSheet(@NonNull FrameLayout sheet);

@StableSheetState
abstract int getStateOnStart();
Expand Down
Expand Up @@ -67,7 +67,7 @@
* side sheet.
*/
public class SideSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V>
implements Sheet {
implements Sheet<SideSheetCallback> {

private SheetDelegate sheetDelegate;

Expand Down Expand Up @@ -325,10 +325,12 @@ public boolean onLayoutChild(

nestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));

for (SideSheetCallback callback : callbacks) {
callback.onLayout(child);
for (SheetCallback callback : callbacks) {
if (callback instanceof SideSheetCallback) {
SideSheetCallback sideSheetCallback = (SideSheetCallback) callback;
sideSheetCallback.onLayout(child);
}
}

return true;
}

Expand Down Expand Up @@ -621,6 +623,7 @@ float getHideThreshold() {
*
* @param callback The callback to notify when side sheet events occur.
*/
@Override
public void addCallback(@NonNull SideSheetCallback callback) {
callbacks.add(callback);
}
Expand All @@ -630,6 +633,7 @@ public void addCallback(@NonNull SideSheetCallback callback) {
*
* @param callback The callback to remove.
*/
@Override
public void removeCallback(@NonNull SideSheetCallback callback) {
callbacks.remove(callback);
}
Expand Down Expand Up @@ -712,7 +716,7 @@ void setStateInternal(@SheetState int state) {
updateImportantForAccessibility(false);
}

for (SideSheetCallback callback : callbacks) {
for (SheetCallback callback : callbacks) {
callback.onStateChanged(sheet, state);
}

Expand Down Expand Up @@ -871,7 +875,7 @@ public int getViewHorizontalDragRange(@NonNull View child) {
private void dispatchOnSlide(@NonNull View child, int outwardEdge) {
if (!callbacks.isEmpty()) {
float slideOffset = sheetDelegate.calculateSlideOffsetBasedOnOutwardEdge(outwardEdge);
for (SideSheetCallback callback : callbacks) {
for (SheetCallback callback : callbacks) {
callback.onSlide(child, slideOffset);
}
}
Expand Down
Expand Up @@ -21,7 +21,7 @@
import com.google.android.material.sidesheet.Sheet.SheetState;

/** Callback that monitors side sheet events. */
public abstract class SideSheetCallback {
public abstract class SideSheetCallback implements SheetCallback {

/**
* Called when the sheet changes its state.
Expand All @@ -31,6 +31,7 @@ public abstract class SideSheetCallback {
* {@link SideSheetBehavior#STATE_SETTLING}, {@link SideSheetBehavior#STATE_EXPANDED} or
* {@link SideSheetBehavior#STATE_HIDDEN}.
*/
@Override
public abstract void onStateChanged(@NonNull View sheet, @SheetState int newState);

/**
Expand All @@ -41,6 +42,7 @@ public abstract class SideSheetCallback {
* sheet is moving towards the outward edge. A value of 0 means that the sheet is hidden, and
* a value of 1 means that the sheet is fully expanded.
*/
@Override
public abstract void onSlide(@NonNull View sheet, float slideOffset);

void onLayout(@NonNull View sheet) {}
Expand Down
Expand Up @@ -17,6 +17,8 @@

import com.google.android.material.R;

import static com.google.android.material.sidesheet.Sheet.STATE_HIDDEN;

import android.content.Context;
import android.view.View;
import android.view.Window;
Expand All @@ -28,7 +30,7 @@
import com.google.android.material.sidesheet.Sheet.StableSheetState;

/** Base class for {@link android.app.Dialog}s styled as a side sheet. */
public class SideSheetDialog extends SheetDialog {
public class SideSheetDialog extends SheetDialog<SideSheetCallback> {

private static final int SIDE_SHEET_DIALOG_THEME_ATTR = R.attr.sideSheetDialogTheme;
private static final int SIDE_SHEET_DIALOG_DEFAULT_THEME_RES =
Expand All @@ -38,6 +40,23 @@ public SideSheetDialog(@NonNull Context context) {
this(context, 0);
}

@Override
void addSheetCancelOnHideCallback(
Sheet<SideSheetCallback> behavior) {
behavior.addCallback(
new SideSheetCallback() {
@Override
public void onStateChanged(@NonNull View sheet, int newState) {
if (newState == STATE_HIDDEN) {
cancel();
}
}

@Override
public void onSlide(@NonNull View sheet, float slideOffset) {}
});
}

public SideSheetDialog(@NonNull Context context, @StyleRes int theme) {
super(context, theme, SIDE_SHEET_DIALOG_THEME_ATTR, SIDE_SHEET_DIALOG_DEFAULT_THEME_RES);
// We hide the title bar for any style configuration. Otherwise, there will be a gap
Expand All @@ -47,25 +66,25 @@ public SideSheetDialog(@NonNull Context context, @StyleRes int theme) {

@LayoutRes
@Override
protected int getLayoutResId() {
int getLayoutResId() {
return R.layout.m3_side_sheet_dialog;
}

@IdRes
@Override
protected int getDialogId() {
int getDialogId() {
return R.id.m3_side_sheet;
}

@NonNull
@Override
protected Sheet getBehaviorFromSheet(@NonNull FrameLayout sheet) {
Sheet<SideSheetCallback> getBehaviorFromSheet(@NonNull FrameLayout sheet) {
return SideSheetBehavior.from(sheet);
}

@StableSheetState
@Override
protected int getStateOnStart() {
int getStateOnStart() {
return Sheet.STATE_EXPANDED;
}

Expand All @@ -77,7 +96,7 @@ protected int getStateOnStart() {
@NonNull
@Override
public SideSheetBehavior<? extends View> getBehavior() {
Sheet sheetBehavior = super.getBehavior();
Sheet<SideSheetCallback> sheetBehavior = super.getBehavior();
if (!(sheetBehavior instanceof SideSheetBehavior)) {
throw new IllegalStateException("The view is not associated with SideSheetBehavior");
}
Expand Down
Expand Up @@ -26,6 +26,7 @@
import android.os.Looper;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import org.junit.Before;
Expand Down Expand Up @@ -104,6 +105,22 @@ public void expandedOffset_isIdempotent() {
.isEqualTo(expandedOffsetOnInitialization);
}

@Test
public void click_onScrim_cancelsSheet() {
View scrim = sideSheetDialog.findViewById(R.id.touch_outside);
assertThat(scrim).isNotNull();

showSideSheetDialog();
assertThat(sideSheetDialog.isShowing()).isTrue();
assertThat(scrim.getVisibility()).isEqualTo(View.VISIBLE);

// Click outside the side sheet.
scrim.performClick();
shadowOf(Looper.getMainLooper()).idle();

assertThat(sideSheetDialog.isShowing()).isFalse();
}

@UiThread
private void showSideSheetDialog() {
activity.runOnUiThread(
Expand All @@ -117,7 +134,7 @@ private void showSideSheetDialog() {
private void hideSideSheetDialog() {
activity.runOnUiThread(
() -> {
sideSheetDialog.hide();
sideSheetDialog.dismiss();
shadowOf(Looper.getMainLooper()).idle();
});
}
Expand Down

0 comments on commit 0c4e4a7

Please sign in to comment.