Make child window inherit the scale of parent

The child window computes its layout in parent's frame.
If parent frame is scaled, the position and size of
child window is already based on parent. And the child
surface also has the same scale from parent surface.

This change makes mGlobalScale of child window to 1.
And use the unscaled containing frame display frame of
parent to apply gravity, so the child window can fit
in the scaled content of parent window.

Bug: 182362657
Test: atest WindowStateTests#testCompatOverrideScale

Change-Id: Ie8b9fabcfbccb63f54d51bdd55b23f835151202d
This commit is contained in:
Riddle Hsu
2021-03-24 23:10:48 +08:00
parent 9c1d00870e
commit df46fd52f9
3 changed files with 54 additions and 5 deletions

View File

@@ -255,6 +255,7 @@ import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.utils.CoordinateTransforms;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -624,6 +625,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private PowerManager.WakeLock mDrawLock;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRect2 = new Rect();
private final Point mTmpPoint = new Point();
private final Transaction mTmpTransaction;
@@ -1136,13 +1138,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/**
* @return {@code true} if the application runs in size compatibility mode or has an app level
* scaling override set.
* scaling override set. This method always returns {@code false} on child window because it
* should follow parent's scale.
* @see CompatModePackages#getCompatScale
* @see android.content.res.CompatibilityInfo#supportsScreen
* @see ActivityRecord#hasSizeCompatBounds()
*/
boolean hasCompatScale() {
return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
return (mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord)) && !mIsChildWindow;
}
/**
@@ -1314,7 +1317,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame);
layoutDisplayFrame = mTmpRect2;
layoutDisplayFrame.set(windowFrames.mDisplayFrame);
windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
@@ -4361,6 +4365,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
h = Math.min(h, ph);
}
if (mIsChildWindow) {
final WindowState parent = getTopParentWindow();
if (parent.hasCompatScale()) {
// Scale the containing and display frames because they are in screen coordinates.
// The position of frames are already relative to parent so only size is scaled.
mTmpRect.set(containingFrame);
containingFrame = mTmpRect;
CoordinateTransforms.scaleRectSize(containingFrame, parent.mInvGlobalScale);
if (fitToDisplay) {
mTmpRect2.set(displayFrame);
displayFrame = mTmpRect2;
CoordinateTransforms.scaleRectSize(displayFrame, parent.mInvGlobalScale);
}
}
}
// Set mFrame
Gravity.apply(mAttrs.gravity, w, h, containingFrame,
(int) (x + mAttrs.horizontalMargin * pw),

View File

@@ -152,4 +152,10 @@ public class CoordinateTransforms {
transform.mapRect(tmp);
inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
}
/** Scales the rect without changing its position. */
public static void scaleRectSize(Rect inOutRect, float scale) {
inOutRect.right = inOutRect.left + (int) (inOutRect.width() * scale + .5f);
inOutRect.bottom = inOutRect.top + (int) (inOutRect.height() * scale + .5f);
}
}

View File

@@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -570,21 +571,43 @@ public class WindowStateTests extends WindowTestsBase {
spyOn(cmp);
doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt());
final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win");
makeWindowVisible(w);
final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child");
assertTrue(w.hasCompatScale());
assertFalse(child.hasCompatScale());
makeWindowVisible(w, child);
w.setRequestedSize(100, 200);
child.setRequestedSize(50, 100);
child.mAttrs.width = child.mAttrs.height = 0;
w.mAttrs.x = w.mAttrs.y = 100;
w.mAttrs.width = w.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
w.mAttrs.gravity = Gravity.TOP | Gravity.LEFT;
child.mAttrs.gravity = Gravity.CENTER;
DisplayContentTests.performLayout(mDisplayContent);
// Frame on screen = 100x200. Compat frame on client = 50x100.
// Frame on screen = 200x400 (200, 200 - 400, 600). Compat frame on client = 100x200.
final Rect unscaledCompatFrame = new Rect(w.getWindowFrames().mCompatFrame);
unscaledCompatFrame.scale(overrideScale);
final Rect parentFrame = w.getFrame();
assertEquals(w.getWindowFrames().mFrame, unscaledCompatFrame);
final Rect childFrame = child.getFrame();
assertEquals(childFrame, child.getWindowFrames().mCompatFrame);
// Child frame = 50x100 (225, 250 - 275, 350) according to Gravity.CENTER.
final int childX = parentFrame.left + child.mRequestedWidth / 2;
final int childY = parentFrame.top + child.mRequestedHeight / 2;
final Rect expectedChildFrame = new Rect(childX, childY, childX + child.mRequestedWidth,
childY + child.mRequestedHeight);
assertEquals(expectedChildFrame, childFrame);
// Surface should apply the scale.
w.prepareSurfaces();
verify(w.getPendingTransaction()).setMatrix(w.getSurfaceControl(),
overrideScale, 0, 0, overrideScale);
// Child surface inherits parent's scale, so it doesn't need to scale.
verify(child.getPendingTransaction(), never()).setMatrix(any(), anyInt(), anyInt(),
anyInt(), anyInt());
// According to "dp * density / 160 = px", density is scaled and the size in dp is the same.
final CompatibilityInfo compatInfo = cmp.compatibilityInfoForPackageLocked(