Skip to content

Commit

Permalink
[Side Sheet] Added left/start modal sheet.
Browse files Browse the repository at this point in the history
Sheet gravity is intended to be set once at setup before the sheet is shown; sheet gravity changes are not supported at runtime and will cause an IllegalStateException.

Also refactored modal sheet demo to recreate the sheet every time it's shown, which allows the sheet gravity changes to take effect for modal sheets.

PiperOrigin-RevId: 526957847
  • Loading branch information
afohrman authored and leticiarossi committed Apr 25, 2023
1 parent 44a97f4 commit d0ad45e
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

import io.material.catalog.R;

import static android.view.View.NO_ID;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDialog;
import androidx.appcompat.widget.Toolbar;
import android.util.SparseIntArray;
import android.view.Gravity;
Expand All @@ -29,6 +30,7 @@
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.GravityInt;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -62,6 +64,10 @@ public class SideSheetMainDemoFragment extends DemoFragment {
GRAVITY_ID_RES_MAP.append(R.id.end_gravity_button, Gravity.END);
}

private Button showModalSheetButton;
private Button showDetachedModalSheetButton;
private MaterialButtonToggleGroup sheetGravityButtonToggleGroup;

@Override
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
Expand All @@ -82,6 +88,7 @@ public View onCreateDemoView(
View.inflate(getContext(), getSideSheetsContent(), sideSheetsContainer);

setUpToolbar(view);
setUpSheetGravityButtonToggleGroup(view);

// Set up standard side sheet.
View standardRightSideSheet =
Expand Down Expand Up @@ -121,56 +128,12 @@ public View onCreateDemoView(
R.id.vertically_scrolling_side_sheet_slide_offset_text);

// Set up modal side sheet.
SideSheetDialog sideSheetDialog = new SideSheetDialog(requireContext());
setUpModalSheet(
sideSheetDialog,
R.layout.cat_sidesheet_content,
R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_title);

View showModalSideSheetButton = view.findViewById(R.id.show_modal_side_sheet_button);
showModalSideSheetButton.setOnClickListener(v -> sideSheetDialog.show());

sideSheetDialog
.getBehavior()
.addCallback(
createSideSheetCallback(
sideSheetDialog.findViewById(R.id.side_sheet_state_text),
sideSheetDialog.findViewById(R.id.side_sheet_slide_offset_text)));

View modalSideSheetCloseIconButton = sideSheetDialog.findViewById(R.id.close_icon_button);
if (modalSideSheetCloseIconButton != null) {
modalSideSheetCloseIconButton.setOnClickListener(v -> sideSheetDialog.hide());
}
showModalSheetButton = view.findViewById(R.id.show_modal_side_sheet_button);
setUpModalSheet();

// Set up detached modal side sheet.
SideSheetDialog detachedSideSheetDialog =
new SideSheetDialog(requireContext(), getDetachedModalThemeOverlayResId());

setUpModalSheet(
detachedSideSheetDialog,
R.layout.cat_sidesheet_content,
R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_detached_title);

View showDetachedModalSideSheetButton =
view.findViewById(R.id.show_modal_detached_side_sheet_button);
showDetachedModalSideSheetButton.setOnClickListener(v -> detachedSideSheetDialog.show());

detachedSideSheetDialog
.getBehavior()
.addCallback(
createSideSheetCallback(
detachedSideSheetDialog.findViewById(R.id.side_sheet_state_text),
detachedSideSheetDialog.findViewById(R.id.side_sheet_slide_offset_text)));

View detachedModalSideSheetCloseIconButton =
detachedSideSheetDialog.findViewById(R.id.close_icon_button);
if (detachedModalSideSheetCloseIconButton != null) {
detachedModalSideSheetCloseIconButton.setOnClickListener(v -> detachedSideSheetDialog.hide());
}
showDetachedModalSheetButton = view.findViewById(R.id.show_modal_detached_side_sheet_button);
setUpDetachedModalSheet();

// Set up coplanar side sheet.
View coplanarSideSheet =
Expand Down Expand Up @@ -198,26 +161,28 @@ public View onCreateDemoView(
R.id.coplanar_detached_side_sheet_state_text,
R.id.coplanar_detached_side_sheet_slide_offset_text);

MaterialButtonToggleGroup sheetGravityButtonToggleGroup =
return view;
}

private void setUpSheetGravityButtonToggleGroup(@NonNull View view) {
sheetGravityButtonToggleGroup =
view.findViewById(R.id.sheet_gravity_button_toggle_group);
// Check the end gravity button since the default gravity is end.
// Check the button corresponding to end sheet gravity, which is the default.
sheetGravityButtonToggleGroup.check(R.id.end_gravity_button);
sheetGravityButtonToggleGroup.addOnButtonCheckedListener(
(group, checkedId, isChecked) -> {
int newGravity;
if (isChecked) {
newGravity = getGravityForIdRes(checkedId);
int sheetGravity = getGravityForIdRes(checkedId);

for (View sideSheetView : sideSheetViews) {
ViewGroup.LayoutParams layoutParams = sideSheetView.getLayoutParams();
if (layoutParams instanceof LayoutParams) {
((LayoutParams) layoutParams).gravity = newGravity;
((LayoutParams) layoutParams).gravity = sheetGravity;
sideSheetView.requestLayout();
}
}
}
});
return view;
}

private View setUpSideSheet(
Expand Down Expand Up @@ -247,20 +212,83 @@ private void setSideSheetCallback(
sideSheet.findViewById(stateTextViewId), sideSheet.findViewById(slideOffsetTextId)));
}

private void setUpDetachedModalSheet() {
setUpModalSheet(
getDetachedModalThemeOverlayResId(),
R.layout.cat_sidesheet_content,
R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_detached_title,
showDetachedModalSheetButton,
R.id.close_icon_button);
}

private void setUpModalSheet() {
setUpModalSheet(
R.layout.cat_sidesheet_content,
R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_title,
showModalSheetButton,
R.id.close_icon_button);
}

private void setUpModalSheet(
@NonNull AppCompatDialog sheetDialog,
@LayoutRes int sheetContentLayoutRes,
@IdRes int sheetContentRootIdRes,
@IdRes int sheetTitleIdRes,
@StringRes int sheetTitleStringRes) {
sheetDialog.setContentView(sheetContentLayoutRes);
View modalSheetContent = sheetDialog.findViewById(sheetContentRootIdRes);
if (modalSheetContent != null) {
TextView modalSideSheetTitle = modalSheetContent.findViewById(sheetTitleIdRes);
modalSideSheetTitle.setText(sheetTitleStringRes);
}
new WindowPreferencesManager(requireContext())
.applyEdgeToEdgePreference(sheetDialog.getWindow());
@StringRes int sheetTitleStringRes,
@NonNull Button showSheetButtonIdRes,
@IdRes int closeIconButtonIdRes) {
setUpModalSheet(
NO_ID,
sheetContentLayoutRes,
sheetContentRootIdRes,
sheetTitleIdRes,
sheetTitleStringRes,
showSheetButtonIdRes,
closeIconButtonIdRes);
}

private void setUpModalSheet(
@StyleRes int sheetThemeOverlayRes,
@LayoutRes int sheetContentLayoutRes,
@IdRes int sheetContentRootIdRes,
@IdRes int sheetTitleIdRes,
@StringRes int sheetTitleStringRes,
@NonNull Button showSheetButton,
@IdRes int closeIconButtonIdRes) {
showSheetButton.setOnClickListener(v1 -> {
SideSheetDialog sheetDialog =
sheetThemeOverlayRes == NO_ID
? new SideSheetDialog(requireContext())
: new SideSheetDialog(requireContext(), sheetThemeOverlayRes);

sheetDialog.setContentView(sheetContentLayoutRes);
View modalSheetContent = sheetDialog.findViewById(sheetContentRootIdRes);
if (modalSheetContent != null) {
TextView modalSideSheetTitle = modalSheetContent.findViewById(sheetTitleIdRes);
modalSideSheetTitle.setText(sheetTitleStringRes);
}
new WindowPreferencesManager(requireContext())
.applyEdgeToEdgePreference(sheetDialog.getWindow());

sheetDialog
.getBehavior()
.addCallback(
createSideSheetCallback(
sheetDialog.findViewById(R.id.side_sheet_state_text),
sheetDialog.findViewById(R.id.side_sheet_slide_offset_text)));

sheetDialog.setSheetEdge(getGravityForIdRes(sheetGravityButtonToggleGroup.getCheckedButtonId()));

View modalSideSheetCloseIconButton = sheetDialog.findViewById(closeIconButtonIdRes);
if (modalSideSheetCloseIconButton != null) {
modalSideSheetCloseIconButton.setOnClickListener(v2 -> sheetDialog.hide());
}

sheetDialog.show();
});
}

private void setUpToolbar(@NonNull View view) {
Expand Down Expand Up @@ -298,7 +326,7 @@ private int getDetachedModalThemeOverlayResId() {
return R.style.ThemeOverlay_Catalog_SideSheet_Modal_Detached;
}

@IdRes
@GravityInt
private static int getGravityForIdRes(@IdRes int gravityButtonIdRes) {
return GRAVITY_ID_RES_MAP.get(gravityButtonIdRes);
}
Expand Down
52 changes: 52 additions & 0 deletions lib/java/com/google/android/material/sidesheet/SheetDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,22 @@
import android.os.Bundle;
import androidx.appcompat.app.AppCompatDialog;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.GravityInt;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.sidesheet.Sheet.StableSheetState;
Expand Down Expand Up @@ -124,6 +127,12 @@ protected void onStart() {
}
}

@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
maybeUpdateWindowAnimationsBasedOnLayoutDirection();
}

/**
* This function can be called from a few different use cases, including swiping the dialog away
* or calling `dismiss()` from a `SideSheetDialogFragment`, tapping outside a dialog, etc...
Expand Down Expand Up @@ -265,6 +274,49 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) {
return container;
}

/**
* Set the edge which the sheet should originate from.
*
* <p>Note: This method should be called when the sheet is initialized, before it is shown.
* Runtime sheet edge changes are not supported.
*
* @throws IllegalStateException if the sheet is null or has already been laid out
* @param gravity the edge from which the sheet and its animations should originate.
*/
public void setSheetEdge(@GravityInt int gravity) {
if (sheet == null) {
throw new IllegalStateException(
"Sheet view reference is null; sheet edge cannot be changed if the sheet view is null.");
}
if (ViewCompat.isLaidOut(sheet)) {
throw new IllegalStateException(
"Sheet view has been laid out; sheet edge cannot be changed once the sheet has been laid"
+ " out.");
}
ViewGroup.LayoutParams layoutParams = sheet.getLayoutParams();
if (layoutParams instanceof CoordinatorLayout.LayoutParams) {
((CoordinatorLayout.LayoutParams) layoutParams).gravity = gravity;
maybeUpdateWindowAnimationsBasedOnLayoutDirection();
}
}

private void maybeUpdateWindowAnimationsBasedOnLayoutDirection() {
Window window = getWindow();
if (window != null
&& sheet != null
&& sheet.getLayoutParams() instanceof CoordinatorLayout.LayoutParams) {
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) sheet.getLayoutParams();
int absoluteGravity =
GravityCompat.getAbsoluteGravity(
layoutParams.gravity, ViewCompat.getLayoutDirection(sheet));
window.setWindowAnimations(
absoluteGravity == Gravity.LEFT
? R.style.Animation_Material3_SideSheetDialog_Left
: R.style.Animation_Material3_SideSheetDialog_Right);
}
}

private boolean shouldWindowCloseOnTouchOutside() {
if (!canceledOnTouchOutsideSet) {
TypedArray a =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="275"
android:interpolator="?attr/motionEasingEmphasizedInterpolator">

<translate
android:fromXDelta="-100%"
android:toXDelta="0"/>

</set>
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,11 @@
~ limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="250"
android:interpolator="@android:interpolator/linear_out_slow_in">
android:duration="275"
android:interpolator="?attr/motionEasingEmphasizedInterpolator">

<translate
android:fromXDelta="20%p"
android:fromXDelta="100%"
android:toXDelta="0"/>

<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>

</set>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="275"
android:interpolator="?attr/motionEasingEmphasizedInterpolator">

<translate
android:fromXDelta="0"
android:toXDelta="-100%"/>

</set>
Loading

0 comments on commit d0ad45e

Please sign in to comment.