blob: 8690b36c12d7da4ad9b3381cdfb8959e7106d307 [file] [log] [blame]
package com.android.systemui.shared.recents.utilities;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.wm.shell.util.SplitBounds;
/**
* Utility class to position the thumbnail in the TaskView
*/
public class PreviewPositionHelper {
public static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
/**
* Specifies that a stage is positioned at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
* TODO(b/254378592): Remove after consolidation
*/
public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
/**
* Specifies that a stage is positioned at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
* TODO(b/254378592): Remove after consolidation
*/
public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
// 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;
private SplitBounds mSplitBounds;
private int mDesiredStagePosition;
public Matrix getMatrix() {
return mMatrix;
}
public void setOrientationChanged(boolean orientationChanged) {
mIsOrientationChanged = orientationChanged;
}
public boolean isOrientationChanged() {
return mIsOrientationChanged;
}
public void setSplitBounds(SplitBounds splitBounds, int desiredStagePosition) {
mSplitBounds = splitBounds;
mDesiredStagePosition = desiredStagePosition;
}
/**
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
int taskbarSize, boolean isLargeScreen,
int currentRotation, boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
RectF thumbnailClipHint = new RectF();
float scaledTaskbarSize;
float canvasScreenRatio;
if (mSplitBounds != null) {
if (mSplitBounds.appsStackedVertically) {
if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
// Top app isn't cropped at all by taskbar
canvasScreenRatio = 0;
} else {
// Same as fullscreen ratio
canvasScreenRatio = (float) canvasWidth / screenWidthPx;
}
} else {
// For landscape, scale the width
float taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitBounds.leftTaskPercent
: (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
// Scale landscape width to that of actual screen
float fullscreenTaskWidth = screenWidthPx * taskPercent;
canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
}
} else {
canvasScreenRatio = (float) canvasWidth / screenWidthPx;
}
scaledTaskbarSize = taskbarSize * canvasScreenRatio;
thumbnailClipHint.bottom = isLargeScreen ? 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 && !isLargeScreen;
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 == ROTATION_90 || deltaRotation == ROTATION_270;
}
private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
float translateX = 0;
float translateY = 0;
mMatrix.setRotate(90 * deltaRotate);
switch (deltaRotate) { /* Counter-clockwise */
case ROTATION_90:
translateX = thumbnailPosition.height();
break;
case ROTATION_270:
translateY = thumbnailPosition.width();
break;
case ROTATION_180:
translateX = thumbnailPosition.width();
translateY = thumbnailPosition.height();
break;
}
mMatrix.postTranslate(translateX, translateY);
}
public RectF getClippedInsets() {
return mClippedInsets;
}
}