Loading services/core/java/com/android/server/wm/AppWindowToken.java +33 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.SurfaceControl.HIDDEN; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; Loading Loading @@ -245,6 +246,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree /** Whether this token should be boosted at the top of all app window tokens. */ private boolean mNeedsZBoost; private Letterbox mLetterbox; private final Point mTmpPoint = new Point(); private final Rect mTmpRect = new Rect(); Loading Loading @@ -678,6 +680,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (destroyedSomething) { final DisplayContent dc = getDisplayContent(); dc.assignWindowLayers(true /*setLayoutNeeded*/); updateLetterbox(null); } } Loading Loading @@ -944,6 +947,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void removeChild(WindowState child) { super.removeChild(child); checkKeyguardFlagsChanged(); updateLetterbox(child); } private boolean waitingForReplacement() { Loading Loading @@ -1409,6 +1413,33 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return isInterestingAndDrawn; } void updateLetterbox(WindowState winHint) { final WindowState w = findMainWindow(); if (w != winHint && winHint != null && w != null) { return; } final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow() && fillsParent() && w.hasDrawnLw(); if (needsLetterbox) { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null)); } mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame); } else if (mLetterbox != null) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.openTransaction(); try { mLetterbox.hide(t); } finally { // TODO: This should use pendingTransaction eventually, but right now things // happening on the animation finished callback are happening on the global // transaction. SurfaceControl.mergeToGlobalTransaction(t); SurfaceControl.closeTransaction(); } } } @Override boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent Loading Loading @@ -1635,6 +1666,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // the status bar). In that case we need to use the final frame. if (freeform) { frame.set(win.mFrame); } else if (win.isLetterboxedAppWindow()) { frame.set(getTask().getBounds()); } else { frame.set(win.mContainingFrame); } Loading services/core/java/com/android/server/wm/DisplayContent.java +1 −0 Original line number Diff line number Diff line Loading @@ -697,6 +697,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final AppWindowToken atoken = w.mAppToken; if (atoken != null) { atoken.updateLetterbox(w); final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w); if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) { mTmpUpdateAllDrawn.add(atoken); Loading services/core/java/com/android/server/wm/Letterbox.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import static android.view.SurfaceControl.HIDDEN; import android.graphics.Rect; import android.view.SurfaceControl; import java.util.function.Supplier; /** * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an * outer rect and an inner rect. */ public class Letterbox { private static final Rect EMPTY_RECT = new Rect(); private final Supplier<SurfaceControl.Builder> mFactory; private final Rect mOuter = new Rect(); private final Rect mInner = new Rect(); private final LetterboxSurface mTop = new LetterboxSurface("top"); private final LetterboxSurface mLeft = new LetterboxSurface("left"); private final LetterboxSurface mBottom = new LetterboxSurface("bottom"); private final LetterboxSurface mRight = new LetterboxSurface("right"); /** * Constructs a Letterbox. * * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s */ public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) { mFactory = surfaceControlFactory; } /** * Sets the dimensions of the the letterbox, such that the area between the outer and inner * frames will be covered by black color surfaces. * * @param t a transaction in which to set the dimensions * @param outer the outer frame of the letterbox (this frame will be black, except the area * that intersects with the {code inner} frame). * @param inner the inner frame of the letterbox (this frame will be clear) */ public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) { mOuter.set(outer); mInner.set(inner); mTop.setRect(t, outer.left, outer.top, inner.right, inner.top); mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom); mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom); mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom); } /** * Hides the letterbox. * * @param t a transaction in which to hide the letterbox */ public void hide(SurfaceControl.Transaction t) { setDimensions(t, EMPTY_RECT, EMPTY_RECT); } /** * Destroys the managed {@link SurfaceControl}s. */ public void destroy() { mOuter.setEmpty(); mInner.setEmpty(); mTop.destroy(); mLeft.destroy(); mBottom.destroy(); mRight.destroy(); } private class LetterboxSurface { private final String mType; private SurfaceControl mSurface; private int mLastLeft = 0; private int mLastTop = 0; private int mLastRight = 0; private int mLastBottom = 0; public LetterboxSurface(String type) { mType = type; } public void setRect(SurfaceControl.Transaction t, int left, int top, int right, int bottom) { if (mLastLeft == left && mLastTop == top && mLastRight == right && mLastBottom == bottom) { // Nothing changed. return; } if (left < right && top < bottom) { if (mSurface == null) { createSurface(); } t.setPosition(mSurface, left, top); t.setSize(mSurface, right - left, bottom - top); t.show(mSurface); } else if (mSurface != null) { t.hide(mSurface); } mLastLeft = left; mLastTop = top; mLastRight = right; mLastBottom = bottom; } private void createSurface() { mSurface = mFactory.get().setName("Letterbox - " + mType) .setFlags(HIDDEN).setColorLayer(true).build(); mSurface.setLayer(-1); mSurface.setColor(new float[]{0, 0, 0}); } public void destroy() { if (mSurface != null) { mSurface.destroy(); mSurface = null; } } } } services/core/java/com/android/server/wm/RemoteSurfaceTrace.java +2 −2 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.io.DataOutputStream; // the surface control. // // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side. class RemoteSurfaceTrace extends SurfaceControlWithBackground { class RemoteSurfaceTrace extends SurfaceControl { static final String TAG = "RemoteSurfaceTrace"; final FileDescriptor mWriteFd; Loading @@ -42,7 +42,7 @@ class RemoteSurfaceTrace extends SurfaceControlWithBackground { final WindowManagerService mService; final WindowState mWindow; RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped, RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) { super(wrapped); Loading services/core/java/com/android/server/wm/SurfaceControlWithBackground.javadeleted 100644 → 0 +0 −334 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.Parcel; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; /** * SurfaceControl extension that has black background behind navigation bar area for fullscreen * letterboxed apps. */ class SurfaceControlWithBackground extends SurfaceControl { // SurfaceControl that holds the background. private SurfaceControl mBackgroundControl; // Flag that defines whether the background should be shown. private boolean mVisible; // Way to communicate with corresponding window. private WindowSurfaceController mWindowSurfaceController; // Rect to hold task bounds when computing metrics for background. private Rect mTmpContainerRect = new Rect(); // Last metrics applied to the main SurfaceControl. private float mLastWidth, mLastHeight; private float mLastDsDx = 1, mLastDsDy = 1; private float mLastX, mLastY; // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive. // If we just set an empty crop it will behave as if there is no crop at all. // To fix this we explicitly hide the surface and won't let it to be shown. private boolean mHiddenForCrop = false; public SurfaceControlWithBackground(SurfaceControlWithBackground other) { super(other); mBackgroundControl = other.mBackgroundControl; mVisible = other.mVisible; mWindowSurfaceController = other.mWindowSurfaceController; } public SurfaceControlWithBackground(String name, SurfaceControl.Builder b, int windowType, int w, int h, WindowSurfaceController windowSurfaceController) throws OutOfResourcesException { super(b.build()); // We should only show background behind app windows that are letterboxed in a task. if ((windowType != TYPE_BASE_APPLICATION && windowType != TYPE_APPLICATION_STARTING) || !windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) { return; } mWindowSurfaceController = windowSurfaceController; mLastWidth = w; mLastHeight = h; mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl = b.setName("Background for - " + name) .setSize(mTmpContainerRect.width(), mTmpContainerRect.height()) .setFormat(OPAQUE) .setColorLayer(true) .build(); } @Override public void setAlpha(float alpha) { super.setAlpha(alpha); if (mBackgroundControl == null) { return; } mBackgroundControl.setAlpha(alpha); } @Override public void setLayer(int zorder) { super.setLayer(zorder); if (mBackgroundControl == null) { return; } // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed. mBackgroundControl.setLayer(zorder - 1); } @Override public void setPosition(float x, float y) { super.setPosition(x, y); if (mBackgroundControl == null) { return; } mLastX = x; mLastY = y; updateBgPosition(); } private void updateBgPosition() { mWindowSurfaceController.getContainerRect(mTmpContainerRect); final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx; final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy; mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY); } @Override public void setSize(int w, int h) { super.setSize(w, h); if (mBackgroundControl == null) { return; } mLastWidth = w; mLastHeight = h; mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height()); } @Override public void setWindowCrop(Rect crop) { super.setWindowCrop(crop); if (mBackgroundControl == null) { return; } calculateBgCrop(crop); mBackgroundControl.setWindowCrop(mTmpContainerRect); mHiddenForCrop = mTmpContainerRect.isEmpty(); updateBackgroundVisibility(); } @Override public void setFinalCrop(Rect crop) { super.setFinalCrop(crop); if (mBackgroundControl == null) { return; } mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl.setFinalCrop(mTmpContainerRect); } /** * Compute background crop based on current animation progress for main surface control and * update {@link #mTmpContainerRect} with new values. */ private void calculateBgCrop(Rect crop) { // Track overall progress of animation by computing cropped portion of status bar. final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets; float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top; if (d > 1.f) { // We're running expand animation from launcher, won't compute custom bg crop here. mTmpContainerRect.setEmpty(); return; } // Compute new scaled width and height for background that will depend on current animation // progress. Those consist of current crop rect for the main surface + scaled areas outside // of letterboxed area. // TODO: Because the progress is computed with low precision we're getting smaller values // for background width/height then screen size at the end of the animation. Will round when // the value is smaller then some empiric epsilon. However, this should be fixed by // computing correct frames for letterboxed windows in WindowState. d = d < 0.025f ? 0 : d; mWindowSurfaceController.getContainerRect(mTmpContainerRect); int backgroundWidth = 0, backgroundHeight = 0; // Compute additional offset for the background when app window is positioned not at (0,0). // E.g. landscape with navigation bar on the left. final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx), offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy); // Position and size background. final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition(); switch (bgPosition) { case NAV_BAR_LEFT: backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5); backgroundHeight = crop.height(); offsetX += crop.left - backgroundWidth; offsetY += crop.top; break; case NAV_BAR_RIGHT: backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5); backgroundHeight = crop.height(); offsetX += crop.right; offsetY += crop.top; break; case NAV_BAR_BOTTOM: backgroundWidth = crop.width(); backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5); offsetX += crop.left; offsetY += crop.bottom; break; } mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth, offsetY + backgroundHeight); } @Override public void setLayerStack(int layerStack) { super.setLayerStack(layerStack); if (mBackgroundControl == null) { return; } mBackgroundControl.setLayerStack(layerStack); } @Override public void setOpaque(boolean isOpaque) { super.setOpaque(isOpaque); updateBackgroundVisibility(); } @Override public void setSecure(boolean isSecure) { super.setSecure(isSecure); } @Override public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { super.setMatrix(dsdx, dtdx, dtdy, dsdy); if (mBackgroundControl == null) { return; } mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy); mLastDsDx = dsdx; mLastDsDy = dsdy; updateBgPosition(); } @Override public void hide() { super.hide(); mVisible = false; updateBackgroundVisibility(); } @Override public void show() { super.show(); mVisible = true; updateBackgroundVisibility(); } @Override public void destroy() { super.destroy(); if (mBackgroundControl == null) { return; } mBackgroundControl.destroy(); } @Override public void release() { super.release(); if (mBackgroundControl == null) { return; } mBackgroundControl.release(); } @Override public void setTransparentRegionHint(Region region) { super.setTransparentRegionHint(region); if (mBackgroundControl == null) { return; } mBackgroundControl.setTransparentRegionHint(region); } @Override public void deferTransactionUntil(IBinder handle, long frame) { super.deferTransactionUntil(handle, frame); if (mBackgroundControl == null) { return; } mBackgroundControl.deferTransactionUntil(handle, frame); } @Override public void deferTransactionUntil(Surface barrier, long frame) { super.deferTransactionUntil(barrier, frame); if (mBackgroundControl == null) { return; } mBackgroundControl.deferTransactionUntil(barrier, frame); } private void updateBackgroundVisibility() { if (mBackgroundControl == null) { return; } final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken; if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) { mBackgroundControl.show(); } else { mBackgroundControl.hide(); } } } Loading
services/core/java/com/android/server/wm/AppWindowToken.java +33 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.SurfaceControl.HIDDEN; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; Loading Loading @@ -245,6 +246,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree /** Whether this token should be boosted at the top of all app window tokens. */ private boolean mNeedsZBoost; private Letterbox mLetterbox; private final Point mTmpPoint = new Point(); private final Rect mTmpRect = new Rect(); Loading Loading @@ -678,6 +680,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (destroyedSomething) { final DisplayContent dc = getDisplayContent(); dc.assignWindowLayers(true /*setLayoutNeeded*/); updateLetterbox(null); } } Loading Loading @@ -944,6 +947,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void removeChild(WindowState child) { super.removeChild(child); checkKeyguardFlagsChanged(); updateLetterbox(child); } private boolean waitingForReplacement() { Loading Loading @@ -1409,6 +1413,33 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return isInterestingAndDrawn; } void updateLetterbox(WindowState winHint) { final WindowState w = findMainWindow(); if (w != winHint && winHint != null && w != null) { return; } final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow() && fillsParent() && w.hasDrawnLw(); if (needsLetterbox) { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null)); } mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame); } else if (mLetterbox != null) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.openTransaction(); try { mLetterbox.hide(t); } finally { // TODO: This should use pendingTransaction eventually, but right now things // happening on the animation finished callback are happening on the global // transaction. SurfaceControl.mergeToGlobalTransaction(t); SurfaceControl.closeTransaction(); } } } @Override boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent Loading Loading @@ -1635,6 +1666,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // the status bar). In that case we need to use the final frame. if (freeform) { frame.set(win.mFrame); } else if (win.isLetterboxedAppWindow()) { frame.set(getTask().getBounds()); } else { frame.set(win.mContainingFrame); } Loading
services/core/java/com/android/server/wm/DisplayContent.java +1 −0 Original line number Diff line number Diff line Loading @@ -697,6 +697,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final AppWindowToken atoken = w.mAppToken; if (atoken != null) { atoken.updateLetterbox(w); final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w); if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) { mTmpUpdateAllDrawn.add(atoken); Loading
services/core/java/com/android/server/wm/Letterbox.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import static android.view.SurfaceControl.HIDDEN; import android.graphics.Rect; import android.view.SurfaceControl; import java.util.function.Supplier; /** * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an * outer rect and an inner rect. */ public class Letterbox { private static final Rect EMPTY_RECT = new Rect(); private final Supplier<SurfaceControl.Builder> mFactory; private final Rect mOuter = new Rect(); private final Rect mInner = new Rect(); private final LetterboxSurface mTop = new LetterboxSurface("top"); private final LetterboxSurface mLeft = new LetterboxSurface("left"); private final LetterboxSurface mBottom = new LetterboxSurface("bottom"); private final LetterboxSurface mRight = new LetterboxSurface("right"); /** * Constructs a Letterbox. * * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s */ public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) { mFactory = surfaceControlFactory; } /** * Sets the dimensions of the the letterbox, such that the area between the outer and inner * frames will be covered by black color surfaces. * * @param t a transaction in which to set the dimensions * @param outer the outer frame of the letterbox (this frame will be black, except the area * that intersects with the {code inner} frame). * @param inner the inner frame of the letterbox (this frame will be clear) */ public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) { mOuter.set(outer); mInner.set(inner); mTop.setRect(t, outer.left, outer.top, inner.right, inner.top); mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom); mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom); mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom); } /** * Hides the letterbox. * * @param t a transaction in which to hide the letterbox */ public void hide(SurfaceControl.Transaction t) { setDimensions(t, EMPTY_RECT, EMPTY_RECT); } /** * Destroys the managed {@link SurfaceControl}s. */ public void destroy() { mOuter.setEmpty(); mInner.setEmpty(); mTop.destroy(); mLeft.destroy(); mBottom.destroy(); mRight.destroy(); } private class LetterboxSurface { private final String mType; private SurfaceControl mSurface; private int mLastLeft = 0; private int mLastTop = 0; private int mLastRight = 0; private int mLastBottom = 0; public LetterboxSurface(String type) { mType = type; } public void setRect(SurfaceControl.Transaction t, int left, int top, int right, int bottom) { if (mLastLeft == left && mLastTop == top && mLastRight == right && mLastBottom == bottom) { // Nothing changed. return; } if (left < right && top < bottom) { if (mSurface == null) { createSurface(); } t.setPosition(mSurface, left, top); t.setSize(mSurface, right - left, bottom - top); t.show(mSurface); } else if (mSurface != null) { t.hide(mSurface); } mLastLeft = left; mLastTop = top; mLastRight = right; mLastBottom = bottom; } private void createSurface() { mSurface = mFactory.get().setName("Letterbox - " + mType) .setFlags(HIDDEN).setColorLayer(true).build(); mSurface.setLayer(-1); mSurface.setColor(new float[]{0, 0, 0}); } public void destroy() { if (mSurface != null) { mSurface.destroy(); mSurface = null; } } } }
services/core/java/com/android/server/wm/RemoteSurfaceTrace.java +2 −2 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.io.DataOutputStream; // the surface control. // // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side. class RemoteSurfaceTrace extends SurfaceControlWithBackground { class RemoteSurfaceTrace extends SurfaceControl { static final String TAG = "RemoteSurfaceTrace"; final FileDescriptor mWriteFd; Loading @@ -42,7 +42,7 @@ class RemoteSurfaceTrace extends SurfaceControlWithBackground { final WindowManagerService mService; final WindowState mWindow; RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped, RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) { super(wrapped); Loading
services/core/java/com/android/server/wm/SurfaceControlWithBackground.javadeleted 100644 → 0 +0 −334 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.Parcel; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; /** * SurfaceControl extension that has black background behind navigation bar area for fullscreen * letterboxed apps. */ class SurfaceControlWithBackground extends SurfaceControl { // SurfaceControl that holds the background. private SurfaceControl mBackgroundControl; // Flag that defines whether the background should be shown. private boolean mVisible; // Way to communicate with corresponding window. private WindowSurfaceController mWindowSurfaceController; // Rect to hold task bounds when computing metrics for background. private Rect mTmpContainerRect = new Rect(); // Last metrics applied to the main SurfaceControl. private float mLastWidth, mLastHeight; private float mLastDsDx = 1, mLastDsDy = 1; private float mLastX, mLastY; // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive. // If we just set an empty crop it will behave as if there is no crop at all. // To fix this we explicitly hide the surface and won't let it to be shown. private boolean mHiddenForCrop = false; public SurfaceControlWithBackground(SurfaceControlWithBackground other) { super(other); mBackgroundControl = other.mBackgroundControl; mVisible = other.mVisible; mWindowSurfaceController = other.mWindowSurfaceController; } public SurfaceControlWithBackground(String name, SurfaceControl.Builder b, int windowType, int w, int h, WindowSurfaceController windowSurfaceController) throws OutOfResourcesException { super(b.build()); // We should only show background behind app windows that are letterboxed in a task. if ((windowType != TYPE_BASE_APPLICATION && windowType != TYPE_APPLICATION_STARTING) || !windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) { return; } mWindowSurfaceController = windowSurfaceController; mLastWidth = w; mLastHeight = h; mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl = b.setName("Background for - " + name) .setSize(mTmpContainerRect.width(), mTmpContainerRect.height()) .setFormat(OPAQUE) .setColorLayer(true) .build(); } @Override public void setAlpha(float alpha) { super.setAlpha(alpha); if (mBackgroundControl == null) { return; } mBackgroundControl.setAlpha(alpha); } @Override public void setLayer(int zorder) { super.setLayer(zorder); if (mBackgroundControl == null) { return; } // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed. mBackgroundControl.setLayer(zorder - 1); } @Override public void setPosition(float x, float y) { super.setPosition(x, y); if (mBackgroundControl == null) { return; } mLastX = x; mLastY = y; updateBgPosition(); } private void updateBgPosition() { mWindowSurfaceController.getContainerRect(mTmpContainerRect); final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx; final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy; mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY); } @Override public void setSize(int w, int h) { super.setSize(w, h); if (mBackgroundControl == null) { return; } mLastWidth = w; mLastHeight = h; mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height()); } @Override public void setWindowCrop(Rect crop) { super.setWindowCrop(crop); if (mBackgroundControl == null) { return; } calculateBgCrop(crop); mBackgroundControl.setWindowCrop(mTmpContainerRect); mHiddenForCrop = mTmpContainerRect.isEmpty(); updateBackgroundVisibility(); } @Override public void setFinalCrop(Rect crop) { super.setFinalCrop(crop); if (mBackgroundControl == null) { return; } mWindowSurfaceController.getContainerRect(mTmpContainerRect); mBackgroundControl.setFinalCrop(mTmpContainerRect); } /** * Compute background crop based on current animation progress for main surface control and * update {@link #mTmpContainerRect} with new values. */ private void calculateBgCrop(Rect crop) { // Track overall progress of animation by computing cropped portion of status bar. final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets; float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top; if (d > 1.f) { // We're running expand animation from launcher, won't compute custom bg crop here. mTmpContainerRect.setEmpty(); return; } // Compute new scaled width and height for background that will depend on current animation // progress. Those consist of current crop rect for the main surface + scaled areas outside // of letterboxed area. // TODO: Because the progress is computed with low precision we're getting smaller values // for background width/height then screen size at the end of the animation. Will round when // the value is smaller then some empiric epsilon. However, this should be fixed by // computing correct frames for letterboxed windows in WindowState. d = d < 0.025f ? 0 : d; mWindowSurfaceController.getContainerRect(mTmpContainerRect); int backgroundWidth = 0, backgroundHeight = 0; // Compute additional offset for the background when app window is positioned not at (0,0). // E.g. landscape with navigation bar on the left. final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx), offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy); // Position and size background. final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition(); switch (bgPosition) { case NAV_BAR_LEFT: backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5); backgroundHeight = crop.height(); offsetX += crop.left - backgroundWidth; offsetY += crop.top; break; case NAV_BAR_RIGHT: backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5); backgroundHeight = crop.height(); offsetX += crop.right; offsetY += crop.top; break; case NAV_BAR_BOTTOM: backgroundWidth = crop.width(); backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5); offsetX += crop.left; offsetY += crop.bottom; break; } mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth, offsetY + backgroundHeight); } @Override public void setLayerStack(int layerStack) { super.setLayerStack(layerStack); if (mBackgroundControl == null) { return; } mBackgroundControl.setLayerStack(layerStack); } @Override public void setOpaque(boolean isOpaque) { super.setOpaque(isOpaque); updateBackgroundVisibility(); } @Override public void setSecure(boolean isSecure) { super.setSecure(isSecure); } @Override public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { super.setMatrix(dsdx, dtdx, dtdy, dsdy); if (mBackgroundControl == null) { return; } mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy); mLastDsDx = dsdx; mLastDsDy = dsdy; updateBgPosition(); } @Override public void hide() { super.hide(); mVisible = false; updateBackgroundVisibility(); } @Override public void show() { super.show(); mVisible = true; updateBackgroundVisibility(); } @Override public void destroy() { super.destroy(); if (mBackgroundControl == null) { return; } mBackgroundControl.destroy(); } @Override public void release() { super.release(); if (mBackgroundControl == null) { return; } mBackgroundControl.release(); } @Override public void setTransparentRegionHint(Region region) { super.setTransparentRegionHint(region); if (mBackgroundControl == null) { return; } mBackgroundControl.setTransparentRegionHint(region); } @Override public void deferTransactionUntil(IBinder handle, long frame) { super.deferTransactionUntil(handle, frame); if (mBackgroundControl == null) { return; } mBackgroundControl.deferTransactionUntil(handle, frame); } @Override public void deferTransactionUntil(Surface barrier, long frame) { super.deferTransactionUntil(barrier, frame); if (mBackgroundControl == null) { return; } mBackgroundControl.deferTransactionUntil(barrier, frame); } private void updateBackgroundVisibility() { if (mBackgroundControl == null) { return; } final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken; if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) { mBackgroundControl.show(); } else { mBackgroundControl.hide(); } } }