summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/launcher3/QuickstepTransitionManager.java')
-rw-r--r--quickstep/src/com/android/launcher3/QuickstepTransitionManager.java432
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);
+ }
+ }
}