diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 4cdf557e3e..c03aa3f446 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -46,9 +46,9 @@ import com.android.launcher3.util.TraceHelper; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.TaskAnimationManager; -import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper; import com.android.quickstep.views.TaskView.FullscreenDrawParams; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.utilities.PreviewPositionHelper; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder; @@ -317,9 +317,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // mIsRecentsRtl is the inverse of TaskView RTL. boolean isRtlEnabled = !mIsRecentsRtl; mPositionHelper.updateThumbnailMatrix( - mThumbnailPosition, mThumbnailData, - mTaskRect.width(), mTaskRect.height(), - mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled); + mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(), + mDp.widthPx, mDp.taskbarSize, mDp.isTablet, + mOrientationState.getRecentsActivityRotation(), isRtlEnabled); mPositionHelper.getMatrix().invert(mInversePositionMatrix); if (DEBUG) { Log.d(TAG, " taskRect: " + mTaskRect); diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index 43d38a7304..04a0af15c5 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -16,10 +16,12 @@ package com.android.quickstep.views; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; +import static com.android.systemui.shared.recents.utilities.PreviewPositionHelper.MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT; +import static com.android.systemui.shared.recents.utilities.Utilities.isRelativePercentDifferenceGreaterThan; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; @@ -39,7 +41,6 @@ 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; @@ -58,6 +59,7 @@ import com.android.quickstep.TaskOverlayFactory.TaskOverlay; import com.android.quickstep.views.TaskView.FullscreenDrawParams; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.utilities.PreviewPositionHelper; /** * A task in the Recents view. @@ -65,7 +67,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; public class TaskThumbnailView extends View { private static final MainThreadInitializedObject 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") { @@ -417,7 +418,7 @@ public class TaskThumbnailView extends View { float thumbnailDataAspect = mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight(); - return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect, + return isRelativePercentDifferenceGreaterThan(thumbnailViewAspect, thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT); } @@ -441,8 +442,8 @@ public class TaskThumbnailView extends View { */ private void refreshOverlay() { if (mOverlayEnabled) { - getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix, - mPreviewPositionHelper.mIsOrientationChanged); + getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(), + mPreviewPositionHelper.isOrientationChanged()); } else { getTaskOverlay().reset(); } @@ -463,18 +464,19 @@ public class TaskThumbnailView extends View { } private void updateThumbnailMatrix() { - mPreviewPositionHelper.mIsOrientationChanged = false; + mPreviewPositionHelper.setOrientationChanged(false); if (mBitmapShader != null && mThumbnailData != null) { mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(), mThumbnailData.thumbnail.getHeight()); int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState() .getRecentsActivityRotation(); boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + DeviceProfile dp = mActivity.getDeviceProfile(); mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData, - getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(), - currentRotation, isRtl); + getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.taskbarSize, + dp.isTablet, currentRotation, isRtl); - mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix); + mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix()); mPaint.setShader(mBitmapShader); } getTaskView().updateCurrentFullscreenParams(mPreviewPositionHelper); @@ -514,199 +516,4 @@ public class TaskThumbnailView extends View { } return mThumbnailData.isRealSnapshot && !mTask.isLocked; } - - /** - * Utility class to position the thumbnail in the TaskView - */ - public static class PreviewPositionHelper { - - private static final RectF EMPTY_RECT_F = new RectF(); - - // Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1. - private final RectF mClippedInsets = new RectF(); - private final Matrix mMatrix = new Matrix(); - private boolean mIsOrientationChanged; - - public Matrix getMatrix() { - return mMatrix; - } - - /** - * Updates the matrix based on the provided parameters - */ - public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData, - int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation, - boolean isRtl) { - boolean isRotated = false; - boolean isOrientationDifferent; - - int thumbnailRotation = thumbnailData.rotation; - int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation); - RectF thumbnailClipHint = new RectF(); - float canvasScreenRatio = canvasWidth / (float) dp.widthPx; - float scaledTaskbarSize = dp.taskbarSize * canvasScreenRatio; - thumbnailClipHint.bottom = dp.isTablet ? scaledTaskbarSize : 0; - - float scale = thumbnailData.scale; - final float thumbnailScale; - - // Landscape vs portrait change. - // Note: Disable rotation in grid layout. - boolean windowingModeSupportsRotation = - thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !dp.isTablet; - isOrientationDifferent = isOrientationChange(deltaRotate) - && windowingModeSupportsRotation; - if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) { - // If we haven't measured , skip the thumbnail drawing and only draw the background - // color - thumbnailScale = 0f; - } else { - // Rotate the screenshot if not in multi-window mode - isRotated = deltaRotate > 0 && windowingModeSupportsRotation; - - float surfaceWidth = thumbnailBounds.width() / scale; - float surfaceHeight = thumbnailBounds.height() / scale; - float availableWidth = surfaceWidth - - (thumbnailClipHint.left + thumbnailClipHint.right); - float availableHeight = surfaceHeight - - (thumbnailClipHint.top + thumbnailClipHint.bottom); - - float canvasAspect = canvasWidth / (float) canvasHeight; - float availableAspect = isRotated - ? availableHeight / availableWidth - : availableWidth / availableHeight; - 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; - isOrientationDifferent = false; - } - - if (isAspectLargelyDifferent) { - // Crop letterbox insets if insets isn't already clipped - thumbnailClipHint.left = thumbnailData.letterboxInsets.left; - thumbnailClipHint.right = thumbnailData.letterboxInsets.right; - thumbnailClipHint.top = thumbnailData.letterboxInsets.top; - thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom; - availableWidth = surfaceWidth - - (thumbnailClipHint.left + thumbnailClipHint.right); - availableHeight = surfaceHeight - - (thumbnailClipHint.top + thumbnailClipHint.bottom); - } - - final float targetW, targetH; - if (isOrientationDifferent) { - targetW = canvasHeight; - targetH = canvasWidth; - } else { - targetW = canvasWidth; - targetH = canvasHeight; - } - float targetAspect = targetW / targetH; - - // Update the clipHint such that - // > the final clipped position has same aspect ratio as requested by canvas - // > first fit the width and crop the extra height - // > if that will leave empty space, fit the height and crop the width instead - float croppedWidth = availableWidth; - float croppedHeight = croppedWidth / targetAspect; - if (croppedHeight > availableHeight) { - croppedHeight = availableHeight; - if (croppedHeight < targetH) { - croppedHeight = Math.min(targetH, surfaceHeight); - } - croppedWidth = croppedHeight * targetAspect; - - // One last check in case the task aspect radio messed up something - if (croppedWidth > surfaceWidth) { - croppedWidth = surfaceWidth; - croppedHeight = croppedWidth / targetAspect; - } - } - - // Update the clip hints. Align to 0,0, crop the remaining. - if (isRtl) { - thumbnailClipHint.left += availableWidth - croppedWidth; - if (thumbnailClipHint.right < 0) { - thumbnailClipHint.left += thumbnailClipHint.right; - thumbnailClipHint.right = 0; - } - } else { - thumbnailClipHint.right += availableWidth - croppedWidth; - if (thumbnailClipHint.left < 0) { - thumbnailClipHint.right += thumbnailClipHint.left; - thumbnailClipHint.left = 0; - } - } - thumbnailClipHint.bottom += availableHeight - croppedHeight; - if (thumbnailClipHint.top < 0) { - thumbnailClipHint.bottom += thumbnailClipHint.top; - thumbnailClipHint.top = 0; - } else if (thumbnailClipHint.bottom < 0) { - thumbnailClipHint.top += thumbnailClipHint.bottom; - thumbnailClipHint.bottom = 0; - } - - thumbnailScale = targetW / (croppedWidth * scale); - } - - if (!isRotated) { - mMatrix.setTranslate( - -thumbnailClipHint.left * scale, - -thumbnailClipHint.top * scale); - } else { - setThumbnailRotation(deltaRotate, thumbnailBounds); - } - - mClippedInsets.set(0, 0, 0, scaledTaskbarSize); - - mMatrix.postScale(thumbnailScale, thumbnailScale); - mIsOrientationChanged = isOrientationDifferent; - } - - private int getRotationDelta(int oldRotation, int newRotation) { - int delta = newRotation - oldRotation; - if (delta < 0) delta += 4; - return delta; - } - - /** - * @param deltaRotation the number of 90 degree turns from the current orientation - * @return {@code true} if the change in rotation results in a shift from landscape to - * portrait or vice versa, {@code false} otherwise - */ - private boolean isOrientationChange(int deltaRotation) { - return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270; - } - - private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) { - float translateX = 0; - float translateY = 0; - - mMatrix.setRotate(90 * deltaRotate); - switch (deltaRotate) { /* Counter-clockwise */ - case Surface.ROTATION_90: - translateX = thumbnailPosition.height(); - break; - case Surface.ROTATION_270: - translateY = thumbnailPosition.width(); - break; - case Surface.ROTATION_180: - translateX = thumbnailPosition.width(); - translateY = thumbnailPosition.height(); - break; - } - mMatrix.postTranslate(translateX, translateY); - } - - /** - * Insets to used for clipping the thumbnail (in case it is drawing outside its own space) - */ - public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) { - return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps - ? mClippedInsets : EMPTY_RECT_F; - } - } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 07d8989858..0125775684 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -98,9 +98,9 @@ import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.TaskCornerRadius; import com.android.quickstep.util.TransformParams; -import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.utilities.PreviewPositionHelper; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -121,6 +121,8 @@ public class TaskView extends FrameLayout implements Reusable { private static final String TAG = TaskView.class.getSimpleName(); private static final boolean DEBUG = false; + private static final RectF EMPTY_RECT_F = new RectF(); + public static final int FLAG_UPDATE_ICON = 1; public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1; @@ -1572,7 +1574,7 @@ public class TaskView extends FrameLayout implements Reusable { */ public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale, int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) { - RectF insets = pph.getInsetsToDrawInFullscreen(dp); + RectF insets = getInsetsToDrawInFullscreen(pph, dp); float currentInsetsLeft = insets.left * fullscreenProgress; float currentInsetsTop = insets.top * fullscreenProgress; @@ -1591,6 +1593,14 @@ public class TaskView extends FrameLayout implements Reusable { mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight); } } + + /** + * Insets to used for clipping the thumbnail (in case it is drawing outside its own space) + */ + private static RectF getInsetsToDrawInFullscreen(PreviewPositionHelper pph, DeviceProfile dp) { + return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps + ? pph.getClippedInsets() : EMPTY_RECT_F; + } } public class TaskIdAttributeContainer { diff --git a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt similarity index 64% rename from quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt rename to quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt index cf3c8c9b8d..478535051c 100644 --- a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt +++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt @@ -20,26 +20,34 @@ import android.graphics.RectF import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.launcher3.DeviceProfileBaseTest -import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper +import com.android.quickstep.views.TaskView.FullscreenDrawParams import com.android.systemui.shared.recents.model.ThumbnailData +import com.android.systemui.shared.recents.utilities.PreviewPositionHelper import com.google.common.truth.Truth.assertThat +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock /** - * Test for TaskThumbnailView class. + * Test for FullscreenDrawParams class. */ @SmallTest @RunWith(AndroidJUnit4::class) -class TaskThumbnailViewTest : DeviceProfileBaseTest() { +class FullscreenDrawParamsTest : DeviceProfileBaseTest() { private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java) private val mPreviewPositionHelper = PreviewPositionHelper() + private lateinit var params: FullscreenDrawParams + + @Before + fun setup() { + params = FullscreenDrawParams(context) + } @Test - fun getInsetsToDrawInFullscreen_clipTaskbarSizeFromBottomForTablets() { + fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets() { initializeVarsForTablet() val dp = newDP() val previewRect = Rect(0, 0, 100, 100) @@ -49,15 +57,18 @@ class TaskThumbnailViewTest : DeviceProfileBaseTest() { val isRtl = false mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp, currentRotation, isRtl) + canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation, + isRtl) + params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize / 2f) - assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp)) + assertThat(params.mCurrentDrawnInsets) .isEqualTo(expectedClippedInsets) } @Test - fun getInsetsToDrawInFullscreen_doNotClipTaskbarSizeFromBottomForPhones() { + fun setFullProgress_currentDrawnInsets_doNotClipTaskbarSizeFromBottomForPhones() { initializeVarsForPhone() val dp = newDP() val previewRect = Rect(0, 0, 100, 100) @@ -67,10 +78,13 @@ class TaskThumbnailViewTest : DeviceProfileBaseTest() { val isRtl = false mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp, currentRotation, isRtl) + canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation, + isRtl) + params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) val expectedClippedInsets = RectF(0f, 0f, 0f, 0f) - assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp)) + assertThat(params.mCurrentDrawnInsets) .isEqualTo(expectedClippedInsets) } } \ No newline at end of file diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 616b08a908..f70511af7b 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -818,16 +818,6 @@ public final class Utilities { }; } - /** - * Compares the ratio of two quantities and returns whether that ratio is greater than the - * provided bound. Order of quantities does not matter. Bound should be a decimal representation - * of a percentage. - */ - public static boolean isRelativePercentDifferenceGreaterThan(float first, float second, - float bound) { - return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound; - } - /** * Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent * sizes represent the "space" that will rotate carrying inOutBounds along with it to determine