blob: 4eb021cd55dc370ee98ee8758b7dace3581185c4 [file] [log] [blame]
Adrian Roos4d18a2e2017-12-19 19:08:05 +01001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static android.view.SurfaceControl.HIDDEN;
20
21import android.graphics.Rect;
22import android.view.SurfaceControl;
23
24import java.util.function.Supplier;
25
26/**
27 * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an
28 * outer rect and an inner rect.
29 */
30public class Letterbox {
31
32 private static final Rect EMPTY_RECT = new Rect();
33
34 private final Supplier<SurfaceControl.Builder> mFactory;
35 private final Rect mOuter = new Rect();
36 private final Rect mInner = new Rect();
37 private final LetterboxSurface mTop = new LetterboxSurface("top");
38 private final LetterboxSurface mLeft = new LetterboxSurface("left");
39 private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
40 private final LetterboxSurface mRight = new LetterboxSurface("right");
41
42 /**
43 * Constructs a Letterbox.
44 *
45 * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
46 */
47 public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
48 mFactory = surfaceControlFactory;
49 }
50
51 /**
Adrian Roos23df3a32018-03-15 15:41:13 +010052 * Lays out the letterbox, such that the area between the outer and inner
Adrian Roos4d18a2e2017-12-19 19:08:05 +010053 * frames will be covered by black color surfaces.
54 *
Adrian Roos23df3a32018-03-15 15:41:13 +010055 * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
56 *
Adrian Roos4d18a2e2017-12-19 19:08:05 +010057 * @param outer the outer frame of the letterbox (this frame will be black, except the area
58 * that intersects with the {code inner} frame).
59 * @param inner the inner frame of the letterbox (this frame will be clear)
60 */
Adrian Roos23df3a32018-03-15 15:41:13 +010061 public void layout(Rect outer, Rect inner) {
Adrian Roos4d18a2e2017-12-19 19:08:05 +010062 mOuter.set(outer);
63 mInner.set(inner);
64
Adrian Roos23df3a32018-03-15 15:41:13 +010065 mTop.layout(outer.left, outer.top, inner.right, inner.top);
66 mLeft.layout(outer.left, inner.top, inner.left, outer.bottom);
67 mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom);
68 mRight.layout(inner.right, outer.top, outer.right, inner.bottom);
Adrian Roos4d18a2e2017-12-19 19:08:05 +010069 }
70
Adrian Roos23df3a32018-03-15 15:41:13 +010071
Adrian Roos4d18a2e2017-12-19 19:08:05 +010072 /**
Adrian Roos20e07892018-02-23 19:12:01 +010073 * Gets the insets between the outer and inner rects.
74 */
75 public Rect getInsets() {
76 return new Rect(
77 mLeft.getWidth(),
78 mTop.getHeight(),
79 mRight.getWidth(),
80 mBottom.getHeight());
81 }
82
83 /**
Adrian Roos23df3a32018-03-15 15:41:13 +010084 * Returns true if any part of the letterbox overlaps with the given {@code rect}.
85 */
86 public boolean isOverlappingWith(Rect rect) {
87 return mTop.isOverlappingWith(rect) || mLeft.isOverlappingWith(rect)
88 || mBottom.isOverlappingWith(rect) || mRight.isOverlappingWith(rect);
89 }
90
91 /**
Adrian Roos4d18a2e2017-12-19 19:08:05 +010092 * Hides the letterbox.
93 *
Adrian Roos23df3a32018-03-15 15:41:13 +010094 * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
Adrian Roos4d18a2e2017-12-19 19:08:05 +010095 */
Adrian Roos23df3a32018-03-15 15:41:13 +010096 public void hide() {
97 layout(EMPTY_RECT, EMPTY_RECT);
Adrian Roos4d18a2e2017-12-19 19:08:05 +010098 }
99
100 /**
101 * Destroys the managed {@link SurfaceControl}s.
102 */
103 public void destroy() {
104 mOuter.setEmpty();
105 mInner.setEmpty();
106
107 mTop.destroy();
108 mLeft.destroy();
109 mBottom.destroy();
110 mRight.destroy();
111 }
112
Adrian Roos23df3a32018-03-15 15:41:13 +0100113 /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
114 public boolean needsApplySurfaceChanges() {
115 return mTop.needsApplySurfaceChanges()
116 || mLeft.needsApplySurfaceChanges()
117 || mBottom.needsApplySurfaceChanges()
118 || mRight.needsApplySurfaceChanges();
119 }
120
121 public void applySurfaceChanges(SurfaceControl.Transaction t) {
122 mTop.applySurfaceChanges(t);
123 mLeft.applySurfaceChanges(t);
124 mBottom.applySurfaceChanges(t);
125 mRight.applySurfaceChanges(t);
126 }
127
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100128 private class LetterboxSurface {
129
130 private final String mType;
131 private SurfaceControl mSurface;
132
Adrian Roos23df3a32018-03-15 15:41:13 +0100133 private final Rect mSurfaceFrame = new Rect();
134 private final Rect mLayoutFrame = new Rect();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100135
136 public LetterboxSurface(String type) {
137 mType = type;
138 }
139
Adrian Roos23df3a32018-03-15 15:41:13 +0100140 public void layout(int left, int top, int right, int bottom) {
141 if (mLayoutFrame.left == left && mLayoutFrame.top == top
142 && mLayoutFrame.right == right && mLayoutFrame.bottom == bottom) {
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100143 // Nothing changed.
144 return;
145 }
Adrian Roos23df3a32018-03-15 15:41:13 +0100146 mLayoutFrame.set(left, top, right, bottom);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100147 }
148
149 private void createSurface() {
150 mSurface = mFactory.get().setName("Letterbox - " + mType)
151 .setFlags(HIDDEN).setColorLayer(true).build();
152 mSurface.setLayer(-1);
153 mSurface.setColor(new float[]{0, 0, 0});
154 }
155
156 public void destroy() {
157 if (mSurface != null) {
158 mSurface.destroy();
159 mSurface = null;
160 }
161 }
Adrian Roos20e07892018-02-23 19:12:01 +0100162
163 public int getWidth() {
Adrian Roos23df3a32018-03-15 15:41:13 +0100164 return Math.max(0, mLayoutFrame.width());
Adrian Roos20e07892018-02-23 19:12:01 +0100165 }
166
167 public int getHeight() {
Adrian Roos23df3a32018-03-15 15:41:13 +0100168 return Math.max(0, mLayoutFrame.height());
169 }
170
171 public boolean isOverlappingWith(Rect rect) {
172 if (getWidth() <= 0 || getHeight() <= 0) {
173 return false;
174 }
175 return Rect.intersects(rect, mLayoutFrame);
176 }
177
178 public void applySurfaceChanges(SurfaceControl.Transaction t) {
179 if (mSurfaceFrame.equals(mLayoutFrame)) {
180 // Nothing changed.
181 return;
182 }
183 mSurfaceFrame.set(mLayoutFrame);
184 if (!mSurfaceFrame.isEmpty()) {
185 if (mSurface == null) {
186 createSurface();
187 }
188 t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
189 t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
190 t.show(mSurface);
191 } else if (mSurface != null) {
192 t.hide(mSurface);
193 }
194 }
195
196 public boolean needsApplySurfaceChanges() {
197 return !mSurfaceFrame.equals(mLayoutFrame);
Adrian Roos20e07892018-02-23 19:12:01 +0100198 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100199 }
200}