diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 17a48a7f14..fa4eaedc88 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -36,6 +36,7 @@ 44dp 16dp 16dp + 72dp 0.7 1.1 diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 84b3839825..871ddc6edf 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -28,6 +28,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TR import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.launcher3.testing.TestProtocol.BAD_STATE; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; +import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; @@ -74,6 +75,7 @@ public abstract class BaseRecentsViewStateController getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness()); RECENTS_GRID_PROGRESS.set(mRecentsView, state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f); + OVERVIEW_PROGRESS.set(mRecentsView, state == LauncherState.OVERVIEW ? 1f : 0f); } @Override @@ -117,6 +119,9 @@ public abstract class BaseRecentsViewStateController boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()); setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f, showAsGrid ? INSTANT : FINAL_FRAME); + + setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, + toState == LauncherState.OVERVIEW ? 1f : 0f, INSTANT); } abstract FloatProperty getTaskModalnessProperty(); diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 9ca5fc5fb7..dc60875db8 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -263,7 +263,8 @@ public class TaskIconCache implements DisplayInfoChangeListener { if (mIconFactory == null) { mIconFactory = new BaseIconFactory(mContext, DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(), - mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size)); + mContext.getResources().getDimensionPixelSize( + R.dimen.task_icon_cache_default_icon_size)); } return mIconFactory; } diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index f68bbbce62..c7c3441acf 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -27,6 +27,7 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; @@ -105,6 +106,8 @@ public class FallbackRecentsStateController implements StateHandler RECENTS_GRID_PROGRESS = new FloatProperty("recentsGrid") { @Override @@ -376,6 +380,23 @@ public abstract class RecentsView OVERVIEW_PROGRESS = + new FloatProperty("overviewProgress") { + @Override + public void setValue(RecentsView view, float overviewProgress) { + view.setOverviewProgress(overviewProgress); + } + + @Override + public Float get(RecentsView view) { + return view.mOverviewProgress; + } + }; + // OverScroll constants private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270; @@ -466,6 +487,7 @@ public abstract class RecentsView TEMP_PARAMS = new MainThreadInitializedObject<>(FullscreenDrawParams::new); + private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f; public static final Property DIM_ALPHA = new FloatProperty("dimAlpha") { @@ -83,6 +87,7 @@ public class TaskThumbnailView extends View { private TaskOverlay mOverlay; private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mClearPaint = new Paint(); private final Paint mDimmingPaintAfterClearing = new Paint(); private final int mDimColor; @@ -91,6 +96,8 @@ public class TaskThumbnailView extends View { private final Rect mPreviewRect = new Rect(); private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper(); private TaskView.FullscreenDrawParams mFullscreenParams; + private ImageView mSplashView; + private Drawable mSplashViewDrawable; @Nullable private Task mTask; @@ -101,6 +108,8 @@ public class TaskThumbnailView extends View { /** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */ private float mDimAlpha = 0f; + /** Controls visibility of the splash view, 0 is transparent, 255 fully opaque. */ + private int mSplashAlpha = 0; private boolean mOverlayEnabled; @@ -116,6 +125,7 @@ public class TaskThumbnailView extends View { super(context, attrs, defStyleAttr); mPaint.setFilterBitmap(true); mBackgroundPaint.setColor(Color.WHITE); + mSplashBackgroundPaint.setColor(Color.WHITE); mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); mActivity = BaseActivity.fromContext(context); // Initialize with placeholder value. It is overridden later by TaskView @@ -135,6 +145,8 @@ public class TaskThumbnailView extends View { int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000; mPaint.setColor(color); mBackgroundPaint.setColor(color); + mSplashBackgroundPaint.setColor(color); + updateSplashView(mTask.icon); } /** @@ -152,6 +164,9 @@ public class TaskThumbnailView extends View { boolean thumbnailWasNull = mThumbnailData == null; mThumbnailData = (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null; + if (mTask != null) { + updateSplashView(mTask.icon); + } if (refreshNow) { refresh(thumbnailWasNull && mThumbnailData != null); } @@ -202,6 +217,18 @@ public class TaskThumbnailView extends View { updateThumbnailPaintFilter(); } + /** + * Sets the alpha of the splash view. + */ + public void setSplashAlpha(float splashAlpha) { + mSplashAlpha = (int) (splashAlpha * 255); + if (mSplashViewDrawable != null) { + mSplashViewDrawable.setAlpha(mSplashAlpha); + } + mSplashBackgroundPaint.setAlpha(mSplashAlpha); + invalidate(); + } + public TaskOverlay getTaskOverlay() { if (mOverlay == null) { mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this); @@ -259,6 +286,12 @@ public class TaskThumbnailView extends View { return 0; } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateSplashView(mSplashViewDrawable); + } + @Override protected void onDraw(Canvas canvas) { RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets; @@ -309,6 +342,17 @@ public class TaskThumbnailView extends View { } canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint); + + // Draw splash above thumbnail to hide inconsistencies in rotation and aspect ratios. + if (shouldShowSplashView()) { + if (mSplashView != null) { + canvas.drawRoundRect(x, y, width + 1, height + 1, cornerRadius, + cornerRadius, mSplashBackgroundPaint); + + mSplashView.layout((int) x, (int) (y + 1), (int) width, (int) height - 1); + mSplashView.draw(canvas); + } + } } public TaskView getTaskView() { @@ -323,6 +367,78 @@ public class TaskThumbnailView extends View { } } + /** + * Determine if the splash should be shown over top of the thumbnail. + * + *

We want to show the splash if the aspect ratio or rotation of the thumbnail would be + * different from the task. + */ + boolean shouldShowSplashView() { + return isThumbnailAspectRatioDifferentFromThumbnailData() + || isThumbnailRotationDifferentFromTask(); + } + + private void updateSplashView(Drawable icon) { + if (icon == null || icon.getConstantState() == null) { + return; + } + mSplashViewDrawable = icon.getConstantState().newDrawable().mutate(); + mSplashViewDrawable.setAlpha(mSplashAlpha); + ImageView imageView = mSplashView == null ? new ImageView(getContext()) : mSplashView; + imageView.setImageDrawable(mSplashViewDrawable); + + imageView.setScaleType(ImageView.ScaleType.MATRIX); + Matrix matrix = new Matrix(); + + float drawableWidth = mSplashViewDrawable.getIntrinsicWidth(); + float drawableHeight = mSplashViewDrawable.getIntrinsicHeight(); + float viewWidth = getMeasuredWidth(); + float viewCenterX = viewWidth / 2f; + float viewHeight = getMeasuredHeight(); + float viewCenterY = viewHeight / 2f; + float centeredDrawableLeft = (viewWidth - drawableWidth) / 2f; + float centeredDrawableTop = (viewHeight - drawableHeight) / 2f; + float nonGridScale = getTaskView() == null ? 1 : 1 / getTaskView().getNonGridScale(); + float recentsMaxScale = getTaskView() == null || getTaskView().getRecentsView() == null + ? 1 : 1 / getTaskView().getRecentsView().getMaxScaleForFullScreen(); + float scale = nonGridScale * recentsMaxScale; + + // Center the image in the view. + matrix.setTranslate(centeredDrawableLeft, centeredDrawableTop); + // Apply scale transformation after translation, pivoting around center of view. + matrix.postScale(scale, scale, viewCenterX, viewCenterY); + + imageView.setImageMatrix(matrix); + mSplashView = imageView; + } + + private boolean isThumbnailAspectRatioDifferentFromThumbnailData() { + if (mThumbnailData == null || mThumbnailData.thumbnail == null) { + return false; + } + + float thumbnailViewAspect = getWidth() / (float) getHeight(); + float thumbnailDataAspect = + mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight(); + + return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect, + thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT); + } + + private boolean isThumbnailRotationDifferentFromTask() { + RecentsView recents = getTaskView().getRecentsView(); + if (recents == null || mThumbnailData == null) { + return false; + } + + if (recents.getPagedOrientationHandler() == PagedOrientationHandler.PORTRAIT) { + int currentRotation = recents.getPagedViewOrientedState().getRecentsActivityRotation(); + return (currentRotation - mThumbnailData.rotation) % 2 != 0; + } else { + return recents.getPagedOrientationHandler().getRotation() != mThumbnailData.rotation; + } + } + /** * Potentially re-init the task overlay. Be cautious when calling this as the overlay may * do processing on initialization. @@ -463,8 +579,9 @@ public class TaskThumbnailView extends View { float availableAspect = isRotated ? availableHeight / availableWidth : availableWidth / availableHeight; - boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan( - canvasAspect, availableAspect, 0.1f); + boolean isAspectLargelyDifferent = + Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect, + availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT); if (isRotated && isAspectLargelyDifferent) { // Do not rotate thumbnail if it would not improve fit isRotated = false; diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 1bc538614b..ded0ea620b 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -18,6 +18,7 @@ package com.android.quickstep.views; import static android.view.Display.DEFAULT_DISPLAY; import static android.widget.Toast.LENGTH_SHORT; +import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR; import static com.android.launcher3.Utilities.comp; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; @@ -333,6 +334,7 @@ public class TaskView extends FrameLayout implements Reusable { protected final DigitalWellBeingToast mDigitalWellBeingToast; private float mFullscreenProgress; private float mGridProgress; + protected float mOverviewProgress; private float mNonGridScale = 1; private float mDismissScale = 1; protected final FullscreenDrawParams mCurrentFullscreenParams; @@ -392,7 +394,6 @@ public class TaskView extends FrameLayout implements Reusable { private boolean mIsClickableAsLiveTile = true; - public TaskView(Context context) { this(context, null); } @@ -660,6 +661,9 @@ public class TaskView extends FrameLayout implements Reusable { if (freezeTaskList) { ActivityOptionsCompat.setFreezeRecentTasksList(opts); } + // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow. + opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView() + ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle()); Task.TaskKey key = mTask.key; UI_HELPER_EXECUTOR.execute(() -> { if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) { @@ -1060,6 +1064,21 @@ public class TaskView extends FrameLayout implements Reusable { return scale; } + /** + * Updates progress of task view for entering/exiting overview on swipe up/down. + * + *

Updates the alpha of any splash screen over the thumbnail if it exists. + */ + public void setOverviewProgress(float overviewProgress) { + mOverviewProgress = overviewProgress; + applyThumbnailSplashAlpha(); + } + + protected void applyThumbnailSplashAlpha() { + mSnapshotView.setSplashAlpha( + Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR)); + } + private void setSplitSelectTranslationX(float x) { mSplitSelectTranslationX = x; applyTranslationX();