summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Riddle Hsu <riddlehsu@google.com> 2024-04-26 18:42:49 +0800
committer Riddle Hsu <riddlehsu@google.com> 2024-04-30 09:52:21 +0000
commit2dc699a5b4dcb367d7a0274292d42530bd246b24 (patch)
tree0058f8f9b8df3ea3fba2815e14a97a770e499137
parentb94064952b8993562945e292fe52570b76780f49 (diff)
Use screen space to calculate snapshot scale
The "frame = calculateSnapshotFrame(crop)" is in snapshot space. So originally the scale only undoes config_highResTaskSnapshotScale. But the purpose is to show on screen, so the target size should be current task size. This also fixes extra x-offset when navigation bar is on left side. The case should draw from 0,0 but the frame from calculateSnapshotFrame will add mSystemBarInsets.left. An example of the various bounds: Snapshot_getTaskSize=2000x1000 (the size when capturing) Snapshot_BufferSize=1600x800 (scaled snapshot) Current_mTaskBounds=3000x1500 (assume screen size is changed) Current_mFrame=3000x1400 (window frame may exclude cutout) Now mTaskBounds is deleted and mFrame is used as the target bounds. Because the purpose is to fill the window container and the snapshot surface can be used for activity and task, the window frame is not important. Besides, it can reduce false alarm of !isAspectRatioMatch from comparing the window frame with snapshot size. Also fix SnapshotDrawerUtilsTest which was not able to run due to inaccessible package-private methods. Bug: 336964874 Test: atest SnapshotDrawerUtilsTest Test: Toggle different screen resolutions and switch between multiple existing tasks. The snapshot of starting window should match the real content. Change-Id: Ia164e35ba71e73dd22ec8ce0dad7d356fe791ced
-rw-r--r--core/java/android/window/SnapshotDrawerUtils.java106
-rw-r--r--core/java/android/window/StartingWindowInfo.java11
-rw-r--r--core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
6 files changed, 71 insertions, 67 deletions
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index e5658e63f7ec..29bb32e6443f 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -52,11 +52,9 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.util.Log;
@@ -98,11 +96,6 @@ public class SnapshotDrawerUtils {
| FLAG_SECURE
| FLAG_DIM_BEHIND;
- private static final RectF sTmpSnapshotSize = new RectF();
- private static final RectF sTmpDstFrame = new RectF();
-
- private static final Matrix sSnapshotMatrix = new Matrix();
- private static final float[] sTmpFloat9 = new float[9];
private static final Paint sBackgroundPaint = new Paint();
/**
@@ -116,24 +109,27 @@ public class SnapshotDrawerUtils {
private final CharSequence mTitle;
private SystemBarBackgroundPainter mSystemBarBackgroundPainter;
- private final Rect mTaskBounds;
private final Rect mFrame = new Rect();
private final Rect mSystemBarInsets = new Rect();
+ private final int mSnapshotW;
+ private final int mSnapshotH;
private boolean mSizeMismatch;
public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
- CharSequence title,
- Rect taskBounds) {
+ CharSequence title) {
mRootSurface = rootSurface;
mSnapshot = snapshot;
mTitle = title;
- mTaskBounds = taskBounds;
+ final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
+ mSnapshotW = hwBuffer.getWidth();
+ mSnapshotH = hwBuffer.getHeight();
}
/**
* Initiate system bar painter to draw the system bar background.
*/
- void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
+ @VisibleForTesting
+ public void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
int appearance, ActivityManager.TaskDescription taskDescription,
@WindowInsets.Type.InsetsType int requestedVisibleTypes) {
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
@@ -143,14 +139,13 @@ public class SnapshotDrawerUtils {
}
/**
- * Set frame size.
+ * Set frame size that the snapshot should fill. It is the bounds of a task or activity.
*/
- void setFrames(Rect frame, Rect systemBarInsets) {
+ @VisibleForTesting
+ public void setFrames(Rect frame, Rect systemBarInsets) {
mFrame.set(frame);
mSystemBarInsets.set(systemBarInsets);
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- mSizeMismatch = (mFrame.width() != snapshot.getWidth()
- || mFrame.height() != snapshot.getHeight());
+ mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
mSystemBarBackgroundPainter.setInsets(systemBarInsets);
}
@@ -186,7 +181,7 @@ public class SnapshotDrawerUtils {
// We consider nearly matched dimensions as there can be rounding errors and the user
// won't notice very minute differences from scaling one dimension more than the other
- boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
+ boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH);
// Keep a reference to it such that it doesn't get destroyed when finalized.
SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -198,12 +193,14 @@ public class SnapshotDrawerUtils {
.build();
final Rect frame;
+ final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
+ float offsetX = letterboxInsets.left;
+ float offsetY = letterboxInsets.top;
// We can just show the surface here as it will still be hidden as the parent is
// still hidden.
mTransaction.show(childSurfaceControl);
if (aspectRatioMismatch) {
Rect crop = null;
- final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
if (letterboxInsets.left != 0 || letterboxInsets.top != 0
|| letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
// Clip off letterbox.
@@ -214,23 +211,27 @@ public class SnapshotDrawerUtils {
// if letterbox doesn't match window frame, try crop by content insets
if (aspectRatioMismatch) {
// Clip off ugly navigation bar.
- crop = calculateSnapshotCrop(mSnapshot.getContentInsets());
+ final Rect contentInsets = mSnapshot.getContentInsets();
+ crop = calculateSnapshotCrop(contentInsets);
+ offsetX = contentInsets.left;
+ offsetY = contentInsets.top;
}
frame = calculateSnapshotFrame(crop);
- mTransaction.setWindowCrop(childSurfaceControl, crop);
- mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
- sTmpSnapshotSize.set(crop);
- sTmpDstFrame.set(frame);
+ mTransaction.setCrop(childSurfaceControl, crop);
} else {
frame = null;
- sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
- sTmpDstFrame.set(mFrame);
- sTmpDstFrame.offsetTo(0, 0);
}
- // Scale the mismatch dimensions to fill the task bounds
- sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL);
- mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9);
+ // Align the snapshot with content area.
+ if (offsetX != 0f || offsetY != 0f) {
+ mTransaction.setPosition(childSurfaceControl,
+ -offsetX * mFrame.width() / mSnapshot.getTaskSize().x,
+ -offsetY * mFrame.height() / mSnapshot.getTaskSize().y);
+ }
+ // Scale the mismatch dimensions to fill the target frame.
+ final float scaleX = (float) mFrame.width() / mSnapshotW;
+ final float scaleY = (float) mFrame.height() / mSnapshotH;
+ mTransaction.setScale(childSurfaceControl, scaleX, scaleY);
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
@@ -261,17 +262,17 @@ public class SnapshotDrawerUtils {
* @param insets Content insets or Letterbox insets
* @return crop rect in snapshot coordinate space.
*/
- Rect calculateSnapshotCrop(@NonNull Rect insets) {
+ @VisibleForTesting
+ public Rect calculateSnapshotCrop(@NonNull Rect insets) {
final Rect rect = new Rect();
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
+ rect.set(0, 0, mSnapshotW, mSnapshotH);
- final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
- final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
+ final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
+ final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
// Let's remove all system decorations except the status bar, but only if the task is at
// the very top of the screen.
- final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
+ final boolean isTop = mFrame.top == 0;
rect.inset((int) (insets.left * scaleX),
isTop ? 0 : (int) (insets.top * scaleY),
(int) (insets.right * scaleX),
@@ -284,10 +285,10 @@ public class SnapshotDrawerUtils {
*
* @param crop rect that is in snapshot coordinate space.
*/
- Rect calculateSnapshotFrame(Rect crop) {
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
- final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
+ @VisibleForTesting
+ public Rect calculateSnapshotFrame(Rect crop) {
+ final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
+ final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
// Rescale the frame from snapshot to window coordinate space
final Rect frame = new Rect(0, 0,
@@ -303,7 +304,8 @@ public class SnapshotDrawerUtils {
/**
* Draw status bar and navigation bar background.
*/
- void drawBackgroundAndBars(Canvas c, Rect frame) {
+ @VisibleForTesting
+ public void drawBackgroundAndBars(Canvas c, Rect frame) {
final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
final boolean fillHorizontally = c.getWidth() > frame.right;
final boolean fillVertically = c.getHeight() > frame.bottom;
@@ -320,33 +322,27 @@ public class SnapshotDrawerUtils {
/**
* Ask system bar background painter to draw status bar background.
- *
*/
- void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
+ @VisibleForTesting
+ public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
}
/**
* Ask system bar background painter to draw navigation bar background.
- *
*/
- void drawNavigationBarBackground(Canvas c) {
+ @VisibleForTesting
+ public void drawNavigationBarBackground(Canvas c) {
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
}
- /**
- * @return true if the aspect ratio match between a frame and a snapshot buffer.
- */
- public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) {
+ private static boolean isAspectRatioMatch(Rect frame, int w, int h) {
if (frame.isEmpty()) {
return false;
}
- final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- return Math.abs(
- ((float) buffer.getWidth() / buffer.getHeight())
- - ((float) frame.width() / frame.height())) <= 0.01f;
+ return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f;
}
private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
@@ -378,14 +374,14 @@ public class SnapshotDrawerUtils {
*/
public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
SurfaceControl rootSurface, TaskSnapshot snapshot,
- Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState,
+ Rect windowBounds, InsetsState topWindowInsetsState,
boolean releaseAfterDraw) {
if (windowBounds.isEmpty()) {
Log.e(TAG, "Unable to draw snapshot on an empty windowBounds");
return;
}
final SnapshotSurface drawSurface = new SnapshotSurface(
- rootSurface, snapshot, lp.getTitle(), configBounds);
+ rootSurface, snapshot, lp.getTitle());
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 260d9a82472f..72df343a2dbe 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -79,11 +80,17 @@ public final class StartingWindowInfo implements Parcelable {
/**
* The {@link TaskInfo} from this task.
- * @hide
+ * <p>Note that the configuration of this taskInfo could be from the top activity of its task.
+ * Because only activity contains persisted configuration (e.g. night mode, language). Besides,
+ * it can also be used for activity level snapshot.
*/
@NonNull
public ActivityManager.RunningTaskInfo taskInfo;
+ /** The bounds of the target task. */
+ @NonNull
+ public final Rect taskBounds = new Rect();
+
/**
* The {@link ActivityInfo} of the target activity which to create the starting window.
* It can be null if the info is the same as the top in task info.
@@ -253,6 +260,7 @@ public final class StartingWindowInfo implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedObject(taskInfo, flags);
+ taskBounds.writeToParcel(dest, flags);
dest.writeTypedObject(targetActivityInfo, flags);
dest.writeInt(startingWindowTypeParameter);
dest.writeTypedObject(topOpaqueWindowInsetsState, flags);
@@ -269,6 +277,7 @@ public final class StartingWindowInfo implements Parcelable {
void readFromParcel(@NonNull Parcel source) {
taskInfo = source.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ taskBounds.readFromParcel(source);
targetActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
startingWindowTypeParameter = source.readInt();
topOpaqueWindowInsetsState = source.readTypedObject(InsetsState.CREATOR);
diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
index 036154634ae7..6c8dcd39e223 100644
--- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
+++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
@@ -77,7 +77,7 @@ public class SnapshotDrawerUtilsTest {
Color.RED, Color.BLUE);
mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface(
- new SurfaceControl(), snapshot, "Test", taskBounds);
+ new SurfaceControl(), snapshot, "Test");
mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0,
taskDescription, WindowInsets.Type.defaultVisible());
}
@@ -167,14 +167,16 @@ public class SnapshotDrawerUtilsTest {
@Test
public void testCalculateSnapshotCrop_taskNotOnTop() {
final Rect contentInsets = new Rect(0, 10, 0, 10);
- setupSurface(100, 100, contentInsets, 0, new Rect(0, 50, 100, 150));
+ final Rect bounds = new Rect(0, 50, 100, 150);
+ setupSurface(100, 100, contentInsets, 0, bounds);
+ mSnapshotSurface.setFrames(bounds, contentInsets);
assertEquals(new Rect(0, 10, 100, 90),
mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
public void testCalculateSnapshotCrop_navBarLeft() {
- final Rect contentInsets = new Rect(0, 10, 0, 0);
+ final Rect contentInsets = new Rect(10, 0, 0, 0);
setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
assertEquals(new Rect(10, 0, 100, 100),
mSnapshotSurface.calculateSnapshotCrop(contentInsets));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index ceac40d9ba95..5fec38dc23d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -27,7 +27,6 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.graphics.Paint;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -100,8 +99,6 @@ public class TaskSnapshotWindow {
return null;
}
- final Point taskSize = snapshot.getTaskSize();
- final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
final int orientation = snapshot.getOrientation();
final int displayId = runningTaskInfo.displayId;
@@ -150,7 +147,7 @@ public class TaskSnapshotWindow {
}
SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
- taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */);
+ info.taskBounds, topWindowInsetsState, true /* releaseAfterDraw */);
snapshotSurface.mHasDrawn = true;
snapshotSurface.reportDrawn();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index fed2f34b5e0c..5c814dcc9b16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.view.Display;
@@ -77,15 +76,13 @@ class WindowlessSnapshotWindowCreator {
runningTaskInfo.configuration, rootSurface);
final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
mContext, display, wlw, "WindowlessSnapshotWindowCreator");
- final Point taskSize = snapshot.getTaskSize();
- final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y);
final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
final FrameLayout rootLayout = new FrameLayout(
mSplashscreenContentDrawer.createViewContextWrapper(mContext));
mViewHost.setView(rootLayout, lp);
SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot,
- snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
+ windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
final ActivityManager.TaskDescription taskDescription =
SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f23a440eb0ed..89b8e15b9461 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3689,6 +3689,9 @@ class Task extends TaskFragment {
info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes();
}
}
+ final Rect rotatedBounds = activity.getFixedRotationTransformDisplayBounds();
+ info.taskBounds.set(rotatedBounds != null ? rotatedBounds
+ : info.taskInfo.configuration.windowConfiguration.getBounds());
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
info.taskInfo.configuration.setTo(activity.getConfiguration());