Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c539b9b8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move letterboxing to AppWinToken"

parents 7ac85a3b 4d18a2e4
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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();
@@ -678,6 +680,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (destroyedSomething) {
            final DisplayContent dc = getDisplayContent();
            dc.assignWindowLayers(true /*setLayoutNeeded*/);
            updateLetterbox(null);
        }
    }

@@ -944,6 +947,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    void removeChild(WindowState child) {
        super.removeChild(child);
        checkKeyguardFlagsChanged();
        updateLetterbox(child);
    }

    private boolean waitingForReplacement() {
@@ -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
@@ -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);
            }
+1 −0
Original line number Diff line number Diff line
@@ -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);
+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;
            }
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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);

+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