Compatible behavior for non-resizable activity (2/N)
Scale and offset activity with compatibility bounds horizontally.
This reuses the existing scaling flow of compatibility mode,
so the insets scaling is also handled when computing frames.
Bug: 112288258
Test: atest AppWindowTokenTests#testSizeCompatBounds
Change-Id: I42a3fae7cd27f3ab6d79d885bedbea2e34874d01
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index bcf6aba..92346e9 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -239,6 +239,17 @@
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
+ /**
+ * The scale to fit at least one side of the activity to its parent. If the activity uses
+ * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
+ */
+ private float mSizeCompatScale = 1f;
+ /**
+ * The bounds in global coordinates for activity in size compatibility mode.
+ * @see ActivityRecord#inSizeCompatMode
+ */
+ private Rect mSizeCompatBounds;
+
private boolean mDisablePreviewScreenshots;
private Task mLastParent;
@@ -1555,11 +1566,52 @@
return mOrientation;
}
+ /** @return {@code true} if the compatibility bounds is taking effect. */
+ boolean inSizeCompatMode() {
+ return mSizeCompatBounds != null;
+ }
+
+ @Override
+ float getSizeCompatScale() {
+ return inSizeCompatMode() ? mSizeCompatScale : super.getSizeCompatScale();
+ }
+
+ /**
+ * @return Non-empty bounds if the activity has override bounds.
+ * @see ActivityRecord#resolveOverrideConfiguration(Configuration)
+ */
+ Rect getResolvedOverrideBounds() {
+ // Get bounds from resolved override configuration because it is computed with orientation.
+ return getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+ }
+
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWinMode = getWindowingMode();
mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
+
+ final Task task = getTask();
+ final Rect overrideBounds = getResolvedOverrideBounds();
+ if (task != null && !overrideBounds.isEmpty()
+ // If the changes come from change-listener, the incoming parent configuration is
+ // still the old one. Make sure their orientations are the same to reduce computing
+ // the compatibility bounds for the intermediate state.
+ && getResolvedOverrideConfiguration().orientation == newParentConfig.orientation) {
+ final Rect taskBounds = task.getBounds();
+ // Since we only center the activity horizontally, if only the fixed height is smaller
+ // than its container, the override bounds don't need to take effect.
+ if ((overrideBounds.width() != taskBounds.width()
+ || overrideBounds.height() > taskBounds.height())) {
+ calculateCompatBoundsTransformation(newParentConfig);
+ updateSurfacePosition();
+ } else if (mSizeCompatBounds != null) {
+ mSizeCompatBounds = null;
+ mSizeCompatScale = 1f;
+ updateSurfacePosition();
+ }
+ }
+
final int winMode = getWindowingMode();
if (prevWinMode == winMode) {
@@ -1671,6 +1723,54 @@
return mThumbnail;
}
+ /**
+ * Calculates the scale and offset to horizontal center the size compatibility bounds into the
+ * region which is available to application.
+ */
+ private void calculateCompatBoundsTransformation(Configuration newParentConfig) {
+ final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ final Rect viewportBounds = parentAppBounds != null
+ ? parentAppBounds : newParentConfig.windowConfiguration.getBounds();
+ final Rect contentBounds = getResolvedOverrideBounds();
+ final float contentW = contentBounds.width();
+ final float contentH = contentBounds.height();
+ final float viewportW = viewportBounds.width();
+ final float viewportH = viewportBounds.height();
+ // Only allow to scale down.
+ mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
+ ? 1 : Math.min(viewportW / contentW, viewportH / contentH);
+ final int offsetX = (int) ((viewportW - contentW * mSizeCompatScale + 1) * 0.5f)
+ + viewportBounds.left;
+
+ if (mSizeCompatBounds == null) {
+ mSizeCompatBounds = new Rect();
+ }
+ mSizeCompatBounds.set(contentBounds);
+ mSizeCompatBounds.offsetTo(0, 0);
+ mSizeCompatBounds.scale(mSizeCompatScale);
+ mSizeCompatBounds.left += offsetX;
+ mSizeCompatBounds.right += offsetX;
+ }
+
+ @Override
+ public Rect getBounds() {
+ if (mSizeCompatBounds != null) {
+ return mSizeCompatBounds;
+ }
+ return super.getBounds();
+ }
+
+ @Override
+ public boolean matchParentBounds() {
+ if (super.matchParentBounds()) {
+ return true;
+ }
+ // An activity in size compatibility mode may have override bounds which equals to its
+ // parent bounds, so the exact bounds should also be checked.
+ final WindowContainer parent = getParent();
+ return parent == null || parent.getBounds().equals(getResolvedOverrideBounds());
+ }
+
@Override
void checkAppWindowsReadyToShow() {
if (allDrawn == mLastAllDrawn) {
@@ -2864,6 +2964,10 @@
if (mPendingRelaunchCount != 0) {
pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
}
+ if (mSizeCompatScale != 1f || mSizeCompatBounds != null) {
+ pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
+ + mSizeCompatBounds);
+ }
if (mRemovingFromDisplay) {
pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6c3e1f4..a748e78 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -47,7 +47,6 @@
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -1837,6 +1836,9 @@
return;
}
outDisplayFrame.set(win.getDisplayFrameLw());
+ if (win.inSizeCompatMode()) {
+ outDisplayFrame.scale(win.mInvGlobalScale);
+ }
}
}
@@ -1961,8 +1963,6 @@
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
- win.mEnforceSizeCompat =
- (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 85b251a..48cd6b6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -246,7 +246,6 @@
final boolean mIsWallpaper;
private final boolean mIsFloatingLayer;
int mSeq;
- boolean mEnforceSizeCompat;
int mViewVisibility;
int mSystemUiVisibility;
/**
@@ -505,7 +504,8 @@
*/
private PowerManager.WakeLock mDrawLock;
- final private Rect mTmpRect = new Rect();
+ private final Rect mTmpRect = new Rect();
+ private final Point mTmpPoint = new Point();
/**
* Whether the window was resized by us while it was gone for layout.
@@ -655,7 +655,6 @@
mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
mSeq = seq;
- mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
if (localLOGV) Slog.v(
@@ -733,6 +732,18 @@
}
/**
+ * @return {@code true} if the application runs in size compatibility mode.
+ * @see android.content.res.CompatibilityInfo#supportsScreen
+ * @see ActivityRecord#inSizeCompatMode
+ */
+ boolean inSizeCompatMode() {
+ return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
+ || (mAppToken != null && mAppToken.inSizeCompatMode()
+ // Exclude starting window because it is not displayed by the application.
+ && mAttrs.type != TYPE_APPLICATION_STARTING);
+ }
+
+ /**
* Returns whether this {@link WindowState} has been considered for drawing by its parent.
*/
boolean getDrawnStateEvaluated() {
@@ -995,7 +1006,7 @@
mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode()) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
// reported to the app in its coordinate space.
@@ -1354,8 +1365,8 @@
}
void prelayout() {
- if (mEnforceSizeCompat) {
- mGlobalScale = getDisplayContent().mCompatibleScreenScale;
+ if (inSizeCompatMode()) {
+ mGlobalScale = mToken.getSizeCompatScale();
mInvGlobalScale = 1 / mGlobalScale;
} else {
mGlobalScale = mInvGlobalScale = 1;
@@ -2145,6 +2156,30 @@
int getSurfaceTouchableRegion(Region region, int flags) {
final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ if (mAppToken != null && !mAppToken.getResolvedOverrideBounds().isEmpty()) {
+ // There may have touchable letterboxes around the activity, so in order to let the
+ // letterboxes are able to receive touch event and slip to activity, the activity with
+ // compatibility bounds cannot occupy full screen touchable region.
+ if (modal) {
+ // A modal window uses the whole compatibility bounds.
+ flags |= FLAG_NOT_TOUCH_MODAL;
+ mTmpRect.set(mAppToken.getResolvedOverrideBounds());
+ // TODO(b/112288258): Remove the forced offset when the override bounds always
+ // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}).
+ mTmpRect.offsetTo(0, 0);
+ } else {
+ // Non-modal uses the application based frame.
+ mTmpRect.set(mWindowFrames.mCompatFrame);
+ }
+ // The offset of compatibility bounds is applied to surface of {@link #AppWindowToken}
+ // and frame, so it is unnecessary to translate twice in surface based coordinates.
+ final int surfaceOffsetX = mAppToken.inSizeCompatMode()
+ ? mAppToken.getBounds().left : 0;
+ mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
+ region.set(mTmpRect);
+ return flags;
+ }
+
if (modal && mAppToken != null) {
// Limit the outer touch to the activity stack region.
flags |= FLAG_NOT_TOUCH_MODAL;
@@ -2943,7 +2978,7 @@
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
- final Rect frame = mWindowFrames.mFrame;
+ final Rect frame = mWindowFrames.mCompatFrame;
final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
final Rect contentInsets = mWindowFrames.mLastContentInsets;
final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
@@ -3353,7 +3388,7 @@
pw.println(prefix + "mHasSurface=" + mHasSurface
+ " isReadyForDisplay()=" + isReadyForDisplay()
+ " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode()) {
pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
}
if (dumpAll) {
@@ -3477,17 +3512,18 @@
float x, y;
int w,h;
+ final boolean inSizeCompatMode = inSizeCompatMode();
if ((mAttrs.flags & FLAG_SCALED) != 0) {
if (mAttrs.width < 0) {
w = pw;
- } else if (mEnforceSizeCompat) {
+ } else if (inSizeCompatMode) {
w = (int)(mAttrs.width * mGlobalScale + .5f);
} else {
w = mAttrs.width;
}
if (mAttrs.height < 0) {
h = ph;
- } else if (mEnforceSizeCompat) {
+ } else if (inSizeCompatMode) {
h = (int)(mAttrs.height * mGlobalScale + .5f);
} else {
h = mAttrs.height;
@@ -3495,21 +3531,21 @@
} else {
if (mAttrs.width == MATCH_PARENT) {
w = pw;
- } else if (mEnforceSizeCompat) {
+ } else if (inSizeCompatMode) {
w = (int)(mRequestedWidth * mGlobalScale + .5f);
} else {
w = mRequestedWidth;
}
if (mAttrs.height == MATCH_PARENT) {
h = ph;
- } else if (mEnforceSizeCompat) {
+ } else if (inSizeCompatMode) {
h = (int)(mRequestedHeight * mGlobalScale + .5f);
} else {
h = mRequestedHeight;
}
}
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode) {
x = mAttrs.x * mGlobalScale;
y = mAttrs.y * mGlobalScale;
} else {
@@ -3537,7 +3573,7 @@
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode) {
// See comparable block in computeFrameLw.
mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
}
@@ -3650,7 +3686,7 @@
float translateToWindowX(float x) {
float winX = x - mWindowFrames.mFrame.left;
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode()) {
winX *= mGlobalScale;
}
return winX;
@@ -3658,7 +3694,7 @@
float translateToWindowY(float y) {
float winY = y - mWindowFrames.mFrame.top;
- if (mEnforceSizeCompat) {
+ if (inSizeCompatMode()) {
winY *= mGlobalScale;
}
return winY;
@@ -4233,12 +4269,11 @@
*/
void calculatePolicyCrop(Rect policyCrop) {
final DisplayContent displayContent = getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (!isDefaultDisplay()) {
+ if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) {
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
- // TODO(multi-display)
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
mWindowFrames.mCompatFrame.height());
policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
@@ -4304,7 +4339,7 @@
// scale function because we want to round things to make the crop
// always round to a larger rect to ensure we don't crop too
// much and hide part of the window that should be seen.
- if (mEnforceSizeCompat && mInvGlobalScale != 1.0f) {
+ if (inSizeCompatMode() && mInvGlobalScale != 1.0f) {
final float scale = mInvGlobalScale;
systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
@@ -4664,8 +4699,9 @@
// Since the parent was outset by its surface insets, we need to undo the outsetting
// with insetting by the same amount.
final WindowState parent = getParentWindow();
- outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
- -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
+ transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets);
+ outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
+ -parent.mWindowFrames.mFrame.top + mTmpPoint.y);
} else if (parentWindowContainer != null) {
final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
@@ -4683,7 +4719,22 @@
}
// Expand for surface insets. See WindowState.expandForSurfaceInsets.
- outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
+ transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
+ outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
+ }
+
+ /**
+ * The surface insets from layout parameter are in application coordinate. If the window is
+ * scaled, the insets also need to be scaled for surface position in global coordinate.
+ */
+ private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
+ if (!inSizeCompatMode()) {
+ outPos.x = surfaceInsets.left;
+ outPos.y = surfaceInsets.top;
+ return;
+ }
+ outPos.x = (int) (surfaceInsets.left * mGlobalScale + 0.5f);
+ outPos.y = (int) (surfaceInsets.top * mGlobalScale + 0.5f);
}
boolean needsRelativeLayeringToIme() {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 3b9b8ba..f0b9c62 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -182,6 +182,14 @@
}
/**
+ * @return The scale for applications running in compatibility mode. Multiply the size in the
+ * application by this scale will be the size in the screen.
+ */
+ float getSizeCompatScale() {
+ return mDisplayContent.mCompatibleScreenScale;
+ }
+
+ /**
* Returns true if the new window is considered greater than the existing window in terms of
* z-order.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index bc62de1..90cda4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -44,6 +44,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -211,6 +212,48 @@
}
@Test
+ public void testSizeCompatBounds() {
+ // The real surface transaction is unnecessary.
+ mToken.setSkipPrepareSurfaces(true);
+
+ final Rect fixedBounds = mToken.getRequestedOverrideConfiguration().windowConfiguration
+ .getBounds();
+ fixedBounds.set(0, 0, 1200, 1600);
+ final Configuration newParentConfig = mTask.getConfiguration();
+
+ // Change the size of the container to two times smaller with insets.
+ newParentConfig.windowConfiguration.setAppBounds(200, 0, 800, 800);
+ final Rect containerAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ final Rect containerBounds = newParentConfig.windowConfiguration.getBounds();
+ containerBounds.set(0, 0, 600, 800);
+ mToken.onConfigurationChanged(newParentConfig);
+
+ assertTrue(mToken.inSizeCompatMode());
+ assertEquals(containerAppBounds, mToken.getBounds());
+ assertEquals((float) containerAppBounds.width() / fixedBounds.width(),
+ mToken.getSizeCompatScale(), 0.0001f /* delta */);
+
+ // Change the width of the container to two times bigger.
+ containerAppBounds.set(0, 0, 2400, 1600);
+ containerBounds.set(containerAppBounds);
+ mToken.onConfigurationChanged(newParentConfig);
+
+ assertTrue(mToken.inSizeCompatMode());
+ // Don't scale up, so the bounds keep the same as the fixed width.
+ assertEquals(fixedBounds.width(), mToken.getBounds().width());
+ // Assert the position is horizontal center.
+ assertEquals((containerAppBounds.width() - fixedBounds.width()) / 2,
+ mToken.getBounds().left);
+ assertEquals(1f, mToken.getSizeCompatScale(), 0.0001f /* delta */);
+
+ // Change the width of the container to fit the fixed bounds.
+ containerBounds.set(0, 0, 1200, 2000);
+ mToken.onConfigurationChanged(newParentConfig);
+ // Assert don't use fixed bounds because the region is enough.
+ assertFalse(mToken.inSizeCompatMode());
+ }
+
+ @Test
@Presubmit
public void testGetOrientation() {
mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 0a4a8a4..fb30f8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -449,8 +449,7 @@
// Now simulate switch to fullscreen for letterboxed app.
final int xInset = logicalWidth / 10;
- final int yInset = logicalWidth / 10;
- final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset);
+ final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
config.windowConfiguration.setBounds(cf);
w.mAppToken.onRequestedOverrideConfigurationChanged(config);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index a494889..114eac9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -151,6 +151,7 @@
/** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
public static class TestAppWindowToken extends AppWindowToken {
boolean mOnTop = false;
+ private boolean mSkipPrepareSurfaces;
private Transaction mPendingTransactionOverride;
boolean mSkipOnParentChanged = true;
@@ -213,6 +214,17 @@
return mOnTop;
}
+ @Override
+ void prepareSurfaces() {
+ if (!mSkipPrepareSurfaces) {
+ super.prepareSurfaces();
+ }
+ }
+
+ void setSkipPrepareSurfaces(boolean ignore) {
+ mSkipPrepareSurfaces = ignore;
+ }
+
void setPendingTransaction(Transaction transaction) {
mPendingTransactionOverride = transaction;
}