Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c51363fc authored by Fengjiang Li's avatar Fengjiang Li
Browse files

[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
parent 88fea708
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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" />
+132 −0
Original line number Diff line number Diff line
@@ -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}.
+2 −76
Original line number Diff line number Diff line
@@ -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.
     */
+1 −2
Original line number Diff line number Diff line
@@ -347,8 +347,7 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
    /** 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));
    }

    /**
+33 −0
Original line number Diff line number Diff line
@@ -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);