diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 910e473401..c6c6c01542 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -460,7 +460,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; diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index fb676459a3..36764a1519 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -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; @@ -780,7 +781,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; diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java index 66e4f4c61c..5c19ab8bf4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java @@ -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 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); } diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 2ea34d7f93..9398277032 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -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,155 +81,206 @@ public class LauncherSwipeHandlerV2 extends @Override protected HomeAnimationFactory createHomeAnimationFactory(ArrayList launchCookies, long duration) { - HomeAnimationFactory homeAnimFactory; - if (mActivity != null) { - final View workspaceView = findWorkspaceView(launchCookies, - mRecentsView.getRunningTaskView()); - boolean canUseWorkspaceView = - workspaceView != null && workspaceView.isAttachedToWindow(); - - mActivity.getRootView().setForceHideBackArrow(true); - mActivity.setHintUserWillBeActive(); - - if (canUseWorkspaceView) { - 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)); - final float launcherAlphaMax = - rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress); - - RectF iconLocation = new RectF(); - FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView, - true /* hideOriginal */, iconLocation, false /* isOpening */); - - // 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() { - - // There is a delay in loading the icon, so we need to keep the window - // opaque until it is ready. - private boolean mIsFloatingIconReady = false; - - private @Nullable ValueAnimator mBounceBackAnimator; - - @Override - public RectF getWindowTargetRect() { - if (PROTOTYPE_APP_CLOSE.get()) { - // We want the target rect to be at this offset position, so that all - // launcher content can spring back upwards. - floatingIconView.setPositionOffsetY(transY); - } - return iconLocation; - } - - @Override - public void setAnimation(RectFSpringAnim anim) { - anim.addAnimatorListener(floatingIconView); - floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged); - floatingIconView.setFastFinishRunnable(anim::end); - if (PROTOTYPE_APP_CLOSE.get()) { - mBounceBackAnimator = bounceBackToRestingPosition(); - // Use a spring to put drag layer translation back to 0. - anim.addAnimatorListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - floatingIconView.setPositionOffsetY(0); - mBounceBackAnimator.start(); - } - }); - - Workspace workspace = mActivity.getWorkspace(); - workspace.setPivotToScaleWithSelf(mActivity.getHotseat()); - } - } - - private ValueAnimator bounceBackToRestingPosition() { - DragLayer dl = mActivity.getDragLayer(); - Workspace workspace = mActivity.getWorkspace(); - Hotseat hotseat = mActivity.getHotseat(); - - 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); - - ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext()) - .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness)) - .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping)) - .setMinimumVisibleChange(1f) - .setStartValue(startValue) - .setEndValue(endValue) - .setStartVelocity(pixelPerSecond) - .build(dl, VIEW_TRANSLATE_Y); - springTransY.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - dl.setTranslationY(0f); - dl.setAlpha(1f); - SCALE_PROPERTY.set(workspace, 1f); - SCALE_PROPERTY.set(hotseat, 1f); - } - }); - return springTransY; - } - - @Override - public boolean keepWindowOpaque() { - if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) { - mIsFloatingIconReady = true; - return false; - } - return true; - } - - @Override - public void update(@Nullable AppCloseConfig config, RectF currentRect, - float progress, float radius) { - int fgAlpha = 255; - if (config != null && PROTOTYPE_APP_CLOSE.get()) { - DragLayer dl = mActivity.getDragLayer(); - float translationY = config.getWorkspaceTransY(); - dl.setTranslationY(translationY); - - float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR); - dl.setAlpha(Math.min(alpha, 1f)); - - float scale = Math.min(1f, config.getWorkspaceScale()); - SCALE_PROPERTY.set(mActivity.getWorkspace(), scale); - SCALE_PROPERTY.set(mActivity.getHotseat(), scale); - SCALE_PROPERTY.set(mActivity.getAppsView(), scale); - - progress = config.getInterpolatedProgress(); - fgAlpha = config.getFgAlpha(); - } - floatingIconView.update(1f, fgAlpha, currentRect, progress, - windowAlphaThreshold, radius, false); - } - - @Override - public void onCancel() { - floatingIconView.fastFinish(); - if (mBounceBackAnimator != null) { - mBounceBackAnimator.cancel(); - } - } - }; - } else { - homeAnimFactory = new LauncherHomeAnimationFactory(); - } - } else { - homeAnimFactory = new HomeAnimationFactory() { + 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); } }; - mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, - isPresent -> mRecentsView.startHome()); } - return homeAnimFactory; + + final View workspaceView = findWorkspaceView(launchCookies, + mRecentsView.getRunningTaskView()); + boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow(); + + mActivity.getRootView().setForceHideBackArrow(true); + mActivity.setHintUserWillBeActive(); + + 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)); + final float launcherAlphaMax = + rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress); + + RectF iconLocation = new RectF(); + FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView, + true /* hideOriginal */, iconLocation, false /* isOpening */); + + // 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; + return new LauncherHomeAnimationFactory() { + + // There is a delay in loading the icon, so we need to keep the window + // opaque until it is ready. + private boolean mIsFloatingIconReady = false; + + private @Nullable ValueAnimator mBounceBackAnimator; + + @Override + public RectF getWindowTargetRect() { + if (PROTOTYPE_APP_CLOSE.get()) { + // We want the target rect to be at this offset position, so that all + // launcher content can spring back upwards. + floatingIconView.setPositionOffsetY(transY); + } + return iconLocation; + } + + @Override + public void setAnimation(RectFSpringAnim anim) { + anim.addAnimatorListener(floatingIconView); + floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged); + floatingIconView.setFastFinishRunnable(anim::end); + if (PROTOTYPE_APP_CLOSE.get()) { + mBounceBackAnimator = bounceBackToRestingPosition(); + // Use a spring to put drag layer translation back to 0. + anim.addAnimatorListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + floatingIconView.setPositionOffsetY(0); + mBounceBackAnimator.start(); + } + }); + + Workspace workspace = mActivity.getWorkspace(); + workspace.setPivotToScaleWithSelf(mActivity.getHotseat()); + } + } + + private ValueAnimator bounceBackToRestingPosition() { + DragLayer dl = mActivity.getDragLayer(); + Workspace workspace = mActivity.getWorkspace(); + Hotseat hotseat = mActivity.getHotseat(); + + 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); + + ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext()) + .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness)) + .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping)) + .setMinimumVisibleChange(1f) + .setStartValue(startValue) + .setEndValue(endValue) + .setStartVelocity(pixelPerSecond) + .build(dl, VIEW_TRANSLATE_Y); + springTransY.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dl.setTranslationY(0f); + dl.setAlpha(1f); + SCALE_PROPERTY.set(workspace, 1f); + SCALE_PROPERTY.set(hotseat, 1f); + } + }); + return springTransY; + } + + @Override + public boolean keepWindowOpaque() { + if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) { + mIsFloatingIconReady = true; + return false; + } + return true; + } + + @Override + public void update(@Nullable AppCloseConfig config, RectF currentRect, + float progress, float radius) { + int fgAlpha = 255; + if (config != null && PROTOTYPE_APP_CLOSE.get()) { + DragLayer dl = mActivity.getDragLayer(); + float translationY = config.getWorkspaceTransY(); + dl.setTranslationY(translationY); + + float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR); + dl.setAlpha(Math.min(alpha, 1f)); + + float scale = Math.min(1f, config.getWorkspaceScale()); + SCALE_PROPERTY.set(mActivity.getWorkspace(), scale); + SCALE_PROPERTY.set(mActivity.getHotseat(), scale); + SCALE_PROPERTY.set(mActivity.getAppsView(), scale); + + progress = config.getInterpolatedProgress(); + fgAlpha = config.getFgAlpha(); + } + floatingIconView.update(1f, fgAlpha, currentRect, progress, + windowAlphaThreshold, radius, false); + } + + @Override + public void onCancel() { + floatingIconView.fastFinish(); + if (mBounceBackAnimator != null) { + mBounceBackAnimator.cancel(); + } + } + }; + } + + 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 RectF getWindowTargetRect() { + return backgroundLocation; + } + + @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); + } + + @Override + public void onCancel() { + floatingWidgetView.fastFinish(); + } + }; } /** diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 0f34a72ad4..29a00d16b0 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -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 diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java index d23884c9ef..8499902f23 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java @@ -20,10 +20,10 @@ import android.animation.Animator.AnimatorListener; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Matrix; -import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.util.AttributeSet; +import android.util.Size; import android.view.GhostView; import android.view.View; import android.view.ViewGroup; @@ -113,7 +113,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener } private void init(DragLayer dragLayer, LauncherAppWidgetHostView originalView, - RectF widgetBackgroundPosition, Rect windowTargetBounds, float windowCornerRadius) { + RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius) { mAppWidgetView = originalView; mAppWidgetView.beginDeferringUpdates(); mBackgroundPosition = widgetBackgroundPosition; @@ -128,7 +128,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener getRelativePosition(mAppWidgetBackgroundView, mAppWidgetView, mBackgroundOffset); mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius); // Layout call before GhostView creation so that the overlaid view isn't clipped - layout(0, 0, windowTargetBounds.width(), windowTargetBounds.height()); + layout(0, 0, windowSize.getWidth(), windowSize.getHeight()); mForegroundOverlayView = GhostView.addGhost(mAppWidgetView, this); positionViews(); @@ -219,19 +219,19 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener * * @param widgetBackgroundPosition a {@link RectF} that will be updated with the widget's * background bounds - * @param windowTargetBounds the bounds of the window when launched + * @param windowSize the size of the window when launched * @param windowCornerRadius the corner radius of the window */ public static FloatingWidgetView getFloatingWidgetView(Launcher launcher, LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition, - Rect windowTargetBounds, float windowCornerRadius) { + Size windowSize, float windowCornerRadius) { final DragLayer dragLayer = launcher.getDragLayer(); ViewGroup parent = (ViewGroup) dragLayer.getParent(); FloatingWidgetView floatingView = launcher.getViewCache().getView(R.layout.floating_widget_view, launcher, parent); floatingView.recycle(); - floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowTargetBounds, + floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowSize, windowCornerRadius); parent.addView(floatingView); return floatingView; @@ -240,7 +240,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener private static void getRelativePosition(View descendant, View ancestor, RectF position) { float[] points = new float[]{0, 0, descendant.getWidth(), descendant.getHeight()}; Utilities.getDescendantCoordRelativeToAncestor(descendant, ancestor, points, - false /* includeRootScroll */); + false /* includeRootScroll */, true /* ignoreTransform */); position.set( Math.min(points[0], points[2]), Math.min(points[1], points[3]),