[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
diff --git a/res/values/id.xml b/res/values/id.xml
index 198496f..59813ad 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -40,6 +40,7 @@
<item type="id" name="cache_entry_tag_id" />
<item type="id" name="saved_clip_children_tag_id" />
+ <item type="id" name="saved_clip_to_padding_tag_id" />
<item type="id" name="saved_floating_widget_foreground" />
<item type="id" name="saved_floating_widget_background" />
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d44438f..d3019c5 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -68,6 +68,8 @@
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;
@@ -773,6 +775,136 @@
}
/**
+ * 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}.
* <p>
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index e2c5795..aeef5b8 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_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.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;
@@ -423,79 +422,6 @@
}
/**
- * 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.
*/
public void setShiftRange(float shiftRange) {
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index ddc3cbb..4c9371d 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -347,8 +347,7 @@
/** 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 c3bb993..1e17252 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -17,6 +17,10 @@
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.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 @@
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 @@
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();
@@ -134,6 +148,25 @@
}
@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);
if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {