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

Commit 3bdfc3cd authored by Cyrus Boadway's avatar Cyrus Boadway
Browse files

Create return-to-home app widget animation

If an app has been most recently launched from an app widget, when
swiped away, the app animates to the widget's position.

This is done by attributing the app launch to the widget through
the ActivityOptions's launch cookies, and using a FloatingWidgetView
throughout the animation.

Bug: 169042867
Test: manual
Change-Id: I24c2623b5b3407504a4768b076849c47f73cbae0
parent 5f35ff02
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -447,7 +447,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                // Fall through and continue if it's an app or shortcut
            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                // Fall through and continue if it's an app, shortcut, or widget
                break;
            default:
                return;
+4 −1
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
import android.util.Pair;
import android.util.Size;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -781,7 +782,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
        final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
                ? 0 : getWindowCornerRadius(mLauncher.getResources());
        final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
                v, widgetBackgroundBounds, windowTargetBounds, finalWindowRadius);
                v, widgetBackgroundBounds,
                new Size(windowTargetBounds.width(), windowTargetBounds.height()),
                finalWindowRadius);
        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
                ? floatingView.getInitialCornerRadius() : 0;

+5 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.util.Pair;
import android.view.View;
import android.widget.RemoteViews;

import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.widget.LauncherAppWidgetHostView;

@@ -49,6 +50,10 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
        Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(hostView);
        ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
                .getActivityLaunchOptions(mLauncher, hostView);
        Object itemInfo = hostView.getTag();
        if (itemInfo instanceof ItemInfo) {
            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
        }
        options = Pair.create(options.first, activityOptions.options);
        return RemoteViews.startPendingIntent(hostView, pendingIntent, options);
    }
+193 −138
Original line number Diff line number Diff line
@@ -30,9 +30,11 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.Size;
import android.view.View;

import androidx.annotation.NonNull;
@@ -50,9 +52,11 @@ import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.ResourceProvider;
@@ -77,17 +81,34 @@ public class LauncherSwipeHandlerV2 extends
    @Override
    protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
            long duration) {
        HomeAnimationFactory homeAnimFactory;
        if (mActivity != null) {
        if (mActivity == null) {
            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                    isPresent -> mRecentsView.startHome());
            return new HomeAnimationFactory() {
                @Override
                public AnimatorPlaybackController createActivityAnimationToHome() {
                    return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
                }
            };
        }

        final View workspaceView = findWorkspaceView(launchCookies,
                mRecentsView.getRunningTaskView());
            boolean canUseWorkspaceView =
                    workspaceView != null && workspaceView.isAttachedToWindow();
        boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();

        mActivity.getRootView().setForceHideBackArrow(true);
        mActivity.setHintUserWillBeActive();

            if (canUseWorkspaceView) {
        if (!canUseWorkspaceView) {
            return new LauncherHomeAnimationFactory();
        }
        if (workspaceView instanceof LauncherAppWidgetHostView) {
            return createWidgetHomeAnimationFactory((LauncherAppWidgetHostView) workspaceView);
        }
        return createIconHomeAnimationFactory(workspaceView);
    }

    private HomeAnimationFactory createIconHomeAnimationFactory(View workspaceView) {
        final ResourceProvider rp = DynamicResource.provider(mActivity);
        final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
        float dpPerSecond = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp_per_s));
@@ -101,7 +122,7 @@ public class LauncherSwipeHandlerV2 extends
        // We want the window alpha to be 0 once this threshold is met, so that the
        // FolderIconView can be seen morphing into the icon shape.
        float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
                homeAnimFactory = new LauncherHomeAnimationFactory() {
        return new LauncherHomeAnimationFactory() {

            // There is a delay in loading the icon, so we need to keep the window
            // opaque until it is ready.
@@ -148,8 +169,7 @@ public class LauncherSwipeHandlerV2 extends
                final float startValue = transY;
                final float endValue = 0;
                // Ensures the velocity is always aligned with the direction.
                        float pixelPerSecond = Math.abs(dpPerSecond)
                                * Math.signum(endValue - transY);
                float pixelPerSecond = Math.abs(dpPerSecond) * Math.signum(endValue - transY);

                ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
                        .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
@@ -212,20 +232,55 @@ public class LauncherSwipeHandlerV2 extends
                }
            }
        };
            } else {
                homeAnimFactory = new LauncherHomeAnimationFactory();
    }
        } else {
            homeAnimFactory = new HomeAnimationFactory() {

    private HomeAnimationFactory createWidgetHomeAnimationFactory(
            LauncherAppWidgetHostView hostView) {

        RectF backgroundLocation = new RectF();
        Rect crop = new Rect();
        mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
        Size windowSize = new Size(crop.width(), crop.height());
        FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
                hostView, backgroundLocation, windowSize,
                mTaskViewSimulator.getCurrentCornerRadius());

        return new LauncherHomeAnimationFactory() {

            @Override
                public AnimatorPlaybackController createActivityAnimationToHome() {
                    return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
            public RectF getWindowTargetRect() {
                return backgroundLocation;
            }
            };
            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                    isPresent -> mRecentsView.startHome());

            @Override
            public float getEndRadius(RectF cropRectF) {
                return floatingWidgetView.getInitialCornerRadius();
            }

            @Override
            public void setAnimation(RectFSpringAnim anim) {
                anim.addAnimatorListener(floatingWidgetView);
                floatingWidgetView.setFastFinishRunnable(anim::end);
            }

            @Override
            public boolean keepWindowOpaque() {
                return false;
            }

            @Override
            public void update(@Nullable AppCloseConfig config, RectF currentRect,
                    float progress, float radius) {
                floatingWidgetView.update(currentRect, 1 /* floatingWidgetAlpha */,
                        config != null ? config.getFgAlpha() : 1f /* foregroundAlpha */,
                        0 /* fallbackBackgroundAlpha */, 1 - progress);
            }
        return homeAnimFactory;

            @Override
            public void onCancel() {
                floatingWidgetView.fastFinish();
            }
        };
    }

    /**
+8 −4
Original line number Diff line number Diff line
@@ -145,6 +145,11 @@ public abstract class SwipeUpAnimationLogic {
                    targetX + halfIconSize, targetY + halfIconSize);
        }

        /** Returns the corner radius of the window at the end of the animation. */
        public float getEndRadius(RectF cropRectF) {
            return cropRectF.width() / 2f;
        }

        public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();

        public void playAtomicAnimation(float velocity) {
@@ -197,8 +202,7 @@ public abstract class SwipeUpAnimationLogic {
        final RectF targetRect = homeAnimationFactory.getWindowTargetRect();

        Matrix homeToWindowPositionMap = new Matrix();
        final RectF startRect = updateProgressForStartRect(
                homeToWindowPositionMap, startProgress);
        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());

        // Move the startRect to Launcher space as floatingIconView runs in Launcher
@@ -210,7 +214,7 @@ public abstract class SwipeUpAnimationLogic {
        if (PROTOTYPE_APP_CLOSE.get()) {
            anim = new RectFSpringAnim2(startRect, targetRect, mContext,
                    mTaskViewSimulator.getCurrentCornerRadius(),
                    cropRectF.width() / 2f);
                    homeAnimationFactory.getEndRadius(cropRectF));
        } else {
            anim = new RectFSpringAnim(startRect, targetRect, mContext);
        }
@@ -269,7 +273,7 @@ public abstract class SwipeUpAnimationLogic {
            // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
            // rounding at the end of the animation.
            mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
            mEndRadius = cropRectF.width() / 2f;
            mEndRadius = factory.getEndRadius(cropRectF);
        }

        @Override
Loading