Merge "Extract PreviewPositionHelper to shared library" into tm-qpr-dev

This commit is contained in:
Nick Chameyev
2022-10-05 12:52:02 +00:00
committed by Android (Google) Code Review
5 changed files with 51 additions and 230 deletions

View File

@@ -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);

View File

@@ -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<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") {
@@ -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;
}
}
}

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -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