From c51363fc4675df5e000884c9797e73e0bd069548 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Fri, 15 Mar 2024 16:23:49 -0700 Subject: [PATCH] [Predictive Back] Support WidgetsTwoPaneSheet Fix: 325930715 Test: Manual - attached video to bug Flag: aconfig com.android.launcher3.enable_predictive_back_gesture TEAMFOOD Change-Id: I44098de12253f803526160bce6457a940b2153c7 --- res/values/id.xml | 1 + src/com/android/launcher3/Utilities.java | 132 ++++++++++++++++++ .../allapps/AllAppsTransitionController.java | 78 +---------- .../launcher3/views/AbstractSlideInView.java | 3 +- .../widget/picker/WidgetsTwoPaneSheet.java | 33 +++++ 5 files changed, 169 insertions(+), 78 deletions(-) diff --git a/res/values/id.xml b/res/values/id.xml index 198496f741..59813ad290 100644 --- a/res/values/id.xml +++ b/res/values/id.xml @@ -40,6 +40,7 @@ + diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index d44438f5bc..d3019c58d9 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -68,6 +68,8 @@ import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; import android.view.animation.Interpolator; import androidx.annotation.ChecksSdkIntAtLeast; @@ -772,6 +774,136 @@ public final class Utilities { )); } + /** + * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent + * (direct or indirect) inclusive. This method will also save the old clipChildren value on each + * view with {@link View#setTag(int, Object)}, which can be restored in + * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}. + * + * Note that if parent is null or not a parent of the view, this method will be applied all the + * way to root view. + * + * @param v child view + * @param parent direct or indirect parent of child view + * @param clipChildren whether we should clip children + */ + public static void setClipChildrenOnViewTree( + @Nullable View v, + @Nullable ViewParent parent, + boolean clipChildren) { + if (v == null) { + return; + } + + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + boolean oldClipChildren = viewGroup.getClipChildren(); + if (oldClipChildren != clipChildren) { + v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren); + viewGroup.setClipChildren(clipChildren); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren); + } + } + + /** + * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value + * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent + * (direct or indirect) inclusive. + * + * Note that if parent is null or not a parent of the view, this method will be applied all the + * way to root view. + * + * @param v child view + * @param parent direct or indirect parent of child view + */ + public static void restoreClipChildrenOnViewTree( + @Nullable View v, @Nullable ViewParent parent) { + if (v == null) { + return; + } + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id); + if (viewTag instanceof Boolean) { + viewGroup.setClipChildren((boolean) viewTag); + viewGroup.setTag(R.id.saved_clip_children_tag_id, null); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + restoreClipChildrenOnViewTree((View) v.getParent(), parent); + } + } + + /** + * Similar to {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} but is calling + * {@link ViewGroup#setClipToPadding}. + */ + public static void setClipToPaddingOnViewTree( + @Nullable View v, + @Nullable ViewParent parent, + boolean clipToPadding) { + if (v == null) { + return; + } + + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + boolean oldClipToPadding = viewGroup.getClipToPadding(); + if (oldClipToPadding != clipToPadding) { + v.setTag(R.id.saved_clip_to_padding_tag_id, oldClipToPadding); + viewGroup.setClipToPadding(clipToPadding); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + setClipToPaddingOnViewTree((View) v.getParent(), parent, clipToPadding); + } + } + + /** + * Similar to {@link #restoreClipChildrenOnViewTree(View, ViewParent)} but is calling + * {@link ViewGroup#setClipToPadding}. + */ + public static void restoreClipToPaddingOnViewTree( + @Nullable View v, @Nullable ViewParent parent) { + if (v == null) { + return; + } + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + Object viewTag = viewGroup.getTag(R.id.saved_clip_to_padding_tag_id); + if (viewTag instanceof Boolean) { + viewGroup.setClipToPadding((boolean) viewTag); + viewGroup.setTag(R.id.saved_clip_to_padding_tag_id, null); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + restoreClipToPaddingOnViewTree((View) v.getParent(), parent); + } + } + /** * Translates the {@code targetView} so that it overlaps with {@code exclusionBounds} as little * as possible, while remaining within {@code inclusionBounds}. diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index e2c5795358..aeef5b8ae4 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -24,6 +24,8 @@ import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.Utilities.restoreClipChildrenOnViewTree; +import static com.android.launcher3.Utilities.setClipChildrenOnViewTree; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; @@ -38,12 +40,9 @@ import android.animation.ValueAnimator; import android.util.FloatProperty; import android.view.HapticFeedbackConstants; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; import android.view.animation.Interpolator; import androidx.annotation.FloatRange; -import androidx.annotation.Nullable; import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; @@ -422,79 +421,6 @@ public class AllAppsTransitionController mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum); } - /** - * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent - * (direct or indirect) inclusive. This method will also save the old clipChildren value on each - * view with {@link View#setTag(int, Object)}, which can be restored in - * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}. - * - * Note that if parent is null or not a parent of the view, this method will be applied all the - * way to root view. - * - * @param v child view - * @param parent direct or indirect parent of child view - * @param clipChildren whether we should clip children - */ - private static void setClipChildrenOnViewTree( - @Nullable View v, - @Nullable ViewParent parent, - boolean clipChildren) { - if (v == null) { - return; - } - - if (v instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) v; - boolean oldClipChildren = viewGroup.getClipChildren(); - if (oldClipChildren != clipChildren) { - v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren); - viewGroup.setClipChildren(clipChildren); - } - } - - if (v == parent) { - return; - } - - if (v.getParent() instanceof View) { - setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren); - } - } - - /** - * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value - * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent - * (direct or indirect) inclusive. - * - * Note that if parent is null or not a parent of the view, this method will be applied all the - * way to root view. - * - * @param v child view - * @param parent direct or indirect parent of child view - */ - private static void restoreClipChildrenOnViewTree( - @Nullable View v, @Nullable ViewParent parent) { - if (v == null) { - return; - } - if (v instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) v; - Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id); - if (viewTag instanceof Boolean) { - viewGroup.setClipChildren((boolean) viewTag); - viewGroup.setTag(R.id.saved_clip_children_tag_id, null); - } - } - - if (v == parent) { - return; - } - - if (v.getParent() instanceof View) { - restoreClipChildrenOnViewTree((View) v.getParent(), parent); - } - } - /** * Updates the total scroll range but does not update the UI. */ diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index ddc3cbb7d0..4c9371dbce 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -347,8 +347,7 @@ public abstract class AbstractSlideInView /** Return extra space revealed during predictive back animation. */ @Px protected int getBottomOffsetPx() { - final int height = getMeasuredHeight(); - return (int) ((height / PREDICTIVE_BACK_MIN_SCALE - height) / 2); + return (int) (getMeasuredHeight() * (1 - PREDICTIVE_BACK_MIN_SCALE)); } /** diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java index c3bb993baf..1e17252308 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java @@ -17,6 +17,10 @@ package com.android.launcher3.widget.picker; import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions; import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker; +import static com.android.launcher3.Utilities.restoreClipChildrenOnViewTree; +import static com.android.launcher3.Utilities.restoreClipToPaddingOnViewTree; +import static com.android.launcher3.Utilities.setClipChildrenOnViewTree; +import static com.android.launcher3.Utilities.setClipToPaddingOnViewTree; import android.content.Context; import android.graphics.Outline; @@ -27,6 +31,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; +import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -60,10 +65,13 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet { private FrameLayout mSuggestedWidgetsContainer; private WidgetsListHeader mSuggestedWidgetsHeader; private PackageUserKey mSuggestedWidgetsPackageUserKey; + private View mPrimaryWidgetListView; private LinearLayout mRightPane; private ScrollView mRightPaneScrollView; private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder; + + private boolean mOldIsBackSwipeProgressing; private int mActivePage = -1; private PackageUserKey mSelectedHeader; @@ -124,6 +132,12 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet { mRightPane.setOutlineProvider(mViewOutlineProviderRightPane); mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view); mRightPaneScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER); + mRightPaneScrollView.setOutlineProvider(mViewOutlineProvider); + mRightPaneScrollView.setClipToOutline(true); + + mPrimaryWidgetListView = findViewById(R.id.primary_widgets_list_view); + mPrimaryWidgetListView.setOutlineProvider(mViewOutlineProvider); + mPrimaryWidgetListView.setClipToOutline(true); onRecommendedWidgetsBound(); onWidgetsBound(); @@ -133,6 +147,25 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet { mFastScroller.setVisibility(GONE); } + @Override + protected void onScaleProgressChanged() { + super.onScaleProgressChanged(); + boolean isBackSwipeProgressing = mSlideInViewScale.value > 0; + if (isBackSwipeProgressing == mOldIsBackSwipeProgressing) { + return; + } + mOldIsBackSwipeProgressing = isBackSwipeProgressing; + if (isBackSwipeProgressing) { + setClipChildrenOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent, false); + setClipChildrenOnViewTree(mRightPaneScrollView, (ViewParent) mContent, false); + setClipToPaddingOnViewTree(mRightPaneScrollView, (ViewParent) mContent, false); + } else { + restoreClipChildrenOnViewTree(mPrimaryWidgetListView, mContent); + restoreClipChildrenOnViewTree(mRightPaneScrollView, mContent); + restoreClipToPaddingOnViewTree(mRightPaneScrollView, mContent); + } + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b);