diff options
Diffstat (limited to 'quickstep/src/com/android/launcher3/QuickstepTransitionManager.java')
-rw-r--r-- | quickstep/src/com/android/launcher3/QuickstepTransitionManager.java | 432 |
1 files changed, 403 insertions, 29 deletions
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index b557779a26..6e2fadd7d3 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -28,18 +28,24 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.Utilities.mapBoundToRange; import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; +import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION; import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH; import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS; +import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.statehandlers.DepthController.DEPTH; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; -import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; +import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; @@ -51,20 +57,24 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.CancellationSignal; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.SystemProperties; +import android.os.UserHandle; import android.util.Pair; import android.util.Size; import android.view.SurfaceControl; @@ -86,8 +96,12 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.statehandlers.DepthController; +import com.android.launcher3.taskbar.LauncherTaskbarUIController; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.ActivityOptionsWrapper; +import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.launcher3.util.ObjectWrapper; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.Themes; import com.android.launcher3.views.FloatingIconView; @@ -97,7 +111,9 @@ import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskViewUtils; import com.android.quickstep.util.MultiValueUpdateListener; +import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RemoteAnimationProvider; +import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.WorkspaceRevealAnim; import com.android.quickstep.views.FloatingWidgetView; @@ -159,6 +175,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener public static final int RECENTS_LAUNCH_DURATION = 336; private static final int LAUNCHER_RESUME_START_DELAY = 100; private static final int CLOSING_TRANSITION_DURATION_MS = 250; + public static final int SPLIT_LAUNCH_DURATION = 370; + public static final int SPLIT_DIVIDER_ANIM_DURATION = 100; public static final int CONTENT_ALPHA_DURATION = 217; protected static final int CONTENT_SCALE_DURATION = 350; @@ -269,7 +287,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION - STATUS_BAR_TRANSITION_PRE_DELAY; RemoteAnimationAdapterCompat adapterCompat = - new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay); + new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay, + mLauncher.getIApplicationThread()); return new ActivityOptionsWrapper( ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback); } @@ -422,6 +441,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 4 - rotationChange); } } + if (mDeviceProfile.isTaskbarPresentInApps) { + // Animate to above the taskbar. + bounds.bottom -= target.contentInsets.bottom; + } return bounds; } @@ -506,7 +529,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get(); if (scrimEnabled) { - int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor); + boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps; + int scrimColor = useTaskbarColor + ? mLauncher.getResources().getColor(R.color.taskbar_background) + : Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor); int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0); int[] colors = isAppOpening ? new int[]{scrimColorTrans, scrimColor} @@ -519,6 +545,30 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener colors); scrim.setDuration(CONTENT_SCRIM_DURATION); scrim.setInterpolator(DEACCEL_1_5); + + if (useTaskbarColor) { + // Hide the taskbar background color since it would duplicate the scrim. + scrim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + LauncherTaskbarUIController taskbarUIController = + mLauncher.getTaskbarUIController(); + if (taskbarUIController != null) { + taskbarUIController.forceHideBackground(true); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + LauncherTaskbarUIController taskbarUIController = + mLauncher.getTaskbarUIController(); + if (taskbarUIController != null) { + taskbarUIController.forceHideBackground(false); + } + } + }); + } + launcherAnimator.play(scrim); } } @@ -611,10 +661,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile, windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1], hasSplashScreen, floatingView.isDifferentFromAppIcon()); - int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2); - int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2); - int right = (int) (left + prop.cropWidthStart); - int bottom = (int) (top + prop.cropHeightStart); + int left = prop.cropCenterXStart - prop.cropWidthStart / 2; + int top = prop.cropCenterYStart - prop.cropHeightStart / 2; + int right = left + prop.cropWidthStart; + int bottom = top + prop.cropHeightStart; // Set the crop here so we can calculate the corner radius below. crop.set(left, top, right, bottom); @@ -633,6 +683,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (v instanceof BubbleTextView) { ((BubbleTextView) v).setStayPressed(false); } + LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController(); + if (taskbarController != null) { + taskbarController.showEdu(); + } openingTargets.release(); } }); @@ -641,7 +695,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ? Math.max(crop.width(), crop.height()) / 2f : 0f; final float finalWindowRadius = mDeviceProfile.isMultiWindowMode - ? 0 : getWindowCornerRadius(mLauncher.getResources()); + ? 0 : getWindowCornerRadius(mLauncher); final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius; MultiValueUpdateListener listener = new MultiValueUpdateListener() { @@ -804,7 +858,13 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // Since we added a start delay, call update here to init the FloatingIconView properly. listener.onUpdate(0, true /* initOnly */); - animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets)); + // If app targets are translucent, do not animate the background as it causes a visible + // flicker when it resets itself at the end of its animation. + if (appTargetsAreTranslucent) { + animatorSet.play(appAnimator); + } else { + animatorSet.playTogether(appAnimator, getBackgroundAnimator()); + } return animatorSet; } @@ -832,7 +892,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } final float finalWindowRadius = mDeviceProfile.isMultiWindowMode - ? 0 : getWindowCornerRadius(mLauncher.getResources()); + ? 0 : getWindowCornerRadius(mLauncher); final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher, v, widgetBackgroundBounds, new Size(windowTargetBounds.width(), windowTargetBounds.height()), @@ -941,11 +1001,20 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } }); - animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets)); + // If app targets are translucent, do not animate the background as it causes a visible + // flicker when it resets itself at the end of its animation. + if (appTargetsAreTranslucent) { + animatorSet.play(appAnimator); + } else { + animatorSet.playTogether(appAnimator, getBackgroundAnimator()); + } return animatorSet; } - private ObjectAnimator getBackgroundAnimator(RemoteAnimationTargetCompat[] appTargets) { + /** + * Returns animator that controls depth/blur of the background. + */ + private ObjectAnimator getBackgroundAnimator() { // When launching an app from overview that doesn't map to a task, we still want to just // blur the wallpaper instead of the launcher surface as well boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW; @@ -1015,7 +1084,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener new RemoteAnimationAdapterCompat( new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner, false /* startAtFrontOfQueue */), - CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */)); + CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */, + mLauncher.getIApplicationThread())); if (KEYGUARD_ANIMATION.get()) { mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */); @@ -1025,7 +1095,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener new LauncherAnimationRunner( mHandler, mKeyguardGoingAwayRunner, true /* startAtFrontOfQueue */), - CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */)); + CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */, + mLauncher.getIApplicationThread())); } new ActivityCompat(mLauncher).registerRemoteAnimations(definition); @@ -1043,8 +1114,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */); mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition( new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner, - false /* startAtFrontOfQueue */)); - mLauncherOpenTransition.addHomeOpenCheck(); + false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread()); + mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName()); SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition); } } @@ -1085,7 +1156,16 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) { - return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode); + for (RemoteAnimationTargetCompat target : targets) { + if (target.mode == mode && target.taskInfo != null + // Compare component name instead of task-id because transitions will promote + // the target up to the root task while getTaskId returns the leaf. + && target.taskInfo.topActivity != null + && target.taskInfo.topActivity.equals(mLauncher.getComponentName())) { + return true; + } + } + return false; } /** @@ -1105,7 +1185,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1); unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS); float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 : - QuickStepContract.getWindowCornerRadius(mLauncher.getResources()); + QuickStepContract.getWindowCornerRadius(mLauncher); unlockAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -1135,10 +1215,181 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } /** - * Animator that controls the transformations of the windows the targets that are closing. + * Returns view on launcher that corresponds to the closing app in the list of app targets */ - private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets) { + private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) { + for (RemoteAnimationTargetCompat appTarget : appTargets) { + if (appTarget.mode == MODE_CLOSING) { + View launcherView = findLauncherView(appTarget); + if (launcherView != null) { + return launcherView; + } + } + } + return null; + } + + /** + * Returns view on launcher that corresponds to the {@param runningTaskTarget}. + */ + private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) { + if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) { + return null; + } + + final ComponentName[] taskInfoActivities = new ComponentName[] { + runningTaskTarget.taskInfo.baseActivity, + runningTaskTarget.taskInfo.origActivity, + runningTaskTarget.taskInfo.realActivity, + runningTaskTarget.taskInfo.topActivity}; + + String packageName = null; + for (ComponentName component : taskInfoActivities) { + if (component != null && component.getPackageName() != null) { + packageName = component.getPackageName(); + break; + } + } + + if (packageName == null) { + return null; + } + + // Find the associated item info for the launch cookie (if available), note that predicted + // apps actually have an id of -1, so use another default id here + final ArrayList<IBinder> launchCookies = runningTaskTarget.taskInfo.launchCookies == null + ? new ArrayList<>() + : runningTaskTarget.taskInfo.launchCookies; + + int launchCookieItemId = NO_MATCHING_ID; + for (IBinder cookie : launchCookies) { + Integer itemId = ObjectWrapper.unwrap(cookie); + if (itemId != null) { + launchCookieItemId = itemId; + break; + } + } + + return mLauncher.getFirstMatchForAppClose(launchCookieItemId, + packageName, UserHandle.of(runningTaskTarget.taskInfo.userId)); + } + + private @NonNull RectF getDefaultWindowTargetRect() { + RecentsView recentsView = mLauncher.getOverviewPanel(); + PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler(); + DeviceProfile dp = mLauncher.getDeviceProfile(); + final int halfIconSize = dp.iconSizePx / 2; + float primaryDimension = orientationHandler + .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); + float secondaryDimension = orientationHandler + .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); + final float targetX = primaryDimension / 2f; + final float targetY = secondaryDimension - dp.hotseatBarSizePx; + return new RectF(targetX - halfIconSize, targetY - halfIconSize, + targetX + halfIconSize, targetY + halfIconSize); + } + + /** + * Closing animator that animates the window into its final location on the workspace. + */ + private void getClosingWindowAnimators(AnimatorSet animation, + RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) { + FloatingIconView floatingIconView = null; + FloatingWidgetView floatingWidget = null; + RectF targetRect = new RectF(); + + RemoteAnimationTargetCompat runningTaskTarget = null; + boolean isTransluscent = false; + for (RemoteAnimationTargetCompat target : targets) { + if (target.mode == MODE_CLOSING) { + runningTaskTarget = target; + isTransluscent = runningTaskTarget.isTranslucent; + break; + } + } + + // Get floating view and target rect. + if (launcherView instanceof LauncherAppWidgetHostView) { + Size windowSize = new Size(mDeviceProfile.availableWidthPx, + mDeviceProfile.availableHeightPx); + int fallbackBackgroundColor = + FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget); + floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher, + (LauncherAppWidgetHostView) launcherView, targetRect, windowSize, + mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher), + isTransluscent, fallbackBackgroundColor); + } else if (launcherView != null) { + floatingIconView = getFloatingIconView(mLauncher, launcherView, + true /* hideOriginal */, targetRect, false /* isOpening */); + } else { + targetRect.set(getDefaultWindowTargetRect()); + } + + final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx); + RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher, + mDeviceProfile); + + // Hook up floating views to the closing window animators. + final int rotationChange = getRotationChange(targets); + Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange); + if (floatingIconView != null) { + anim.addAnimatorListener(floatingIconView); + floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged); + floatingIconView.setFastFinishRunnable(anim::end); + FloatingIconView finalFloatingIconView = floatingIconView; + + // 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. + final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION; + + RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect, + windowTargetBounds) { + @Override + public void onUpdate(RectF currentRectF, float progress) { + finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress, + windowAlphaThreshold, getCornerRadius(progress), false); + + super.onUpdate(currentRectF, progress); + } + }; + anim.addOnUpdateListener(runner); + } else if (floatingWidget != null) { + anim.addAnimatorListener(floatingWidget); + floatingWidget.setOnTargetChangeListener(anim::onTargetPositionChanged); + floatingWidget.setFastFinishRunnable(anim::end); + + final float floatingWidgetAlpha = isTransluscent ? 0 : 1; + FloatingWidgetView finalFloatingWidget = floatingWidget; + RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect, + windowTargetBounds) { + @Override + public void onUpdate(RectF currentRectF, float progress) { + final float fallbackBackgroundAlpha = + 1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE); + final float foregroundAlpha = + mapBoundToRange(progress, 0.5f, 1, 0, 1, EXAGGERATED_EASE); + finalFloatingWidget.update(currentRectF, floatingWidgetAlpha, foregroundAlpha, + fallbackBackgroundAlpha, 1 - progress); + + super.onUpdate(currentRectF, progress); + } + }; + anim.addOnUpdateListener(runner); + } + + // Use a fixed velocity to start the animation. + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + anim.start(mLauncher, velocityPxPerS); + } + }); + } + + /** + * Closing window animator that moves the window down and offscreen. + */ + private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) { final int rotationChange = getRotationChange(appTargets); SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer); Matrix matrix = new Matrix(); @@ -1147,7 +1398,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1); int duration = CLOSING_TRANSITION_DURATION_MS; float windowCornerRadius = mDeviceProfile.isMultiWindowMode - ? 0 : getWindowCornerRadius(mLauncher.getResources()); + ? 0 : getWindowCornerRadius(mLauncher); float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius; closingAnimator.setDuration(duration); closingAnimator.addUpdateListener(new MultiValueUpdateListener() { @@ -1277,7 +1528,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener LauncherAnimationRunner.AnimationResult result) { if (mLauncher.isDestroyed()) { AnimatorSet anim = new AnimatorSet(); - anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets)); + anim.play(getFallbackClosingWindowAnimators(appTargets)); result.setAnimation(anim, mLauncher.getApplicationContext()); return; } @@ -1304,9 +1555,34 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (anim == null) { anim = new AnimatorSet(); - anim.play(mFromUnlock - ? getUnlockWindowAnimator(appTargets, wallpaperTargets) - : getClosingWindowAnimators(appTargets, wallpaperTargets)); + + final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible() + || launcherIsATargetWithMode(appTargets, MODE_OPENING); + + View launcherView = findLauncherView(appTargets); + boolean playFallBackAnimation = (launcherView == null + && launcherIsForceInvisibleOrOpening) + || mLauncher.getWorkspace().isOverlayShown(); + + boolean playWorkspaceReveal = true; + if (mFromUnlock) { + anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets)); + } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() + && !playFallBackAnimation) { + // Use a fixed velocity to start the animation. + float velocityPxPerS = DynamicResource.provider(mLauncher) + .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); + PointF velocity = new PointF(0, -velocityPxPerS); + getClosingWindowAnimators(anim, appTargets, launcherView, velocity); + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, + true /* animateOverviewScrim */, launcherView).getAnimators()); + // We play StaggeredWorkspaceAnim as a part of the closing window animation. + playWorkspaceReveal = false; + } + } else { + anim.play(getFallbackClosingWindowAnimators(appTargets)); + } // Normally, we run the launcher content animation when we are transitioning // home, but if home is already visible, then we don't want to animate the @@ -1316,8 +1592,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // targets list because it is already visible). In that case, we force // invisibility on touch down, and only reset it after the animation to home // is initialized. - if (launcherIsATargetWithMode(appTargets, MODE_OPENING) - || mLauncher.isForceInvisible()) { + if (launcherIsForceInvisibleOrOpening) { addCujInstrumentation( anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME); // Only register the content animation for cancellation when state changes @@ -1334,7 +1609,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } }); } else { - anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators()); + if (playWorkspaceReveal) { + anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators()); + } } } } @@ -1483,4 +1760,101 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color)); } } + + /** + * RectFSpringAnim update listener to be used for app to home animation. + */ + private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener { + private final RemoteAnimationTargetCompat[] mAppTargets; + private final Matrix mMatrix = new Matrix(); + private final Point mTmpPos = new Point(); + private final Rect mCurrentRect = new Rect(); + private final float mStartRadius; + private final float mEndRadius; + private final SurfaceTransactionApplier mSurfaceApplier; + private final Rect mWindowTargetBounds = new Rect(); + + private final Rect mTmpRect = new Rect(); + + SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect, + Rect windowTargetBounds) { + mAppTargets = appTargets; + mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher); + mEndRadius = Math.max(1, targetRect.width()) / 2f; + mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer); + mWindowTargetBounds.set(windowTargetBounds); + } + + public float getCornerRadius(float progress) { + return Utilities.mapRange(progress, mStartRadius, mEndRadius); + } + + @Override + public void onUpdate(RectF currentRectF, float progress) { + SurfaceParams[] params = new SurfaceParams[mAppTargets.length]; + for (int i = mAppTargets.length - 1; i >= 0; i--) { + RemoteAnimationTargetCompat target = mAppTargets[i]; + SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash); + + if (target.localBounds != null) { + mTmpPos.set(target.localBounds.left, target.localBounds.top); + } else { + mTmpPos.set(target.position.x, target.position.y); + } + + if (target.mode == MODE_CLOSING) { + currentRectF.round(mCurrentRect); + + // Scale the target window to match the currentRectF. + final float scale; + + // We need to infer the crop (we crop the window to match the currentRectF). + if (mWindowTargetBounds.height() > mWindowTargetBounds.width()) { + scale = Math.min(1f, currentRectF.width() / mWindowTargetBounds.width()); + + int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale)); + int croppedHeight = mWindowTargetBounds.height() - unscaledHeight; + mTmpRect.set(0, 0, mWindowTargetBounds.width(), + mWindowTargetBounds.height() - croppedHeight); + } else { + scale = Math.min(1f, currentRectF.height() / mWindowTargetBounds.height()); + + int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale)); + int croppedWidth = mWindowTargetBounds.width() - unscaledWidth; + mTmpRect.set(0, 0, mWindowTargetBounds.width() - croppedWidth, + mWindowTargetBounds.height()); + } + + // Match size and position of currentRect. + mMatrix.setScale(scale, scale); + mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top); + + builder.withMatrix(mMatrix) + .withWindowCrop(mTmpRect) + .withAlpha(getWindowAlpha(progress)) + .withCornerRadius(getCornerRadius(progress) / scale); + } else if (target.mode == MODE_OPENING) { + mMatrix.setTranslate(mTmpPos.x, mTmpPos.y); + builder.withMatrix(mMatrix) + .withAlpha(1f); + } + params[i] = builder.build(); + } + mSurfaceApplier.scheduleApply(params); + } + + protected float getWindowAlpha(float progress) { + // Alpha interpolates between [1, 0] between progress values [start, end] + final float start = 0f; + final float end = 0.85f; + + if (progress <= start) { + return 1f; + } + if (progress >= end) { + return 0f; + } + return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + } + } } |