Loading quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +4 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +12 −205 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -65,7 +67,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; 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") { Loading Loading @@ -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); } Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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; } } } quickstep/src/com/android/quickstep/views/TaskView.java +12 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt→quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt +23 −9 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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 src/com/android/launcher3/Utilities.java +0 −10 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +4 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +12 −205 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -65,7 +67,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; 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") { Loading Loading @@ -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); } Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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; } } }
quickstep/src/com/android/quickstep/views/TaskView.java +12 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading
quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt→quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt +23 −9 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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
src/com/android/launcher3/Utilities.java +0 −10 Original line number Diff line number Diff line Loading @@ -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 Loading