Skip to content

Commit 562285e

Browse files
afohrmanleticiarossi
authored andcommitted
[Predictive Back][Side Sheet] Added predictive back support for standard side sheets.
PiperOrigin-RevId: 527326750
1 parent 29b59c2 commit 562285e

File tree

3 files changed

+192
-39
lines changed

3 files changed

+192
-39
lines changed

catalog/java/io/material/catalog/sidesheet/SideSheetMainDemoFragment.java

Lines changed: 108 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static android.view.View.NO_ID;
2222

23+
import android.os.Build.VERSION_CODES;
2324
import android.os.Bundle;
2425
import androidx.appcompat.app.AppCompatActivity;
2526
import androidx.appcompat.widget.Toolbar;
@@ -30,11 +31,14 @@
3031
import android.view.ViewGroup;
3132
import android.widget.Button;
3233
import android.widget.TextView;
34+
import android.window.BackEvent;
35+
import androidx.activity.OnBackPressedCallback;
3336
import androidx.annotation.GravityInt;
3437
import androidx.annotation.IdRes;
3538
import androidx.annotation.LayoutRes;
3639
import androidx.annotation.NonNull;
3740
import androidx.annotation.Nullable;
41+
import androidx.annotation.RequiresApi;
3842
import androidx.annotation.StringRes;
3943
import androidx.annotation.StyleRes;
4044
import androidx.coordinatorlayout.widget.CoordinatorLayout.LayoutParams;
@@ -96,7 +100,8 @@ public View onCreateDemoView(
96100
view,
97101
R.id.standard_side_sheet_container,
98102
R.id.show_standard_side_sheet_button,
99-
R.id.close_icon_button);
103+
R.id.close_icon_button,
104+
/* shouldHandleBack= */ true);
100105

101106
setSideSheetCallback(
102107
standardRightSideSheet, R.id.side_sheet_state_text, R.id.side_sheet_slide_offset_text);
@@ -107,7 +112,8 @@ public View onCreateDemoView(
107112
view,
108113
R.id.standard_detached_side_sheet_container,
109114
R.id.show_standard_detached_side_sheet_button,
110-
R.id.detached_close_icon_button);
115+
R.id.detached_close_icon_button,
116+
/* shouldHandleBack= */ true);
111117

112118
setSideSheetCallback(
113119
detachedStandardSideSheet,
@@ -120,7 +126,8 @@ public View onCreateDemoView(
120126
view,
121127
R.id.vertically_scrolling_side_sheet_container,
122128
R.id.show_vertically_scrolling_side_sheet_button,
123-
R.id.vertically_scrolling_side_sheet_close_icon_button);
129+
R.id.vertically_scrolling_side_sheet_close_icon_button,
130+
/* shouldHandleBack= */ true);
124131

125132
setSideSheetCallback(
126133
verticallyScrollingSideSheet,
@@ -141,7 +148,8 @@ public View onCreateDemoView(
141148
view,
142149
R.id.coplanar_side_sheet_container,
143150
R.id.show_coplanar_side_sheet_button,
144-
R.id.coplanar_side_sheet_close_icon_button);
151+
R.id.coplanar_side_sheet_close_icon_button,
152+
/* shouldHandleBack= */ false);
145153

146154
setSideSheetCallback(
147155
coplanarSideSheet,
@@ -154,7 +162,8 @@ public View onCreateDemoView(
154162
view,
155163
R.id.coplanar_detached_side_sheet_container,
156164
R.id.show_coplanar_detached_side_sheet_button,
157-
R.id.coplanar_detached_side_sheet_close_icon_button);
165+
R.id.coplanar_detached_side_sheet_close_icon_button,
166+
/* shouldHandleBack= */ false);
158167

159168
setSideSheetCallback(
160169
detachedCoplanarSideSheet,
@@ -165,8 +174,7 @@ public View onCreateDemoView(
165174
}
166175

167176
private void setUpSheetGravityButtonToggleGroup(@NonNull View view) {
168-
sheetGravityButtonToggleGroup =
169-
view.findViewById(R.id.sheet_gravity_button_toggle_group);
177+
sheetGravityButtonToggleGroup = view.findViewById(R.id.sheet_gravity_button_toggle_group);
170178
// Check the button corresponding to end sheet gravity, which is the default.
171179
sheetGravityButtonToggleGroup.check(R.id.end_gravity_button);
172180
sheetGravityButtonToggleGroup.addOnButtonCheckedListener(
@@ -185,11 +193,39 @@ private void setUpSheetGravityButtonToggleGroup(@NonNull View view) {
185193
});
186194
}
187195

196+
private void setupBackHandling(SideSheetBehavior<View> sideSheetBehavior) {
197+
OnBackPressedCallback nonModalOnBackPressedCallback =
198+
createNonModalOnBackPressedCallback(sideSheetBehavior);
199+
requireActivity().getOnBackPressedDispatcher().addCallback(this, nonModalOnBackPressedCallback);
200+
sideSheetBehavior.addCallback(
201+
new SideSheetCallback() {
202+
@Override
203+
public void onStateChanged(@NonNull View sheet, int newState) {
204+
switch (newState) {
205+
case SideSheetBehavior.STATE_EXPANDED:
206+
case SideSheetBehavior.STATE_SETTLING:
207+
nonModalOnBackPressedCallback.setEnabled(true);
208+
break;
209+
case SideSheetBehavior.STATE_HIDDEN:
210+
nonModalOnBackPressedCallback.setEnabled(false);
211+
break;
212+
case SideSheetBehavior.STATE_DRAGGING:
213+
default:
214+
break;
215+
}
216+
}
217+
218+
@Override
219+
public void onSlide(@NonNull View sheet, float slideOffset) {}
220+
});
221+
}
222+
188223
private View setUpSideSheet(
189224
@NonNull View view,
190225
@IdRes int sideSheetContainerId,
191226
@IdRes int showSideSheetButtonId,
192-
@IdRes int closeIconButtonId) {
227+
@IdRes int closeIconButtonId,
228+
boolean shouldHandleBack) {
193229
View sideSheet = view.findViewById(sideSheetContainerId);
194230
SideSheetBehavior<View> sideSheetBehavior = SideSheetBehavior.from(sideSheet);
195231

@@ -199,6 +235,10 @@ private View setUpSideSheet(
199235
View standardSideSheetCloseIconButton = sideSheet.findViewById(closeIconButtonId);
200236
standardSideSheetCloseIconButton.setOnClickListener(v -> hideSideSheet(sideSheetBehavior));
201237

238+
if (shouldHandleBack) {
239+
setupBackHandling(sideSheetBehavior);
240+
}
241+
202242
sideSheetViews.add(sideSheet);
203243

204244
return sideSheet;
@@ -258,37 +298,39 @@ private void setUpModalSheet(
258298
@StringRes int sheetTitleStringRes,
259299
@NonNull Button showSheetButton,
260300
@IdRes int closeIconButtonIdRes) {
261-
showSheetButton.setOnClickListener(v1 -> {
262-
SideSheetDialog sheetDialog =
263-
sheetThemeOverlayRes == NO_ID
264-
? new SideSheetDialog(requireContext())
265-
: new SideSheetDialog(requireContext(), sheetThemeOverlayRes);
266-
267-
sheetDialog.setContentView(sheetContentLayoutRes);
268-
View modalSheetContent = sheetDialog.findViewById(sheetContentRootIdRes);
269-
if (modalSheetContent != null) {
270-
TextView modalSideSheetTitle = modalSheetContent.findViewById(sheetTitleIdRes);
271-
modalSideSheetTitle.setText(sheetTitleStringRes);
272-
}
273-
new WindowPreferencesManager(requireContext())
274-
.applyEdgeToEdgePreference(sheetDialog.getWindow());
275-
276-
sheetDialog
277-
.getBehavior()
278-
.addCallback(
279-
createSideSheetCallback(
280-
sheetDialog.findViewById(R.id.side_sheet_state_text),
281-
sheetDialog.findViewById(R.id.side_sheet_slide_offset_text)));
282-
283-
sheetDialog.setSheetEdge(getGravityForIdRes(sheetGravityButtonToggleGroup.getCheckedButtonId()));
284-
285-
View modalSideSheetCloseIconButton = sheetDialog.findViewById(closeIconButtonIdRes);
286-
if (modalSideSheetCloseIconButton != null) {
287-
modalSideSheetCloseIconButton.setOnClickListener(v2 -> sheetDialog.hide());
288-
}
301+
showSheetButton.setOnClickListener(
302+
v1 -> {
303+
SideSheetDialog sheetDialog =
304+
sheetThemeOverlayRes == NO_ID
305+
? new SideSheetDialog(requireContext())
306+
: new SideSheetDialog(requireContext(), sheetThemeOverlayRes);
307+
308+
sheetDialog.setContentView(sheetContentLayoutRes);
309+
View modalSheetContent = sheetDialog.findViewById(sheetContentRootIdRes);
310+
if (modalSheetContent != null) {
311+
TextView modalSideSheetTitle = modalSheetContent.findViewById(sheetTitleIdRes);
312+
modalSideSheetTitle.setText(sheetTitleStringRes);
313+
}
314+
new WindowPreferencesManager(requireContext())
315+
.applyEdgeToEdgePreference(sheetDialog.getWindow());
316+
317+
sheetDialog
318+
.getBehavior()
319+
.addCallback(
320+
createSideSheetCallback(
321+
sheetDialog.findViewById(R.id.side_sheet_state_text),
322+
sheetDialog.findViewById(R.id.side_sheet_slide_offset_text)));
323+
324+
sheetDialog.setSheetEdge(
325+
getGravityForIdRes(sheetGravityButtonToggleGroup.getCheckedButtonId()));
326+
327+
View modalSideSheetCloseIconButton = sheetDialog.findViewById(closeIconButtonIdRes);
328+
if (modalSideSheetCloseIconButton != null) {
329+
modalSideSheetCloseIconButton.setOnClickListener(v2 -> sheetDialog.hide());
330+
}
289331

290-
sheetDialog.show();
291-
});
332+
sheetDialog.show();
333+
});
292334
}
293335

294336
private void setUpToolbar(@NonNull View view) {
@@ -364,4 +406,32 @@ public void onSlide(@NonNull View sheet, float slideOffset) {
364406
}
365407
};
366408
}
409+
410+
private OnBackPressedCallback createNonModalOnBackPressedCallback(
411+
SideSheetBehavior<View> behavior) {
412+
return new OnBackPressedCallback(/* enabled= */ false) {
413+
@RequiresApi(VERSION_CODES.UPSIDE_DOWN_CAKE)
414+
@Override
415+
public void handleOnBackStarted(@NonNull BackEvent backEvent) {
416+
behavior.startBackProgress(backEvent);
417+
}
418+
419+
@RequiresApi(VERSION_CODES.UPSIDE_DOWN_CAKE)
420+
@Override
421+
public void handleOnBackProgressed(@NonNull BackEvent backEvent) {
422+
behavior.updateBackProgress(backEvent);
423+
}
424+
425+
@Override
426+
public void handleOnBackPressed() {
427+
behavior.handleBackInvoked();
428+
}
429+
430+
@RequiresApi(VERSION_CODES.UPSIDE_DOWN_CAKE)
431+
@Override
432+
public void handleOnBackCancelled() {
433+
behavior.cancelBackProgress();
434+
}
435+
};
436+
}
367437
}

lib/java/com/google/android/material/sidesheet/Sheet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020

2121
import androidx.annotation.IntDef;
2222
import androidx.annotation.RestrictTo;
23+
import com.google.android.material.motion.MaterialBackHandler;
2324
import java.lang.annotation.Retention;
2425
import java.lang.annotation.RetentionPolicy;
2526

2627
/**
2728
* Interface for sheet constants and {@code IntDefs} to be shared between the different {@link
2829
* Sheet} implementations.
2930
*/
30-
interface Sheet<C extends SheetCallback> {
31+
interface Sheet<C extends SheetCallback> extends MaterialBackHandler {
3132
/** The sheet is dragging. */
3233
int STATE_DRAGGING = 1;
3334

0 commit comments

Comments
 (0)