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

Commit 324d3b0f authored by Sihua Ma's avatar Sihua Ma
Browse files

Add widget layout transition when resizing

Also getting rid of part of the logic that updates the frame size in multi-window mode since multi-window is no longer supported

Test: N/A
Bug: 268553314
Flag: ENABLE_WIDGET_TRANSITION_FOR_RESIZING. OFF
Change-Id: I081b11441b562fccec7feb12cec0b28b9a0ea3a2
parent b0b8e3a5
Loading
Loading
Loading
Loading
+78 −20
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.appwidget.AppWidgetProviderInfo;
@@ -26,12 +27,14 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;

import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -47,15 +50,18 @@ import java.util.ArrayList;
import java.util.List;

public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
    private static final int SNAP_DURATION = 150;
    private static final int SNAP_DURATION_MS = 150;
    private static final float DIMMED_HANDLE_ALPHA = 0f;
    private static final float RESIZE_THRESHOLD = 0.66f;
    private static final int RESIZE_TRANSITION_DURATION_MS = 150;

    private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
            "launcher.reconfigurable_widget_education_tip_seen";
    private static final Rect sTmpRect = new Rect();
    private static final Rect sTmpRect2 = new Rect();

    private static final int[] sDragLayerLoc = new int[2];

    private static final int HANDLE_COUNT = 4;
    private static final int INDEX_LEFT = 0;
    private static final int INDEX_TOP = 1;
@@ -124,6 +130,12 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
    private int mTopTouchRegionAdjustment = 0;
    private int mBottomTouchRegionAdjustment = 0;

    private int[] mWidgetViewWindowPos;
    private final Rect mWidgetViewOldRect = new Rect();
    private final Rect mWidgetViewNewRect = new Rect();
    private final @Nullable LauncherAppWidgetHostView.CellChildViewPreLayoutListener
            mCellChildViewPreLayoutListener;

    private int mXDown, mYDown;

    public AppWidgetResizeFrame(Context context) {
@@ -140,6 +152,18 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
        mLauncher = Launcher.getLauncher(context);
        mStateAnnouncer = DragViewStateAnnouncer.createFor(this);

        mCellChildViewPreLayoutListener = FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()
                ? (v, left, top, right, bottom) -> {
                            if (mWidgetViewWindowPos == null) {
                                mWidgetViewWindowPos = new int[2];
                            }
                            v.getLocationInWindow(mWidgetViewWindowPos);
                            mWidgetViewOldRect.set(v.getLeft(), v.getTop(), v.getRight(),
                                    v.getBottom());
                            mWidgetViewNewRect.set(left, top, right, bottom);
                        }
                : null;

        mBackgroundPadding = getResources()
                .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
        mTouchTargetWidth = 2 * mBackgroundPadding;
@@ -260,6 +284,14 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
            }
        }

        if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
            mWidgetView.setCellChildViewPreLayoutListener(mCellChildViewPreLayoutListener);
            mWidgetViewOldRect.set(mWidgetView.getLeft(), mWidgetView.getTop(),
                    mWidgetView.getRight(),
                    mWidgetView.getBottom());
            mWidgetViewNewRect.set(mWidgetViewOldRect);
        }

        CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
        ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
        CellPos presenterPos = mLauncher.getCellPosMapper().mapModelToPresenter(widgetInfo);
@@ -344,22 +376,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O

        resizeWidgetIfNeeded(false);

        // When the widget resizes in multi-window mode, the translation value changes to maintain
        // a center fit. These overrides ensure the resize frame always aligns with the widget view.
        getSnappedRectRelativeToDragLayer(sTmpRect);
        if (mLeftBorderActive) {
            lp.width = sTmpRect.width() + sTmpRect.left - lp.x;
        }
        if (mTopBorderActive) {
            lp.height = sTmpRect.height() + sTmpRect.top - lp.y;
        }
        if (mRightBorderActive) {
            lp.x = sTmpRect.left;
        }
        if (mBottomBorderActive) {
            lp.y = sTmpRect.top;
        }

        // Handle invalid resize across CellLayouts in the two panel UI.
        if (mCellLayout.getParent() instanceof Workspace) {
            Workspace<?> workspace = (Workspace<?>) mCellLayout.getParent();
@@ -508,9 +524,13 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
     * Returns the rect of this view when the frame is snapped around the widget, with the bounds
     * relative to the {@link DragLayer}.
     */
    private void getSnappedRectRelativeToDragLayer(Rect out) {
    private void getSnappedRectRelativeToDragLayer(@NonNull Rect out) {
        float scale = mWidgetView.getScaleToFit();
        if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
            getViewRectRelativeToDragLayer(out);
        } else {
            mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
        }

        int width = 2 * mBackgroundPadding + Math.round(scale * out.width());
        int height = 2 * mBackgroundPadding + Math.round(scale * out.height());
@@ -523,7 +543,41 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
        out.bottom = out.top + height;
    }

    private void getViewRectRelativeToDragLayer(@NonNull Rect out) {
        int[] afterPos = getViewPosRelativeToDragLayer();
        out.set(afterPos[0], afterPos[1], afterPos[0] + mWidgetViewNewRect.width(),
                afterPos[1] + mWidgetViewNewRect.height());
    }

    /** Returns the relative x and y values of the widget view after the layout transition */
    private int[] getViewPosRelativeToDragLayer() {
        mDragLayer.getLocationInWindow(sDragLayerLoc);
        int x = sDragLayerLoc[0];
        int y = sDragLayerLoc[1];

        if (mWidgetViewWindowPos == null) {
            mWidgetViewWindowPos = new int[2];
            mWidgetView.getLocationInWindow(mWidgetViewWindowPos);
        }

        int leftOffset = mWidgetViewNewRect.left - mWidgetViewOldRect.left;
        int topOffset = mWidgetViewNewRect.top - mWidgetViewOldRect.top;

        return new int[] {mWidgetViewWindowPos[0] - x + leftOffset,
                mWidgetViewWindowPos[1] - y + topOffset};
    }

    private void snapToWidget(boolean animate) {
        // The widget is guaranteed to be attached to the cell layout at this point, thus setting
        // the transition here
        if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()
                && mWidgetView.getLayoutTransition() == null) {
            final LayoutTransition transition = new LayoutTransition();
            transition.setDuration(RESIZE_TRANSITION_DURATION_MS);
            transition.enableTransitionType(LayoutTransition.CHANGING);
            mWidgetView.setLayoutTransition(transition);
        }

        getSnappedRectRelativeToDragLayer(sTmpRect);
        int newWidth = sTmpRect.width();
        int newHeight = sTmpRect.height();
@@ -585,7 +639,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
                updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
                        /* springLoadedProgress= */ 0f, /* animatorSet= */ set);
            }
            set.setDuration(SNAP_DURATION);
            set.setDuration(SNAP_DURATION_MS);
            set.start();
        }

@@ -665,6 +719,10 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O

    @Override
    protected void handleClose(boolean animate) {
        if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
            mWidgetView.clearCellChildViewPreLayoutListener();
            mWidgetView.setLayoutTransition(null);
        }
        mDragLayer.removeView(this);
    }

+11 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.NavigableAppWidgetHostView;

public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
@@ -217,6 +218,16 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.

        int childLeft = lp.x;
        int childTop = lp.y;

        // We want to get the layout position of the widget, but layout() is a final function in
        // ViewGroup which makes it impossible to be overridden. Overriding onLayout() will have no
        // effect since it will not be called when the transition is enabled. The only possible
        // solution here seems to be sending the positions when CellLayout is laying out the views
        if (child instanceof LauncherAppWidgetHostView widgetView
                && widgetView.getCellChildViewPreLayoutListener() != null) {
            widgetView.getCellChildViewPreLayoutListener().notifyBoundChangeOnPreLayout(child,
                    childLeft, childTop, childLeft + lp.width, childTop + lp.height);
        }
        child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);

        if (lp.dropped) {
+38 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.widget.AdapterView;
import android.widget.Advanceable;
import android.widget.RemoteViews;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.launcher3.CheckLongPressHelper;
@@ -63,6 +64,8 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
    private static final long ADVANCE_INTERVAL = 20000;
    private static final long ADVANCE_STAGGER = 250;

    private @Nullable CellChildViewPreLayoutListener mCellChildViewPreLayoutListener;

    // Maintains a list of widget ids which are supposed to be auto advanced.
    private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
    // Maximum duration for which updates can be deferred.
@@ -335,6 +338,26 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
        requestLayout();
    }

    /**
     * Set the pre-layout listener
     * @param listener The listener to be notified when {@code CellLayout} is to layout this view
     */
    public void setCellChildViewPreLayoutListener(
            @NonNull CellChildViewPreLayoutListener listener) {
        mCellChildViewPreLayoutListener = listener;
    }

    /** @return The current cell layout listener */
    @Nullable
    public CellChildViewPreLayoutListener getCellChildViewPreLayoutListener() {
        return mCellChildViewPreLayoutListener;
    }

    /** Clear the listener for the pre-layout in CellLayout */
    public void clearCellChildViewPreLayoutListener() {
        mCellChildViewPreLayoutListener = null;
    }

    @Override
    public void onColorsChanged(SparseIntArray colors) {
        if (isDeferringUpdates()) {
@@ -460,4 +483,19 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
        }
        return false;
    }

    /**
     * Listener interface to be called when {@code CellLayout} is about to layout this child view
     */
    public interface CellChildViewPreLayoutListener {
        /**
         * Notify the bound changes to this view on pre-layout
         * @param v The view which the listener is set for
         * @param left The new left coordinate of this view
         * @param top The new top coordinate of this view
         * @param right The new right coordinate of this view
         * @param bottom The new bottom coordinate of this view
         */
        void notifyBoundChangeOnPreLayout(View v, int left, int top, int right, int bottom);
    }
}