blob: ab7eec52a959bc47f7f61bdc18ebb983071ca231 [file] [log] [blame]
Adrian Roos5b518852018-01-23 17:23:38 +01001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui;
16
Adrian Roos51072a82018-04-10 15:17:08 -070017import static android.view.Surface.ROTATION_0;
18import static android.view.Surface.ROTATION_180;
19import static android.view.Surface.ROTATION_270;
20import static android.view.Surface.ROTATION_90;
Adrian Roos5b518852018-01-23 17:23:38 +010021import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
23import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
24
Adrian Roos5b518852018-01-23 17:23:38 +010025import static com.android.systemui.tuner.TunablePadding.FLAG_END;
Beverlye91f0d02018-05-15 14:40:47 -040026import static com.android.systemui.tuner.TunablePadding.FLAG_START;
Adrian Roos5b518852018-01-23 17:23:38 +010027
Adrian Roos51072a82018-04-10 15:17:08 -070028import android.annotation.Dimension;
Beverly374acde2018-06-13 10:49:26 -040029import android.app.ActivityManager;
Adrian Roos5b518852018-01-23 17:23:38 +010030import android.app.Fragment;
Beverly374acde2018-06-13 10:49:26 -040031import android.content.BroadcastReceiver;
Adrian Roos5b518852018-01-23 17:23:38 +010032import android.content.Context;
Beverly374acde2018-06-13 10:49:26 -040033import android.content.Intent;
34import android.content.IntentFilter;
Adrian Roos5b518852018-01-23 17:23:38 +010035import android.content.res.ColorStateList;
36import android.content.res.Configuration;
37import android.graphics.Canvas;
38import android.graphics.Color;
Adrian Roos51072a82018-04-10 15:17:08 -070039import android.graphics.Matrix;
Adrian Roos5b518852018-01-23 17:23:38 +010040import android.graphics.Paint;
41import android.graphics.Path;
42import android.graphics.PixelFormat;
43import android.graphics.Rect;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010044import android.graphics.Region;
Adrian Roos5b518852018-01-23 17:23:38 +010045import android.hardware.display.DisplayManager;
Adrian Roosfaa102f2018-08-02 15:56:15 +020046import android.os.Handler;
47import android.os.HandlerThread;
Adrian Roos56d1a2c2018-03-08 23:22:19 +010048import android.os.SystemProperties;
Adrian Roos5b518852018-01-23 17:23:38 +010049import android.provider.Settings.Secure;
Adrian Roos5b518852018-01-23 17:23:38 +010050import android.util.DisplayMetrics;
Adrian Roosfaa102f2018-08-02 15:56:15 +020051import android.util.Log;
Adrian Roos5b518852018-01-23 17:23:38 +010052import android.view.DisplayCutout;
53import android.view.DisplayInfo;
54import android.view.Gravity;
55import android.view.LayoutInflater;
Adrian Roos51072a82018-04-10 15:17:08 -070056import android.view.Surface;
Adrian Roos5b518852018-01-23 17:23:38 +010057import android.view.View;
58import android.view.View.OnLayoutChangeListener;
59import android.view.ViewGroup;
60import android.view.ViewGroup.LayoutParams;
Vishnu Nair83537a72018-07-19 21:27:48 -070061import android.view.ViewTreeObserver;
Adrian Roos5b518852018-01-23 17:23:38 +010062import android.view.WindowManager;
63import android.widget.FrameLayout;
64import android.widget.ImageView;
65
Gus Prevasab336792018-11-14 13:52:20 -050066import androidx.annotation.VisibleForTesting;
67
Adrian Roosfaa102f2018-08-02 15:56:15 +020068import com.android.internal.util.Preconditions;
Evan Lairdb0506ca2018-03-15 10:34:31 -040069import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000070import com.android.systemui.broadcast.BroadcastDispatcher;
71import com.android.systemui.dagger.qualifiers.MainHandler;
Adrian Roos5b518852018-01-23 17:23:38 +010072import com.android.systemui.fragments.FragmentHostManager;
73import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
74import com.android.systemui.plugins.qs.QS;
75import com.android.systemui.qs.SecureSetting;
76import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
77import com.android.systemui.statusbar.phone.StatusBar;
78import com.android.systemui.tuner.TunablePadding;
79import com.android.systemui.tuner.TunerService;
80import com.android.systemui.tuner.TunerService.Tunable;
Beverlye91f0d02018-05-15 14:40:47 -040081import com.android.systemui.util.leak.RotationUtils;
Adrian Roos5b518852018-01-23 17:23:38 +010082
Issei Suzuki43190bd2018-08-20 17:28:41 +020083import java.util.ArrayList;
84import java.util.List;
85
Dave Mankoff4ddc25b2019-10-23 15:46:08 -040086import javax.inject.Inject;
87import javax.inject.Singleton;
88
89import dagger.Lazy;
90
Adrian Roos5b518852018-01-23 17:23:38 +010091/**
92 * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
93 * for antialiasing and emulation purposes.
94 */
Dave Mankoff4ddc25b2019-10-23 15:46:08 -040095@Singleton
shawnlin87af5382019-09-13 14:13:13 +080096public class ScreenDecorations extends SystemUI implements Tunable {
Adrian Roosfaa102f2018-08-02 15:56:15 +020097 private static final boolean DEBUG = false;
98 private static final String TAG = "ScreenDecorations";
99
Adrian Roos5b518852018-01-23 17:23:38 +0100100 public static final String SIZE = "sysui_rounded_size";
101 public static final String PADDING = "sysui_rounded_content_padding";
Adrian Roos56d1a2c2018-03-08 23:22:19 +0100102 private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
103 SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
James O'Leary4335c702019-05-29 12:38:51 -0400104 private static final boolean VERBOSE = false;
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400105 private final Lazy<StatusBar> mStatusBarLazy;
Adrian Roos5b518852018-01-23 17:23:38 +0100106
Beverlye91f0d02018-05-15 14:40:47 -0400107 private DisplayManager mDisplayManager;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000108 private final BroadcastDispatcher mBroadcastDispatcher;
109 private final Handler mMainHandler;
110 private final TunerService mTunerService;
Beverlye91f0d02018-05-15 14:40:47 -0400111 private DisplayManager.DisplayListener mDisplayListener;
112
James O'Leary4335c702019-05-29 12:38:51 -0400113 @VisibleForTesting
114 protected int mRoundedDefault;
115 @VisibleForTesting
116 protected int mRoundedDefaultTop;
117 @VisibleForTesting
118 protected int mRoundedDefaultBottom;
Adrian Roos5b518852018-01-23 17:23:38 +0100119 private View mOverlay;
120 private View mBottomOverlay;
121 private float mDensity;
122 private WindowManager mWindowManager;
Beverlye91f0d02018-05-15 14:40:47 -0400123 private int mRotation;
Adrian Roos9acf8132018-05-31 19:54:53 +0200124 private DisplayCutoutView mCutoutTop;
125 private DisplayCutoutView mCutoutBottom;
Beverly374acde2018-06-13 10:49:26 -0400126 private SecureSetting mColorInversionSetting;
Vishnu Nair83537a72018-07-19 21:27:48 -0700127 private boolean mPendingRotationChange;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200128 private Handler mHandler;
Adrian Roos5b518852018-01-23 17:23:38 +0100129
Issei Suzuki43190bd2018-08-20 17:28:41 +0200130 /**
131 * Converts a set of {@link Rect}s into a {@link Region}
132 *
133 * @hide
134 */
135 public static Region rectsToRegion(List<Rect> rects) {
136 Region result = Region.obtain();
137 if (rects != null) {
138 for (Rect r : rects) {
139 if (r != null && !r.isEmpty()) {
140 result.op(r, Region.Op.UNION);
141 }
142 }
143 }
144 return result;
145 }
146
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400147 @Inject
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000148 public ScreenDecorations(Context context,
149 Lazy<StatusBar> statusBarLazy,
150 @MainHandler Handler handler,
151 BroadcastDispatcher broadcastDispatcher,
152 TunerService tunerService) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400153 super(context);
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400154 mStatusBarLazy = statusBarLazy;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000155 mMainHandler = handler;
156 mBroadcastDispatcher = broadcastDispatcher;
157 mTunerService = tunerService;
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400158 }
159
Adrian Roos5b518852018-01-23 17:23:38 +0100160 @Override
161 public void start() {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200162 mHandler = startHandlerThread();
163 mHandler.post(this::startOnScreenDecorationsThread);
164 setupStatusBarPaddingIfNeeded();
James O'Leary4335c702019-05-29 12:38:51 -0400165 }
166
Adrian Roosfaa102f2018-08-02 15:56:15 +0200167 @VisibleForTesting
168 Handler startHandlerThread() {
169 HandlerThread thread = new HandlerThread("ScreenDecorations");
170 thread.start();
171 return thread.getThreadHandler();
172 }
173
174 private void startOnScreenDecorationsThread() {
175 mRotation = RotationUtils.getExactRotation(mContext);
Adrian Roos5b518852018-01-23 17:23:38 +0100176 mWindowManager = mContext.getSystemService(WindowManager.class);
Adrian Roos64c9d902018-08-20 13:43:38 +0200177 updateRoundedCornerRadii();
shawnlin87af5382019-09-13 14:13:13 +0800178 if (hasRoundedCorners() || shouldDrawCutout()) {
Adrian Roos5b518852018-01-23 17:23:38 +0100179 setupDecorations();
180 }
Beverlya5f7a302018-04-25 09:19:05 -0400181
Beverlye91f0d02018-05-15 14:40:47 -0400182 mDisplayListener = new DisplayManager.DisplayListener() {
183 @Override
184 public void onDisplayAdded(int displayId) {
185 // do nothing
186 }
187
188 @Override
189 public void onDisplayRemoved(int displayId) {
190 // do nothing
191 }
192
193 @Override
194 public void onDisplayChanged(int displayId) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200195 final int newRotation = RotationUtils.getExactRotation(mContext);
196 if (mOverlay != null && mBottomOverlay != null && mRotation != newRotation) {
Vishnu Nair83537a72018-07-19 21:27:48 -0700197 // We cannot immediately update the orientation. Otherwise
198 // WindowManager is still deferring layout until it has finished dispatching
199 // the config changes, which may cause divergence between what we draw
200 // (new orientation), and where we are placed on the screen (old orientation).
201 // Instead we wait until either:
202 // - we are trying to redraw. This because WM resized our window and told us to.
203 // - the config change has been dispatched, so WM is no longer deferring layout.
204 mPendingRotationChange = true;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200205 if (DEBUG) {
206 Log.i(TAG, "Rotation changed, deferring " + newRotation + ", staying at "
207 + mRotation);
208 }
209
Vishnu Nair83537a72018-07-19 21:27:48 -0700210 mOverlay.getViewTreeObserver().addOnPreDrawListener(
Adrian Roosfaa102f2018-08-02 15:56:15 +0200211 new RestartingPreDrawListener(mOverlay, newRotation));
Vishnu Nair83537a72018-07-19 21:27:48 -0700212 mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
Adrian Roosfaa102f2018-08-02 15:56:15 +0200213 new RestartingPreDrawListener(mBottomOverlay, newRotation));
Vishnu Nair83537a72018-07-19 21:27:48 -0700214 }
Beverlye91f0d02018-05-15 14:40:47 -0400215 updateOrientation();
216 }
217 };
218
Beverlye91f0d02018-05-15 14:40:47 -0400219 mDisplayManager = (DisplayManager) mContext.getSystemService(
220 Context.DISPLAY_SERVICE);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200221 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
222 updateOrientation();
Adrian Roos5b518852018-01-23 17:23:38 +0100223 }
224
225 private void setupDecorations() {
226 mOverlay = LayoutInflater.from(mContext)
227 .inflate(R.layout.rounded_corners, null);
Adrian Roos9acf8132018-05-31 19:54:53 +0200228 mCutoutTop = new DisplayCutoutView(mContext, true,
Vishnu Nair83537a72018-07-19 21:27:48 -0700229 this::updateWindowVisibilities, this);
James O'Leary4335c702019-05-29 12:38:51 -0400230 ((ViewGroup) mOverlay).addView(mCutoutTop);
Adrian Roos5b518852018-01-23 17:23:38 +0100231 mBottomOverlay = LayoutInflater.from(mContext)
232 .inflate(R.layout.rounded_corners, null);
Adrian Roos9acf8132018-05-31 19:54:53 +0200233 mCutoutBottom = new DisplayCutoutView(mContext, false,
Vishnu Nair83537a72018-07-19 21:27:48 -0700234 this::updateWindowVisibilities, this);
James O'Leary4335c702019-05-29 12:38:51 -0400235 ((ViewGroup) mBottomOverlay).addView(mCutoutBottom);
Adrian Roos5b518852018-01-23 17:23:38 +0100236
237 mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
238 mOverlay.setAlpha(0);
John Recka2d20b42018-10-01 12:21:55 -0700239 mOverlay.setForceDarkAllowed(false);
Adrian Roos5b518852018-01-23 17:23:38 +0100240
241 mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
242 mBottomOverlay.setAlpha(0);
John Recka2d20b42018-10-01 12:21:55 -0700243 mBottomOverlay.setForceDarkAllowed(false);
Adrian Roos5b518852018-01-23 17:23:38 +0100244
245 updateViews();
246
247 mWindowManager.addView(mOverlay, getWindowLayoutParams());
248 mWindowManager.addView(mBottomOverlay, getBottomLayoutParams());
249
250 DisplayMetrics metrics = new DisplayMetrics();
251 mWindowManager.getDefaultDisplay().getMetrics(metrics);
252 mDensity = metrics.density;
253
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000254 mMainHandler.post(() -> mTunerService.addTunable(this, SIZE));
Adrian Roos5b518852018-01-23 17:23:38 +0100255
256 // Watch color inversion and invert the overlay as needed.
Adrian Roosfaa102f2018-08-02 15:56:15 +0200257 mColorInversionSetting = new SecureSetting(mContext, mHandler,
Adrian Roos5b518852018-01-23 17:23:38 +0100258 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
259 @Override
260 protected void handleValueChanged(int value, boolean observedChange) {
Beverly374acde2018-06-13 10:49:26 -0400261 updateColorInversion(value);
Adrian Roos5b518852018-01-23 17:23:38 +0100262 }
263 };
Beverly374acde2018-06-13 10:49:26 -0400264 mColorInversionSetting.setListening(true);
265 mColorInversionSetting.onChange(false);
266
267 IntentFilter filter = new IntentFilter();
268 filter.addAction(Intent.ACTION_USER_SWITCHED);
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000269 mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler);
Adrian Roos5b518852018-01-23 17:23:38 +0100270
271 mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
272 @Override
273 public void onLayoutChange(View v, int left, int top, int right, int bottom,
274 int oldLeft,
275 int oldTop, int oldRight, int oldBottom) {
276 mOverlay.removeOnLayoutChangeListener(this);
277 mOverlay.animate()
278 .alpha(1)
279 .setDuration(1000)
280 .start();
281 mBottomOverlay.animate()
282 .alpha(1)
283 .setDuration(1000)
284 .start();
285 }
286 });
Adrian Roosfaa102f2018-08-02 15:56:15 +0200287
288 mOverlay.getViewTreeObserver().addOnPreDrawListener(
289 new ValidatingPreDrawListener(mOverlay));
290 mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
291 new ValidatingPreDrawListener(mBottomOverlay));
Adrian Roos5b518852018-01-23 17:23:38 +0100292 }
293
Beverly374acde2018-06-13 10:49:26 -0400294 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
295 @Override
296 public void onReceive(Context context, Intent intent) {
297 String action = intent.getAction();
298 if (action.equals(Intent.ACTION_USER_SWITCHED)) {
299 int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
300 ActivityManager.getCurrentUser());
301 // update color inversion setting to the new user
302 mColorInversionSetting.setUserId(newUserId);
303 updateColorInversion(mColorInversionSetting.getValue());
304 }
305 }
306 };
307
308 private void updateColorInversion(int colorsInvertedValue) {
309 int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
310 ColorStateList tintList = ColorStateList.valueOf(tint);
311 ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
312 ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
313 ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
314 ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
315 mCutoutTop.setColor(tint);
316 mCutoutBottom.setColor(tint);
317 }
318
Adrian Roos5b518852018-01-23 17:23:38 +0100319 @Override
320 protected void onConfigurationChanged(Configuration newConfig) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200321 mHandler.post(() -> {
322 int oldRotation = mRotation;
323 mPendingRotationChange = false;
324 updateOrientation();
Adrian Roos64c9d902018-08-20 13:43:38 +0200325 updateRoundedCornerRadii();
Adrian Roosfaa102f2018-08-02 15:56:15 +0200326 if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
327 if (shouldDrawCutout() && mOverlay == null) {
328 setupDecorations();
329 }
330 if (mOverlay != null) {
331 // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(),
332 // which ensures that the forced seamless rotation will end, even if we updated
333 // the rotation before window manager was ready (and was still waiting for sending
334 // the updated rotation).
335 updateLayoutParams();
336 }
337 });
Beverlye91f0d02018-05-15 14:40:47 -0400338 }
339
Adrian Roosfaa102f2018-08-02 15:56:15 +0200340 private void updateOrientation() {
341 Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
342 "must call on " + mHandler.getLooper().getThread()
343 + ", but was " + Thread.currentThread());
Vishnu Nair83537a72018-07-19 21:27:48 -0700344 if (mPendingRotationChange) {
345 return;
346 }
Beverlye91f0d02018-05-15 14:40:47 -0400347 int newRotation = RotationUtils.getExactRotation(mContext);
348 if (newRotation != mRotation) {
349 mRotation = newRotation;
Adrian Roos5b518852018-01-23 17:23:38 +0100350
351 if (mOverlay != null) {
352 updateLayoutParams();
353 updateViews();
354 }
Adrian Roos5b518852018-01-23 17:23:38 +0100355 }
356 }
357
Adrian Roos64c9d902018-08-20 13:43:38 +0200358 private void updateRoundedCornerRadii() {
359 final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500360 com.android.internal.R.dimen.rounded_corner_radius);
Adrian Roos64c9d902018-08-20 13:43:38 +0200361 final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500362 com.android.internal.R.dimen.rounded_corner_radius_top);
Adrian Roos64c9d902018-08-20 13:43:38 +0200363 final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500364 com.android.internal.R.dimen.rounded_corner_radius_bottom);
Adrian Roos64c9d902018-08-20 13:43:38 +0200365
366 final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
367 || mRoundedDefaultBottom != newRoundedDefaultBottom
368 || mRoundedDefaultTop != newRoundedDefaultTop;
369
370 if (roundedCornersChanged) {
371 mRoundedDefault = newRoundedDefault;
372 mRoundedDefaultTop = newRoundedDefaultTop;
373 mRoundedDefaultBottom = newRoundedDefaultBottom;
374 onTuningChanged(SIZE, null);
375 }
376 }
377
Adrian Roos5b518852018-01-23 17:23:38 +0100378 private void updateViews() {
379 View topLeft = mOverlay.findViewById(R.id.left);
380 View topRight = mOverlay.findViewById(R.id.right);
381 View bottomLeft = mBottomOverlay.findViewById(R.id.left);
382 View bottomRight = mBottomOverlay.findViewById(R.id.right);
Beverlye91f0d02018-05-15 14:40:47 -0400383
384 if (mRotation == RotationUtils.ROTATION_NONE) {
385 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
386 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
387 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
388 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
389 } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
390 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
391 updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270);
James O'Leary4335c702019-05-29 12:38:51 -0400392 updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);
Beverlye91f0d02018-05-15 14:40:47 -0400393 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
394 } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
395 updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
396 updateView(topRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
397 updateView(bottomLeft, Gravity.TOP | Gravity.LEFT, 0);
398 updateView(bottomRight, Gravity.TOP | Gravity.RIGHT, 90);
399 } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) {
400 updateView(topLeft, Gravity.BOTTOM | Gravity.RIGHT, 180);
401 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
402 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
403 updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0);
Adrian Roos5b518852018-01-23 17:23:38 +0100404 }
Adrian Roos5b518852018-01-23 17:23:38 +0100405
Adrian Roos9acf8132018-05-31 19:54:53 +0200406 mCutoutTop.setRotation(mRotation);
407 mCutoutBottom.setRotation(mRotation);
408
Adrian Roos5b518852018-01-23 17:23:38 +0100409 updateWindowVisibilities();
410 }
411
412 private void updateView(View v, int gravity, int rotation) {
James O'Leary4335c702019-05-29 12:38:51 -0400413 ((FrameLayout.LayoutParams) v.getLayoutParams()).gravity = gravity;
Adrian Roos5b518852018-01-23 17:23:38 +0100414 v.setRotation(rotation);
415 }
416
417 private void updateWindowVisibilities() {
418 updateWindowVisibility(mOverlay);
419 updateWindowVisibility(mBottomOverlay);
420 }
421
422 private void updateWindowVisibility(View overlay) {
423 boolean visibleForCutout = shouldDrawCutout()
424 && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
Beverlya5f7a302018-04-25 09:19:05 -0400425 boolean visibleForRoundedCorners = hasRoundedCorners();
shawnlin87af5382019-09-13 14:13:13 +0800426 overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
Adrian Roos5b518852018-01-23 17:23:38 +0100427 ? View.VISIBLE : View.GONE);
428 }
429
Beverlya5f7a302018-04-25 09:19:05 -0400430 private boolean hasRoundedCorners() {
431 return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
432 }
433
Adrian Roos5b518852018-01-23 17:23:38 +0100434 private boolean shouldDrawCutout() {
Adrian Roosc41b32b2018-04-12 10:13:48 +0200435 return shouldDrawCutout(mContext);
436 }
437
438 static boolean shouldDrawCutout(Context context) {
439 return context.getResources().getBoolean(
Adrian Roos5b518852018-01-23 17:23:38 +0100440 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
441 }
442
Adrian Roosfaa102f2018-08-02 15:56:15 +0200443
444 private void setupStatusBarPaddingIfNeeded() {
445 // TODO: This should be moved to a more appropriate place, as it is not related to the
446 // screen decorations overlay.
447 int padding = mContext.getResources().getDimensionPixelSize(
448 R.dimen.rounded_corner_content_padding);
449 if (padding != 0) {
450 setupStatusBarPadding(padding);
451 }
452
453 }
454
455 private void setupStatusBarPadding(int padding) {
Adrian Roos5b518852018-01-23 17:23:38 +0100456 // Add some padding to all the content near the edge of the screen.
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400457 StatusBar statusBar = mStatusBarLazy.get();
458 View statusBarWindow = statusBar.getStatusBarWindow();
459 if (statusBarWindow != null) {
460 TunablePadding.addTunablePadding(statusBarWindow.findViewById(R.id.keyguard_header),
461 PADDING, padding, FLAG_END);
Adrian Roos5b518852018-01-23 17:23:38 +0100462
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400463 FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBarWindow);
Adrian Roos5b518852018-01-23 17:23:38 +0100464 fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
465 new TunablePaddingTagListener(padding, R.id.status_bar));
466 fragmentHostManager.addTagListener(QS.TAG,
467 new TunablePaddingTagListener(padding, R.id.header));
468 }
469 }
470
471 @VisibleForTesting
472 WindowManager.LayoutParams getWindowLayoutParams() {
473 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
474 ViewGroup.LayoutParams.MATCH_PARENT,
475 LayoutParams.WRAP_CONTENT,
476 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
Evan Lairdb0506ca2018-03-15 10:34:31 -0400477 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
Adrian Roos5b518852018-01-23 17:23:38 +0100478 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
479 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
480 | WindowManager.LayoutParams.FLAG_SLIPPERY
481 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
482 PixelFormat.TRANSLUCENT);
Roshan Piusa3f89c62019-10-11 08:50:53 -0700483 lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
Robert Carr772e8bc2018-03-14 11:51:23 -0700484 | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
485
Adrian Roos56d1a2c2018-03-08 23:22:19 +0100486 if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
487 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
488 }
Robert Carr772e8bc2018-03-14 11:51:23 -0700489
Adrian Roos5b518852018-01-23 17:23:38 +0100490 lp.setTitle("ScreenDecorOverlay");
Beverlye91f0d02018-05-15 14:40:47 -0400491 if (mRotation == RotationUtils.ROTATION_SEASCAPE
492 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
493 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
494 } else {
495 lp.gravity = Gravity.TOP | Gravity.LEFT;
496 }
Adrian Roos5b518852018-01-23 17:23:38 +0100497 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Beverlye91f0d02018-05-15 14:40:47 -0400498 if (isLandscape(mRotation)) {
Adrian Roos5b518852018-01-23 17:23:38 +0100499 lp.width = WRAP_CONTENT;
500 lp.height = MATCH_PARENT;
501 }
Peiyong Lin75045382019-03-04 19:22:33 -0800502 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
Adrian Roos5b518852018-01-23 17:23:38 +0100503 return lp;
504 }
505
506 private WindowManager.LayoutParams getBottomLayoutParams() {
507 WindowManager.LayoutParams lp = getWindowLayoutParams();
508 lp.setTitle("ScreenDecorOverlayBottom");
Beverlye91f0d02018-05-15 14:40:47 -0400509 if (mRotation == RotationUtils.ROTATION_SEASCAPE
510 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
511 lp.gravity = Gravity.TOP | Gravity.LEFT;
512 } else {
513 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
514 }
Adrian Roos5b518852018-01-23 17:23:38 +0100515 return lp;
516 }
517
518 private void updateLayoutParams() {
519 mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
520 mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
521 }
522
523 @Override
524 public void onTuningChanged(String key, String newValue) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200525 mHandler.post(() -> {
526 if (mOverlay == null) return;
527 if (SIZE.equals(key)) {
528 int size = mRoundedDefault;
529 int sizeTop = mRoundedDefaultTop;
530 int sizeBottom = mRoundedDefaultBottom;
531 if (newValue != null) {
532 try {
533 size = (int) (Integer.parseInt(newValue) * mDensity);
534 } catch (Exception e) {
535 }
Beverlya5f7a302018-04-25 09:19:05 -0400536 }
Adrian Roos72940642018-08-09 15:23:56 +0000537
Adrian Roosfaa102f2018-08-02 15:56:15 +0200538 if (sizeTop == 0) {
539 sizeTop = size;
540 }
541 if (sizeBottom == 0) {
542 sizeBottom = size;
543 }
Adrian Roos72940642018-08-09 15:23:56 +0000544
Adrian Roosfaa102f2018-08-02 15:56:15 +0200545 setSize(mOverlay.findViewById(R.id.left), sizeTop);
546 setSize(mOverlay.findViewById(R.id.right), sizeTop);
547 setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
548 setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
549 }
550 });
Adrian Roos5b518852018-01-23 17:23:38 +0100551 }
552
553 private void setSize(View view, int pixelSize) {
554 LayoutParams params = view.getLayoutParams();
555 params.width = pixelSize;
556 params.height = pixelSize;
557 view.setLayoutParams(params);
558 }
559
560 @VisibleForTesting
561 static class TunablePaddingTagListener implements FragmentListener {
562
563 private final int mPadding;
564 private final int mId;
565 private TunablePadding mTunablePadding;
566
567 public TunablePaddingTagListener(int padding, int id) {
568 mPadding = padding;
569 mId = id;
570 }
571
572 @Override
573 public void onFragmentViewCreated(String tag, Fragment fragment) {
574 if (mTunablePadding != null) {
575 mTunablePadding.destroy();
576 }
577 View view = fragment.getView();
578 if (mId != 0) {
579 view = view.findViewById(mId);
580 }
581 mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
582 FLAG_START | FLAG_END);
583 }
584 }
585
Evan Lairdb0506ca2018-03-15 10:34:31 -0400586 public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
587 RegionInterceptableView {
Adrian Roos5b518852018-01-23 17:23:38 +0100588
589 private final DisplayInfo mInfo = new DisplayInfo();
590 private final Paint mPaint = new Paint();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200591 private final List<Rect> mBounds = new ArrayList();
Adrian Roos5b518852018-01-23 17:23:38 +0100592 private final Rect mBoundingRect = new Rect();
593 private final Path mBoundingPath = new Path();
594 private final int[] mLocation = new int[2];
Adrian Roos9acf8132018-05-31 19:54:53 +0200595 private final boolean mInitialStart;
Adrian Roos5b518852018-01-23 17:23:38 +0100596 private final Runnable mVisibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700597 private final ScreenDecorations mDecorations;
Adrian Roos8b29a842018-05-31 14:14:13 +0200598 private int mColor = Color.BLACK;
Adrian Roos9acf8132018-05-31 19:54:53 +0200599 private boolean mStart;
600 private int mRotation;
Adrian Roos5b518852018-01-23 17:23:38 +0100601
602 public DisplayCutoutView(Context context, boolean start,
Vishnu Nair83537a72018-07-19 21:27:48 -0700603 Runnable visibilityChangedListener, ScreenDecorations decorations) {
Adrian Roos5b518852018-01-23 17:23:38 +0100604 super(context);
Adrian Roos9acf8132018-05-31 19:54:53 +0200605 mInitialStart = start;
Adrian Roos5b518852018-01-23 17:23:38 +0100606 mVisibilityChangedListener = visibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700607 mDecorations = decorations;
Adrian Roos5b518852018-01-23 17:23:38 +0100608 setId(R.id.display_cutout);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200609 if (DEBUG) {
610 getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
611 (mInitialStart ? "OverlayTop" : "OverlayBottom")
612 + " drawn in rot " + mRotation));
613 }
Adrian Roos5b518852018-01-23 17:23:38 +0100614 }
615
Adrian Roos8b29a842018-05-31 14:14:13 +0200616 public void setColor(int color) {
617 mColor = color;
618 invalidate();
619 }
620
Adrian Roos5b518852018-01-23 17:23:38 +0100621 @Override
622 protected void onAttachedToWindow() {
623 super.onAttachedToWindow();
624 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
625 getHandler());
626 update();
627 }
628
629 @Override
630 protected void onDetachedFromWindow() {
631 super.onDetachedFromWindow();
632 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
633 }
634
635 @Override
636 protected void onDraw(Canvas canvas) {
637 super.onDraw(canvas);
638 getLocationOnScreen(mLocation);
639 canvas.translate(-mLocation[0], -mLocation[1]);
640 if (!mBoundingPath.isEmpty()) {
Adrian Roos8b29a842018-05-31 14:14:13 +0200641 mPaint.setColor(mColor);
Adrian Roos5b518852018-01-23 17:23:38 +0100642 mPaint.setStyle(Paint.Style.FILL);
Adrian Roos51072a82018-04-10 15:17:08 -0700643 mPaint.setAntiAlias(true);
Adrian Roos5b518852018-01-23 17:23:38 +0100644 canvas.drawPath(mBoundingPath, mPaint);
645 }
646 }
647
648 @Override
649 public void onDisplayAdded(int displayId) {
650 }
651
652 @Override
653 public void onDisplayRemoved(int displayId) {
654 }
655
656 @Override
657 public void onDisplayChanged(int displayId) {
658 if (displayId == getDisplay().getDisplayId()) {
659 update();
660 }
661 }
662
Adrian Roos9acf8132018-05-31 19:54:53 +0200663 public void setRotation(int rotation) {
664 mRotation = rotation;
665 update();
666 }
667
668 private boolean isStart() {
669 final boolean flipped = (mRotation == RotationUtils.ROTATION_SEASCAPE
670 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN);
671 return flipped ? !mInitialStart : mInitialStart;
672 }
673
Adrian Roos5b518852018-01-23 17:23:38 +0100674 private void update() {
Vishnu Nair83537a72018-07-19 21:27:48 -0700675 if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
Adrian Roos9acf8132018-05-31 19:54:53 +0200676 return;
677 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700678 mStart = isStart();
Adrian Roos5b518852018-01-23 17:23:38 +0100679 requestLayout();
680 getDisplay().getDisplayInfo(mInfo);
Issei Suzuki43190bd2018-08-20 17:28:41 +0200681 mBounds.clear();
Adrian Roos5b518852018-01-23 17:23:38 +0100682 mBoundingRect.setEmpty();
683 mBoundingPath.reset();
684 int newVisible;
Adrian Roosc41b32b2018-04-12 10:13:48 +0200685 if (shouldDrawCutout(getContext()) && hasCutout()) {
Issei Suzuki43190bd2018-08-20 17:28:41 +0200686 mBounds.addAll(mInfo.displayCutout.getBoundingRects());
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100687 localBounds(mBoundingRect);
Adrian Roos0098aee2019-06-03 16:47:02 +0200688 updateGravity();
Adrian Roos51072a82018-04-10 15:17:08 -0700689 updateBoundingPath();
Adrian Roos1b028282018-03-14 14:43:03 +0100690 invalidate();
Adrian Roos5b518852018-01-23 17:23:38 +0100691 newVisible = VISIBLE;
692 } else {
693 newVisible = GONE;
694 }
695 if (newVisible != getVisibility()) {
696 setVisibility(newVisible);
697 mVisibilityChangedListener.run();
698 }
699 }
700
Adrian Roos51072a82018-04-10 15:17:08 -0700701 private void updateBoundingPath() {
702 int lw = mInfo.logicalWidth;
703 int lh = mInfo.logicalHeight;
704
705 boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
706
707 int dw = flipped ? lh : lw;
708 int dh = flipped ? lw : lh;
709
Adrian Roosa99f5d62018-04-16 16:03:04 +0200710 mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
Adrian Roos51072a82018-04-10 15:17:08 -0700711 Matrix m = new Matrix();
712 transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
713 mBoundingPath.transform(m);
714 }
715
716 private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
717 @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
718 switch (rotation) {
719 case ROTATION_0:
720 out.reset();
721 break;
722 case ROTATION_90:
723 out.setRotate(270);
724 out.postTranslate(0, physicalWidth);
725 break;
726 case ROTATION_180:
727 out.setRotate(180);
728 out.postTranslate(physicalWidth, physicalHeight);
729 break;
730 case ROTATION_270:
731 out.setRotate(90);
732 out.postTranslate(physicalHeight, 0);
733 break;
734 default:
735 throw new IllegalArgumentException("Unknown rotation: " + rotation);
736 }
737 }
738
Adrian Roos0098aee2019-06-03 16:47:02 +0200739 private void updateGravity() {
740 LayoutParams lp = getLayoutParams();
741 if (lp instanceof FrameLayout.LayoutParams) {
742 FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) lp;
743 int newGravity = getGravity(mInfo.displayCutout);
744 if (flp.gravity != newGravity) {
745 flp.gravity = newGravity;
746 setLayoutParams(flp);
747 }
748 }
749 }
750
Adrian Roos5b518852018-01-23 17:23:38 +0100751 private boolean hasCutout() {
Adrian Roos24264212018-02-19 16:26:15 +0100752 final DisplayCutout displayCutout = mInfo.displayCutout;
753 if (displayCutout == null) {
Adrian Roos5b518852018-01-23 17:23:38 +0100754 return false;
755 }
Adrian Roos5b518852018-01-23 17:23:38 +0100756 if (mStart) {
757 return displayCutout.getSafeInsetLeft() > 0
758 || displayCutout.getSafeInsetTop() > 0;
759 } else {
760 return displayCutout.getSafeInsetRight() > 0
761 || displayCutout.getSafeInsetBottom() > 0;
762 }
763 }
764
765 @Override
766 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100767 if (mBounds.isEmpty()) {
Adrian Roos5b518852018-01-23 17:23:38 +0100768 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
769 return;
770 }
771 setMeasuredDimension(
772 resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
773 resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
774 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100775
Jorim Jaggi71aaa402018-06-06 17:22:56 +0200776 public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
777 Rect out) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100778 switch (gravity) {
779 case Gravity.TOP:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200780 out.set(displayCutout.getBoundingRectTop());
Evan Laird6703f422018-10-11 13:24:05 -0400781 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100782 case Gravity.LEFT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200783 out.set(displayCutout.getBoundingRectLeft());
Evan Laird6703f422018-10-11 13:24:05 -0400784 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100785 case Gravity.BOTTOM:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200786 out.set(displayCutout.getBoundingRectBottom());
Evan Laird6703f422018-10-11 13:24:05 -0400787 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100788 case Gravity.RIGHT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200789 out.set(displayCutout.getBoundingRectRight());
Evan Laird6703f422018-10-11 13:24:05 -0400790 break;
791 default:
792 out.setEmpty();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100793 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100794 }
795
796 private void localBounds(Rect out) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200797 DisplayCutout displayCutout = mInfo.displayCutout;
798 boundsFromDirection(displayCutout, getGravity(displayCutout), out);
799 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100800
Adrian Roos0098aee2019-06-03 16:47:02 +0200801 private int getGravity(DisplayCutout displayCutout) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100802 if (mStart) {
803 if (displayCutout.getSafeInsetLeft() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200804 return Gravity.LEFT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100805 } else if (displayCutout.getSafeInsetTop() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200806 return Gravity.TOP;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100807 }
808 } else {
809 if (displayCutout.getSafeInsetRight() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200810 return Gravity.RIGHT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100811 } else if (displayCutout.getSafeInsetBottom() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200812 return Gravity.BOTTOM;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100813 }
814 }
Adrian Roos0098aee2019-06-03 16:47:02 +0200815 return Gravity.NO_GRAVITY;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100816 }
Evan Lairdb0506ca2018-03-15 10:34:31 -0400817
818 @Override
819 public boolean shouldInterceptTouch() {
820 return mInfo.displayCutout != null && getVisibility() == VISIBLE;
821 }
822
823 @Override
824 public Region getInterceptRegion() {
825 if (mInfo.displayCutout == null) {
826 return null;
827 }
828
Adrian Roos134e1cb2018-05-16 17:04:29 +0200829 View rootView = getRootView();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200830 Region cutoutBounds = rectsToRegion(
831 mInfo.displayCutout.getBoundingRects());
Adrian Roos134e1cb2018-05-16 17:04:29 +0200832
833 // Transform to window's coordinate space
834 rootView.getLocationOnScreen(mLocation);
835 cutoutBounds.translate(-mLocation[0], -mLocation[1]);
836
837 // Intersect with window's frame
838 cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
839 rootView.getBottom(), Region.Op.INTERSECT);
840
841 return cutoutBounds;
Evan Lairdb0506ca2018-03-15 10:34:31 -0400842 }
Adrian Roos5b518852018-01-23 17:23:38 +0100843 }
Beverlye91f0d02018-05-15 14:40:47 -0400844
845 private boolean isLandscape(int rotation) {
846 return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
847 RotationUtils.ROTATION_SEASCAPE;
848 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700849
850 /**
851 * A pre-draw listener, that cancels the draw and restarts the traversal with the updated
852 * window attributes.
853 */
854 private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
855
856 private final View mView;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200857 private final int mTargetRotation;
Vishnu Nair83537a72018-07-19 21:27:48 -0700858
Adrian Roosfaa102f2018-08-02 15:56:15 +0200859 private RestartingPreDrawListener(View view, int targetRotation) {
860 mView = view;
861 mTargetRotation = targetRotation;
862 }
863
864 @Override
865 public boolean onPreDraw() {
866 mView.getViewTreeObserver().removeOnPreDrawListener(this);
867
868 if (mTargetRotation == mRotation) {
869 if (DEBUG) {
870 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
871 + " already in target rot "
872 + mTargetRotation + ", allow draw without restarting it");
873 }
874 return true;
875 }
876
877 mPendingRotationChange = false;
878 // This changes the window attributes - we need to restart the traversal for them to
879 // take effect.
880 updateOrientation();
881 if (DEBUG) {
882 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
883 + " restarting listener fired, restarting draw for rot " + mRotation);
884 }
885 mView.invalidate();
886 return false;
887 }
888 }
889
890 /**
891 * A pre-draw listener, that validates that the rotation we draw in matches the displays
892 * rotation before continuing the draw.
893 *
894 * This is to prevent a race condition, where we have not received the display changed event
895 * yet, and would thus draw in an old orientation.
896 */
897 private class ValidatingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
898
899 private final View mView;
900
901 public ValidatingPreDrawListener(View view) {
Adrian Roos61f557a2018-08-02 15:56:15 +0200902 mView = view;
Adrian Roos61f557a2018-08-02 15:56:15 +0200903 }
904
905 @Override
906 public boolean onPreDraw() {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200907 final int displayRotation = RotationUtils.getExactRotation(mContext);
908 if (displayRotation != mRotation && !mPendingRotationChange) {
909 if (DEBUG) {
910 Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot "
911 + displayRotation + ". Restarting draw");
912 }
913 mView.invalidate();
914 return false;
915 }
916 return true;
Adrian Roos61f557a2018-08-02 15:56:15 +0200917 }
918 }
Adrian Roos5b518852018-01-23 17:23:38 +0100919}