diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 43d03b3cc6..537add5b1f 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -213,8 +213,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } }; + // Pairs of window starting type and starting window background color for starting tasks // Will never be larger than MAX_NUM_TASKS - private LinkedHashMap mTypeForTaskId; + private LinkedHashMap> mTaskStartParams; public QuickstepTransitionManager(Context context) { mLauncher = Launcher.cast(Launcher.getLauncher(context)); @@ -231,9 +232,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mLauncher.addOnDeviceProfileChangeListener(this); if (supportsSSplashScreen()) { - mTypeForTaskId = new LinkedHashMap(MAX_NUM_TASKS) { + mTaskStartParams = new LinkedHashMap>(MAX_NUM_TASKS) { @Override - protected boolean removeEldestEntry(Entry entry) { + protected boolean removeEldestEntry(Entry> entry) { return size() > MAX_NUM_TASKS; } }; @@ -419,15 +420,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener return bounds; } - private int getOpeningTaskId(RemoteAnimationTargetCompat[] appTargets) { - for (RemoteAnimationTargetCompat target : appTargets) { - if (target.mode == MODE_OPENING) { - return target.taskId; - } - } - return -1; - } - public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider, CancellationSignal cancellationSignal) { mRemoteAnimationProvider = animationProvider; @@ -594,10 +586,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener final boolean hasSplashScreen; if (supportsSSplashScreen()) { - int taskId = getOpeningTaskId(appTargets); - int type = mTypeForTaskId.getOrDefault(taskId, STARTING_WINDOW_TYPE_NONE); - mTypeForTaskId.remove(taskId); - hasSplashScreen = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN; + int taskId = openingTargets.getFirstAppTargetTaskId(); + Pair defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0); + Pair taskParams = + mTaskStartParams.getOrDefault(taskId, defaultParams); + mTaskStartParams.remove(taskId); + hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else { hasSplashScreen = false; } @@ -798,18 +792,30 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener final RectF widgetBackgroundBounds = new RectF(); final Rect appWindowCrop = new Rect(); final Matrix matrix = new Matrix(); + RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets, + wallpaperTargets, nonAppTargets, MODE_OPENING); + + RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget(); + int fallbackBackgroundColor = 0; + if (openingTarget != null && supportsSSplashScreen()) { + fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId) + ? mTaskStartParams.get(openingTarget.taskId).second : 0; + mTaskStartParams.remove(openingTarget.taskId); + } + if (fallbackBackgroundColor == 0) { + fallbackBackgroundColor = + FloatingWidgetView.getDefaultBackgroundColor(mLauncher, openingTarget); + } final float finalWindowRadius = mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher.getResources()); final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher, v, widgetBackgroundBounds, new Size(windowTargetBounds.width(), windowTargetBounds.height()), - finalWindowRadius, appTargetsAreTranslucent); + finalWindowRadius, appTargetsAreTranslucent, fallbackBackgroundColor); final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources()) ? floatingView.getInitialCornerRadius() : 0; - RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets, - wallpaperTargets, nonAppTargets, MODE_OPENING); SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView); openingTargets.addReleaseCheck(surfaceApplier); @@ -1441,8 +1447,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } @Override - public void onTaskLaunching(int taskId, int supportedType) { - mTransitionManager.mTypeForTaskId.put(taskId, supportedType); + public void onTaskLaunching(int taskId, int supportedType, int color) { + mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color)); } } } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 88db2741ab..52f34d90e0 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1071,7 +1071,8 @@ public abstract class AbsSwipeUpHandler, } protected abstract HomeAnimationFactory createHomeAnimationFactory( - ArrayList launchCookies, long duration, boolean isTargetTranslucent); + ArrayList launchCookies, long duration, boolean isTargetTranslucent, + RemoteAnimationTargetCompat runningTaskTarget); private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() { @Override @@ -1117,7 +1118,7 @@ public abstract class AbsSwipeUpHandler, : new ArrayList<>(); boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent; HomeAnimationFactory homeAnimFactory = - createHomeAnimationFactory(cookies, duration, isTranslucent); + createHomeAnimationFactory(cookies, duration, isTranslucent, runningTaskTarget); mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && runningTaskTarget != null && runningTaskTarget.taskInfo.pictureInPictureParams != null diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index 2d81429a94..7290ff6a41 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -129,7 +129,8 @@ public class FallbackSwipeHandler extends @Override protected HomeAnimationFactory createHomeAnimationFactory(ArrayList launchCookies, - long duration, boolean isTargetTranslucent) { + long duration, boolean isTargetTranslucent, + RemoteAnimationTargetCompat runningTaskTarget) { mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); Intent intent = new Intent(mGestureState.getHomeIntent()); diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 1bae1c5f6b..40741e4945 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -20,6 +20,9 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.Utilities.boundToRange; import static com.android.launcher3.Utilities.dpToPx; +import static com.android.launcher3.Utilities.mapBoundToRange; +import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE; +import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; @@ -65,6 +68,7 @@ import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.system.InputConsumerController; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.ArrayList; @@ -84,7 +88,8 @@ public class LauncherSwipeHandlerV2 extends @Override protected HomeAnimationFactory createHomeAnimationFactory(ArrayList launchCookies, - long duration, boolean isTargetTranslucent) { + long duration, boolean isTargetTranslucent, + RemoteAnimationTargetCompat runningTaskTarget) { if (mActivity == null) { mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, isPresent -> mRecentsView.startHome()); @@ -108,7 +113,7 @@ public class LauncherSwipeHandlerV2 extends } if (workspaceView instanceof LauncherAppWidgetHostView) { return createWidgetHomeAnimationFactory((LauncherAppWidgetHostView) workspaceView, - isTargetTranslucent); + isTargetTranslucent, runningTaskTarget); } return createIconHomeAnimationFactory(workspaceView); } @@ -169,15 +174,19 @@ public class LauncherSwipeHandlerV2 extends } private HomeAnimationFactory createWidgetHomeAnimationFactory( - LauncherAppWidgetHostView hostView, boolean isTargetTranslucent) { - + LauncherAppWidgetHostView hostView, boolean isTargetTranslucent, + RemoteAnimationTargetCompat runningTaskTarget) { + final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1; RectF backgroundLocation = new RectF(); Rect crop = new Rect(); mTaskViewSimulator.getCurrentCropRect().roundOut(crop); Size windowSize = new Size(crop.width(), crop.height()); + int fallbackBackgroundColor = + FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget); FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity, hostView, backgroundLocation, windowSize, - mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent); + mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent, + fallbackBackgroundColor); return new FloatingViewHomeAnimationFactory(floatingWidgetView) { @@ -207,12 +216,20 @@ public class LauncherSwipeHandlerV2 extends } @Override - public void update(@Nullable AppCloseConfig config, RectF currentRect, - float progress, float radius) { + public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress, + float radius) { super.update(config, currentRect, progress, radius); - floatingWidgetView.update(currentRect, 1 /* floatingWidgetAlpha */, - config != null ? config.getFgAlpha() : 1f /* foregroundAlpha */, - 0 /* fallbackBackgroundAlpha */, 1 - 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); + floatingWidgetView.update(currentRect, floatingWidgetAlpha, foregroundAlpha, + fallbackBackgroundAlpha, 1 - progress); + } + + @Override + protected float getWindowAlpha(float progress) { + return 1 - mapBoundToRange(progress, 0, 0.5f, 0, 1, LINEAR); } }; } diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index edc3ab22f3..c032889db5 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -85,6 +85,17 @@ public class RemoteAnimationTargets { return null; } + /** Returns the first opening app target. */ + public RemoteAnimationTargetCompat getFirstAppTarget() { + return apps.length > 0 ? apps[0] : null; + } + + /** Returns the task id of the first opening app target, or -1 if none is found. */ + public int getFirstAppTargetTaskId() { + RemoteAnimationTargetCompat target = getFirstAppTarget(); + return target == null ? -1 : target.taskId; + } + public boolean isAnimatingHome() { for (RemoteAnimationTargetCompat target : unfilteredApps) { if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) { diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index b79e9344bf..44954555b0 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -181,6 +181,24 @@ public abstract class SwipeUpAnimationLogic { public boolean supportSwipePipToHome() { return false; } + + /** + * @param progress The progress of the animation to the home screen. + * @return The current alpha to set on the animating app window. + */ + 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); + } } /** @@ -236,24 +254,6 @@ public abstract class SwipeUpAnimationLogic { return anim; } - /** - * @param progress The progress of the animation to the home screen. - * @return The current alpha to set on the animating app window. - */ - 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); - } - protected class SpringAnimationRunner extends AnimationSuccessListener implements RectFSpringAnim.OnUpdateListener, BuilderProxy { @@ -292,7 +292,7 @@ public abstract class SwipeUpAnimationLogic { mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); - float alpha = getWindowAlpha(progress); + float alpha = mAnimationFactory.getWindowAlpha(progress); if (config != null && PROTOTYPE_APP_CLOSE.get()) { alpha = config.getWindowAlpha(); cornerRadius = config.getCornerRadius(); diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java index 9ea2369836..65dba338f8 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java @@ -27,7 +27,6 @@ import android.view.View; import android.view.ViewOutlineProvider; import android.widget.RemoteViews.RemoteViewOutlineProvider; -import com.android.launcher3.util.Themes; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.RoundedCornerEnforcement; @@ -62,7 +61,8 @@ final class FloatingWidgetBackgroundView extends View { setClipToOutline(true); } - void init(LauncherAppWidgetHostView hostView, View backgroundView, float finalRadius) { + void init(LauncherAppWidgetHostView hostView, View backgroundView, float finalRadius, + int fallbackBackgroundColor) { mFinalRadius = finalRadius; mSourceView = backgroundView; mInitialOutlineRadius = getOutlineRadius(hostView, backgroundView); @@ -81,7 +81,7 @@ final class FloatingWidgetBackgroundView extends View { setBackground(mBackgroundProperties.mDrawable); mSourceView.setBackground(null); } else if (mOriginalForeground == null) { - mFallbackDrawable.setColor(Themes.getColorBackground(backgroundView.getContext())); + mFallbackDrawable.setColor(fallbackBackgroundColor); setBackground(mFallbackDrawable); mIsUsingFallback = true; } diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java index 0012dd805d..22ce9421f7 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java @@ -34,10 +34,12 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.util.Themes; import com.android.launcher3.views.FloatingView; import com.android.launcher3.views.ListenerView; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.RoundedCornerEnforcement; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** A view that mimics an App Widget through a launch animation. */ @TargetApi(Build.VERSION_CODES.S) @@ -148,7 +150,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, private void init(DragLayer dragLayer, LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius, - boolean appTargetIsTranslucent) { + boolean appTargetIsTranslucent, int fallbackBackgroundColor) { mAppWidgetView = originalView; mAppWidgetView.beginDeferringUpdates(); mBackgroundPosition = widgetBackgroundPosition; @@ -163,7 +165,8 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, getRelativePosition(mAppWidgetBackgroundView, dragLayer, mBackgroundPosition); getRelativePosition(mAppWidgetBackgroundView, mAppWidgetView, mBackgroundOffset); if (!mAppTargetIsTranslucent) { - mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius); + mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius, + fallbackBackgroundColor); // Layout call before GhostView creation so that the overlaid view isn't clipped layout(0, 0, windowSize.getWidth(), windowSize.getHeight()); mForegroundOverlayView = GhostView.addGhost(mAppWidgetView, this); @@ -274,7 +277,8 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, */ public static FloatingWidgetView getFloatingWidgetView(Launcher launcher, LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition, - Size windowSize, float windowCornerRadius, boolean appTargetsAreTranslucent) { + Size windowSize, float windowCornerRadius, boolean appTargetsAreTranslucent, + int fallbackBackgroundColor) { final DragLayer dragLayer = launcher.getDragLayer(); ViewGroup parent = (ViewGroup) dragLayer.getParent(); FloatingWidgetView floatingView = @@ -282,11 +286,22 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, floatingView.recycle(); floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowSize, - windowCornerRadius, appTargetsAreTranslucent); + windowCornerRadius, appTargetsAreTranslucent, fallbackBackgroundColor); parent.addView(floatingView); return floatingView; } + /** + * Extract a background color from a target's task description, or fall back to the given + * context's theme background color. + */ + public static int getDefaultBackgroundColor( + Context context, RemoteAnimationTargetCompat target) { + return (target != null && target.taskInfo.taskDescription != null) + ? target.taskInfo.taskDescription.getBackgroundColor() + : Themes.getColorBackground(context); + } + 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, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index becbb27fb9..2ef9e3ea30 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -398,6 +398,13 @@ public final class Utilities { return mapRange(interpolator.getInterpolation(progress), toMin, toMax); } + /** Bounds t between a lower and upper bound and maps the result to a range. */ + public static float mapBoundToRange(float t, float lowerBound, float upperBound, + float toMin, float toMax, Interpolator interpolator) { + return mapToRange(boundToRange(t, lowerBound, upperBound), lowerBound, upperBound, + toMin, toMax, interpolator); + } + public static float getProgress(float current, float min, float max) { return Math.abs(current - min) / Math.abs(max - min); }