diff options
2 files changed, 219 insertions, 0 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java new file mode 100644 index 000000000000..72f8b7b09dca --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java @@ -0,0 +1,209 @@ +package com.android.systemui.shared.recents.utilities; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.Surface; + +import com.android.systemui.shared.recents.model.ThumbnailData; + +/** + * 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; + + // 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; + } + + public void setOrientationChanged(boolean orientationChanged) { + mIsOrientationChanged = orientationChanged; + } + + public boolean isOrientationChanged() { + return mIsOrientationChanged; + } + + /** + * Updates the matrix based on the provided parameters + */ + public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData, + int canvasWidth, int canvasHeight, int screenWidthPx, int taskbarSize, boolean isTablet, + 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) screenWidthPx; + float scaledTaskbarSize = taskbarSize * canvasScreenRatio; + thumbnailClipHint.bottom = 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 && !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); + } + + public RectF getClippedInsets() { + return mClippedInsets; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 56326e36ff5e..77a13bd91b90 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -62,6 +62,16 @@ public class Utilities { return false; // Default } + /** + * 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; + } + /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */ public static float computeContrastBetweenColors(int bg, int fg) { float bgR = Color.red(bg) / 255f; |