Show splash screen in quickswitch if thumbnail wrong aspect/orientation.
Test: manual
Bug: 202826469
Change-Id: I8337876ba26bf82d4f65697a86e6727be713ad53
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 17a48a7..fa4eaed 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -36,6 +36,7 @@
<dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
<dimen name="overview_task_margin">16dp</dimen>
<dimen name="overview_page_spacing">16dp</dimen>
+ <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
<item name="overview_max_scale" format="float" type="dimen">0.7</item>
<item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 84b3839..871ddc6 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.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 @@
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 @@
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 9ca5fc5..dc60875 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -263,7 +263,8 @@
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 f68bbbc..c7c3441 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.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 @@
boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
showAsGrid ? INSTANT : FINAL_FRAME);
+ setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, state == RecentsState.DEFAULT ? 1f : 0f,
+ INSTANT);
setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index cb88068..2dff18e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -313,4 +314,11 @@
mSnapshotView2.setDimAlpha(amount);
mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
}
+
+ @Override
+ protected void applyThumbnailSplashAlpha() {
+ super.applyThumbnailSplashAlpha();
+ mSnapshotView2.setSplashAlpha(
+ Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 274a691..583d097 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -363,6 +363,10 @@
}
};
+ /**
+ * Progress of Recents view from carousel layout to grid layout. If Recents is not shown as a
+ * grid, then the value remains 0.
+ */
public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
new FloatProperty<RecentsView>("recentsGrid") {
@Override
@@ -376,6 +380,23 @@
}
};
+ /**
+ * Progress to and from the OVERVIEW state, where being in OverviewState has a value of 1, and
+ * being in any other LauncherState has a value of 0.
+ */
+ public static final FloatProperty<RecentsView> OVERVIEW_PROGRESS =
+ new FloatProperty<RecentsView>("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 @@
protected float mTaskViewsSecondarySplitTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0;
+ private float mOverviewProgress = 0;
private boolean mShowAsGridLastOnLayout = false;
private final IntSet mTopRowIdSet = new IntSet();
@@ -2661,6 +2683,18 @@
mClearAllButton.setGridProgress(gridProgress);
}
+ private void setOverviewProgress(float overviewProgress) {
+ int taskCount = getTaskViewCount();
+ if (taskCount == 0) {
+ return;
+ }
+
+ mOverviewProgress = overviewProgress;
+ for (int i = 0; i < taskCount; i++) {
+ requireTaskViewAt(i).setOverviewProgress(overviewProgress);
+ }
+ }
+
private void enableLayoutTransitions() {
if (mLayoutTransition == null) {
mLayoutTransition = new LayoutTransition();
@@ -4301,6 +4335,7 @@
properties));
}
}
+ anim.play(ObjectAnimator.ofFloat(recentsView, OVERVIEW_PROGRESS, 1, 0));
return anim;
}
@@ -4360,6 +4395,8 @@
BACKGROUND_APP.getDepth(mActivity));
anim.play(depthAnimator);
}
+ anim.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1f, 0f));
+
anim.play(progressAnim);
anim.setInterpolator(interpolator);
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 32dc4d8..81c21ab 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -36,12 +36,14 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.Surface;
import android.view.View;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -50,6 +52,7 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
@@ -64,6 +67,7 @@
public class TaskThumbnailView extends View {
private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
new MainThreadInitializedObject<>(FullscreenDrawParams::new);
+ private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -83,6 +87,7 @@
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 @@
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 @@
/** 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 @@
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 @@
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 @@
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 @@
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);
@@ -264,6 +291,12 @@
}
@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;
canvas.save();
@@ -313,6 +346,17 @@
}
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() {
@@ -328,6 +372,78 @@
}
/**
+ * Determine if the splash should be shown over top of the thumbnail.
+ *
+ * <p>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.
*/
@@ -476,8 +592,9 @@
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 68e9f5a..2427628 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,6 +18,7 @@
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;
@@ -361,6 +362,7 @@
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;
@@ -420,7 +422,6 @@
private boolean mIsClickableAsLiveTile = true;
-
public TaskView(Context context) {
this(context, null);
}
@@ -688,6 +689,9 @@
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)) {
@@ -1088,6 +1092,21 @@
return scale;
}
+ /**
+ * Updates progress of task view for entering/exiting overview on swipe up/down.
+ *
+ * <p>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();