1 /*
2  * Copyright (C) 2019 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.systemui.statusbar.phone;
18 
19 import static android.view.WindowInsets.Type.systemBars;
20 
21 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
22 
23 import android.content.Context;
24 import android.graphics.Insets;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.util.AttributeSet;
28 import android.util.Pair;
29 import android.view.Display;
30 import android.view.DisplayCutout;
31 import android.view.Gravity;
32 import android.view.View;
33 import android.view.WindowInsets;
34 import android.widget.FrameLayout;
35 
36 import com.android.systemui.util.leak.RotationUtils;
37 
38 /**
39  * Status bar view.
40  */
41 public class StatusBarWindowView extends FrameLayout {
42 
43     public static final String TAG = "PhoneStatusBarWindowView";
44     public static final boolean DEBUG = StatusBar.DEBUG;
45 
46     private int mLeftInset = 0;
47     private int mRightInset = 0;
48     private int mTopInset = 0;
49 
StatusBarWindowView(Context context, AttributeSet attrs)50     public StatusBarWindowView(Context context, AttributeSet attrs) {
51         super(context, attrs);
52     }
53 
54     @Override
onApplyWindowInsets(WindowInsets windowInsets)55     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
56         final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
57         mLeftInset = insets.left;
58         mRightInset = insets.right;
59         mTopInset = 0;
60         DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
61         if (displayCutout != null) {
62             mTopInset = displayCutout.getWaterfallInsets().top;
63         }
64         applyMargins();
65         return windowInsets;
66     }
67 
applyMargins()68     private void applyMargins() {
69         final int count = getChildCount();
70         for (int i = 0; i < count; i++) {
71             View child = getChildAt(i);
72             if (child.getLayoutParams() instanceof LayoutParams) {
73                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
74                 if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
75                         || lp.topMargin != mTopInset) {
76                     lp.rightMargin = mRightInset;
77                     lp.leftMargin = mLeftInset;
78                     lp.topMargin = mTopInset;
79                     child.requestLayout();
80                 }
81             }
82         }
83     }
84 
85     /**
86      * Compute the padding needed for status bar related views, e.g., PhoneStatusBar,
87      * QuickStatusBarHeader and KeyguardStatusBarView).
88      *
89      * @param cutout
90      * @param cornerCutoutPadding
91      * @param roundedCornerContentPadding
92      * @return
93      */
paddingNeededForCutoutAndRoundedCorner( DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding, int roundedCornerContentPadding)94     public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
95             DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
96             int roundedCornerContentPadding) {
97         if (cutout == null) {
98             return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
99         }
100 
101         // padding needed for corner cutout.
102         int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
103         int rightCornerCutoutPadding = cutout.getSafeInsetRight();
104         if (cornerCutoutPadding != null) {
105             leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
106             rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
107                     cornerCutoutPadding.second);
108         }
109 
110         return new Pair<>(
111                 Math.max(leftCornerCutoutPadding, roundedCornerContentPadding),
112                 Math.max(rightCornerCutoutPadding, roundedCornerContentPadding));
113     }
114 
115 
116     /**
117      * Compute the corner cutout margins in portrait mode
118      */
cornerCutoutMargins(DisplayCutout cutout, Display display)119     public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
120             Display display) {
121         return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0);
122     }
123 
124     /**
125      * Compute the corner cutout margins in the given orientation (exactRotation)
126      */
statusBarCornerCutoutMargins(DisplayCutout cutout, Display display, int exactRotation, int statusBarHeight)127     public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout,
128             Display display, int exactRotation, int statusBarHeight) {
129         if (cutout == null) {
130             return null;
131         }
132         Point size = new Point();
133         display.getRealSize(size);
134 
135         Rect bounds = new Rect();
136         switch (exactRotation) {
137             case RotationUtils.ROTATION_LANDSCAPE:
138                 boundsFromDirection(cutout, Gravity.LEFT, bounds);
139                 break;
140             case RotationUtils.ROTATION_SEASCAPE:
141                 boundsFromDirection(cutout, Gravity.RIGHT, bounds);
142                 break;
143             case RotationUtils.ROTATION_NONE:
144                 boundsFromDirection(cutout, Gravity.TOP, bounds);
145                 break;
146             case RotationUtils.ROTATION_UPSIDE_DOWN:
147                 // we assume the cutout is always on top in portrait mode
148                 return null;
149         }
150 
151         if (statusBarHeight >= 0 && bounds.top > statusBarHeight) {
152             return null;
153         }
154 
155         if (bounds.left <= 0) {
156             return new Pair<>(bounds.right, 0);
157         }
158 
159         if (bounds.right >= size.x) {
160             return new Pair<>(0, size.x - bounds.left);
161         }
162 
163         return null;
164     }
165 }
166