From cbfb35edc7fffd3b41eff3f62e23a4b5aa4a7d3e Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 23 Sep 2022 12:04:18 +0100 Subject: [PATCH] Extract PreviewPositionHelper to shared library Moves Launcher's PreviewPositionHelper to shared library between SystemUI and Launcher to reuse it in the future in the partial screensharing recents selector. There should be no functional changes in the code itself. Bug: 240924926 Test: presubmit Change-Id: Ib38b6f9db91e63a2598bf81229e3cd3e1a49ca60 --- .../quickstep/util/TaskViewSimulator.java | 8 +- .../quickstep/views/TaskThumbnailView.java | 217 +----------------- .../com/android/quickstep/views/TaskView.java | 14 +- ...iewTest.kt => FullscreenDrawParamsTest.kt} | 32 ++- src/com/android/launcher3/Utilities.java | 10 - 5 files changed, 51 insertions(+), 230 deletions(-) rename quickstep/tests/src/com/android/quickstep/{TaskThumbnailViewTest.kt => FullscreenDrawParamsTest.kt} (64%) 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