blob: 0f9735dc23d4721b2e14bcee02be98fceef6889f [file] [log] [blame]
/*
* 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);
}
/**
* Gets the insets between the outer and inner rects.
*/
public Rect getInsets() {
return new Rect(
mLeft.getWidth(),
mTop.getHeight(),
mRight.getWidth(),
mBottom.getHeight());
}
/**
* 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;
}
}
public int getWidth() {
return Math.max(0, mLastRight - mLastLeft);
}
public int getHeight() {
return Math.max(0, mLastBottom - mLastTop);
}
}
}