Home | History | Annotate | Download | only in wm
      1 /*
      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 
     17 package com.android.server.wm;
     18 
     19 import static android.view.SurfaceControl.HIDDEN;
     20 
     21 import android.graphics.Rect;
     22 import android.view.SurfaceControl;
     23 
     24 import 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  */
     30 public 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     /**
     52      * Lays out the letterbox, such that the area between the outer and inner
     53      * frames will be covered by black color surfaces.
     54      *
     55      * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
     56      *
     57      * @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      */
     61     public void layout(Rect outer, Rect inner) {
     62         mOuter.set(outer);
     63         mInner.set(inner);
     64 
     65         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);
     69     }
     70 
     71 
     72     /**
     73      * 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     /**
     84      * 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     /**
     92      * Hides the letterbox.
     93      *
     94      * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
     95      */
     96     public void hide() {
     97         layout(EMPTY_RECT, EMPTY_RECT);
     98     }
     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 
    113     /** 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 
    128     private class LetterboxSurface {
    129 
    130         private final String mType;
    131         private SurfaceControl mSurface;
    132 
    133         private final Rect mSurfaceFrame = new Rect();
    134         private final Rect mLayoutFrame = new Rect();
    135 
    136         public LetterboxSurface(String type) {
    137             mType = type;
    138         }
    139 
    140         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) {
    143                 // Nothing changed.
    144                 return;
    145             }
    146             mLayoutFrame.set(left, top, right, bottom);
    147         }
    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         }
    162 
    163         public int getWidth() {
    164             return Math.max(0, mLayoutFrame.width());
    165         }
    166 
    167         public int getHeight() {
    168             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);
    198         }
    199     }
    200 }
    201