blob: c533755c76da9b07241afdf2ea9af0887da17d3d [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;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050071import com.android.systemui.dagger.qualifiers.Main;
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,
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500150 @Main Handler handler,
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000151 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 Kozynski5e92c6f2020-01-03 13:56:17 -0500269 mBroadcastDispatcher.registerReceiverWithHandler(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;
Tiger Huang4a7835f2019-11-06 00:07:56 +0800498 lp.setFitWindowInsetsTypes(0 /* types */);
Beverlye91f0d02018-05-15 14:40:47 -0400499 if (isLandscape(mRotation)) {
Adrian Roos5b518852018-01-23 17:23:38 +0100500 lp.width = WRAP_CONTENT;
501 lp.height = MATCH_PARENT;
502 }
Peiyong Lin75045382019-03-04 19:22:33 -0800503 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
Adrian Roos5b518852018-01-23 17:23:38 +0100504 return lp;
505 }
506
507 private WindowManager.LayoutParams getBottomLayoutParams() {
508 WindowManager.LayoutParams lp = getWindowLayoutParams();
509 lp.setTitle("ScreenDecorOverlayBottom");
Beverlye91f0d02018-05-15 14:40:47 -0400510 if (mRotation == RotationUtils.ROTATION_SEASCAPE
511 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
512 lp.gravity = Gravity.TOP | Gravity.LEFT;
513 } else {
514 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
515 }
Adrian Roos5b518852018-01-23 17:23:38 +0100516 return lp;
517 }
518
519 private void updateLayoutParams() {
520 mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
521 mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
522 }
523
524 @Override
525 public void onTuningChanged(String key, String newValue) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200526 mHandler.post(() -> {
527 if (mOverlay == null) return;
528 if (SIZE.equals(key)) {
529 int size = mRoundedDefault;
530 int sizeTop = mRoundedDefaultTop;
531 int sizeBottom = mRoundedDefaultBottom;
532 if (newValue != null) {
533 try {
534 size = (int) (Integer.parseInt(newValue) * mDensity);
535 } catch (Exception e) {
536 }
Beverlya5f7a302018-04-25 09:19:05 -0400537 }
Adrian Roos72940642018-08-09 15:23:56 +0000538
Adrian Roosfaa102f2018-08-02 15:56:15 +0200539 if (sizeTop == 0) {
540 sizeTop = size;
541 }
542 if (sizeBottom == 0) {
543 sizeBottom = size;
544 }
Adrian Roos72940642018-08-09 15:23:56 +0000545
Adrian Roosfaa102f2018-08-02 15:56:15 +0200546 setSize(mOverlay.findViewById(R.id.left), sizeTop);
547 setSize(mOverlay.findViewById(R.id.right), sizeTop);
548 setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
549 setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
550 }
551 });
Adrian Roos5b518852018-01-23 17:23:38 +0100552 }
553
554 private void setSize(View view, int pixelSize) {
555 LayoutParams params = view.getLayoutParams();
556 params.width = pixelSize;
557 params.height = pixelSize;
558 view.setLayoutParams(params);
559 }
560
561 @VisibleForTesting
562 static class TunablePaddingTagListener implements FragmentListener {
563
564 private final int mPadding;
565 private final int mId;
566 private TunablePadding mTunablePadding;
567
568 public TunablePaddingTagListener(int padding, int id) {
569 mPadding = padding;
570 mId = id;
571 }
572
573 @Override
574 public void onFragmentViewCreated(String tag, Fragment fragment) {
575 if (mTunablePadding != null) {
576 mTunablePadding.destroy();
577 }
578 View view = fragment.getView();
579 if (mId != 0) {
580 view = view.findViewById(mId);
581 }
582 mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
583 FLAG_START | FLAG_END);
584 }
585 }
586
Evan Lairdb0506ca2018-03-15 10:34:31 -0400587 public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
588 RegionInterceptableView {
Adrian Roos5b518852018-01-23 17:23:38 +0100589
590 private final DisplayInfo mInfo = new DisplayInfo();
591 private final Paint mPaint = new Paint();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200592 private final List<Rect> mBounds = new ArrayList();
Adrian Roos5b518852018-01-23 17:23:38 +0100593 private final Rect mBoundingRect = new Rect();
594 private final Path mBoundingPath = new Path();
595 private final int[] mLocation = new int[2];
Adrian Roos9acf8132018-05-31 19:54:53 +0200596 private final boolean mInitialStart;
Adrian Roos5b518852018-01-23 17:23:38 +0100597 private final Runnable mVisibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700598 private final ScreenDecorations mDecorations;
Adrian Roos8b29a842018-05-31 14:14:13 +0200599 private int mColor = Color.BLACK;
Adrian Roos9acf8132018-05-31 19:54:53 +0200600 private boolean mStart;
601 private int mRotation;
Adrian Roos5b518852018-01-23 17:23:38 +0100602
603 public DisplayCutoutView(Context context, boolean start,
Vishnu Nair83537a72018-07-19 21:27:48 -0700604 Runnable visibilityChangedListener, ScreenDecorations decorations) {
Adrian Roos5b518852018-01-23 17:23:38 +0100605 super(context);
Adrian Roos9acf8132018-05-31 19:54:53 +0200606 mInitialStart = start;
Adrian Roos5b518852018-01-23 17:23:38 +0100607 mVisibilityChangedListener = visibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700608 mDecorations = decorations;
Adrian Roos5b518852018-01-23 17:23:38 +0100609 setId(R.id.display_cutout);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200610 if (DEBUG) {
611 getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
612 (mInitialStart ? "OverlayTop" : "OverlayBottom")
613 + " drawn in rot " + mRotation));
614 }
Adrian Roos5b518852018-01-23 17:23:38 +0100615 }
616
Adrian Roos8b29a842018-05-31 14:14:13 +0200617 public void setColor(int color) {
618 mColor = color;
619 invalidate();
620 }
621
Adrian Roos5b518852018-01-23 17:23:38 +0100622 @Override
623 protected void onAttachedToWindow() {
624 super.onAttachedToWindow();
625 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
626 getHandler());
627 update();
628 }
629
630 @Override
631 protected void onDetachedFromWindow() {
632 super.onDetachedFromWindow();
633 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
634 }
635
636 @Override
637 protected void onDraw(Canvas canvas) {
638 super.onDraw(canvas);
639 getLocationOnScreen(mLocation);
640 canvas.translate(-mLocation[0], -mLocation[1]);
641 if (!mBoundingPath.isEmpty()) {
Adrian Roos8b29a842018-05-31 14:14:13 +0200642 mPaint.setColor(mColor);
Adrian Roos5b518852018-01-23 17:23:38 +0100643 mPaint.setStyle(Paint.Style.FILL);
Adrian Roos51072a82018-04-10 15:17:08 -0700644 mPaint.setAntiAlias(true);
Adrian Roos5b518852018-01-23 17:23:38 +0100645 canvas.drawPath(mBoundingPath, mPaint);
646 }
647 }
648
649 @Override
650 public void onDisplayAdded(int displayId) {
651 }
652
653 @Override
654 public void onDisplayRemoved(int displayId) {
655 }
656
657 @Override
658 public void onDisplayChanged(int displayId) {
659 if (displayId == getDisplay().getDisplayId()) {
660 update();
661 }
662 }
663
Adrian Roos9acf8132018-05-31 19:54:53 +0200664 public void setRotation(int rotation) {
665 mRotation = rotation;
666 update();
667 }
668
669 private boolean isStart() {
670 final boolean flipped = (mRotation == RotationUtils.ROTATION_SEASCAPE
671 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN);
672 return flipped ? !mInitialStart : mInitialStart;
673 }
674
Adrian Roos5b518852018-01-23 17:23:38 +0100675 private void update() {
Vishnu Nair83537a72018-07-19 21:27:48 -0700676 if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
Adrian Roos9acf8132018-05-31 19:54:53 +0200677 return;
678 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700679 mStart = isStart();
Adrian Roos5b518852018-01-23 17:23:38 +0100680 requestLayout();
681 getDisplay().getDisplayInfo(mInfo);
Issei Suzuki43190bd2018-08-20 17:28:41 +0200682 mBounds.clear();
Adrian Roos5b518852018-01-23 17:23:38 +0100683 mBoundingRect.setEmpty();
684 mBoundingPath.reset();
685 int newVisible;
Adrian Roosc41b32b2018-04-12 10:13:48 +0200686 if (shouldDrawCutout(getContext()) && hasCutout()) {
Issei Suzuki43190bd2018-08-20 17:28:41 +0200687 mBounds.addAll(mInfo.displayCutout.getBoundingRects());
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100688 localBounds(mBoundingRect);
Adrian Roos0098aee2019-06-03 16:47:02 +0200689 updateGravity();
Adrian Roos51072a82018-04-10 15:17:08 -0700690 updateBoundingPath();
Adrian Roos1b028282018-03-14 14:43:03 +0100691 invalidate();
Adrian Roos5b518852018-01-23 17:23:38 +0100692 newVisible = VISIBLE;
693 } else {
694 newVisible = GONE;
695 }
696 if (newVisible != getVisibility()) {
697 setVisibility(newVisible);
698 mVisibilityChangedListener.run();
699 }
700 }
701
Adrian Roos51072a82018-04-10 15:17:08 -0700702 private void updateBoundingPath() {
703 int lw = mInfo.logicalWidth;
704 int lh = mInfo.logicalHeight;
705
706 boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
707
708 int dw = flipped ? lh : lw;
709 int dh = flipped ? lw : lh;
710
Adrian Roosa99f5d62018-04-16 16:03:04 +0200711 mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
Adrian Roos51072a82018-04-10 15:17:08 -0700712 Matrix m = new Matrix();
713 transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
714 mBoundingPath.transform(m);
715 }
716
717 private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
718 @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
719 switch (rotation) {
720 case ROTATION_0:
721 out.reset();
722 break;
723 case ROTATION_90:
724 out.setRotate(270);
725 out.postTranslate(0, physicalWidth);
726 break;
727 case ROTATION_180:
728 out.setRotate(180);
729 out.postTranslate(physicalWidth, physicalHeight);
730 break;
731 case ROTATION_270:
732 out.setRotate(90);
733 out.postTranslate(physicalHeight, 0);
734 break;
735 default:
736 throw new IllegalArgumentException("Unknown rotation: " + rotation);
737 }
738 }
739
Adrian Roos0098aee2019-06-03 16:47:02 +0200740 private void updateGravity() {
741 LayoutParams lp = getLayoutParams();
742 if (lp instanceof FrameLayout.LayoutParams) {
743 FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) lp;
744 int newGravity = getGravity(mInfo.displayCutout);
745 if (flp.gravity != newGravity) {
746 flp.gravity = newGravity;
747 setLayoutParams(flp);
748 }
749 }
750 }
751
Adrian Roos5b518852018-01-23 17:23:38 +0100752 private boolean hasCutout() {
Adrian Roos24264212018-02-19 16:26:15 +0100753 final DisplayCutout displayCutout = mInfo.displayCutout;
754 if (displayCutout == null) {
Adrian Roos5b518852018-01-23 17:23:38 +0100755 return false;
756 }
Adrian Roos5b518852018-01-23 17:23:38 +0100757 if (mStart) {
758 return displayCutout.getSafeInsetLeft() > 0
759 || displayCutout.getSafeInsetTop() > 0;
760 } else {
761 return displayCutout.getSafeInsetRight() > 0
762 || displayCutout.getSafeInsetBottom() > 0;
763 }
764 }
765
766 @Override
767 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100768 if (mBounds.isEmpty()) {
Adrian Roos5b518852018-01-23 17:23:38 +0100769 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
770 return;
771 }
772 setMeasuredDimension(
773 resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
774 resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
775 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100776
Jorim Jaggi71aaa402018-06-06 17:22:56 +0200777 public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
778 Rect out) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100779 switch (gravity) {
780 case Gravity.TOP:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200781 out.set(displayCutout.getBoundingRectTop());
Evan Laird6703f422018-10-11 13:24:05 -0400782 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100783 case Gravity.LEFT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200784 out.set(displayCutout.getBoundingRectLeft());
Evan Laird6703f422018-10-11 13:24:05 -0400785 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100786 case Gravity.BOTTOM:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200787 out.set(displayCutout.getBoundingRectBottom());
Evan Laird6703f422018-10-11 13:24:05 -0400788 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100789 case Gravity.RIGHT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200790 out.set(displayCutout.getBoundingRectRight());
Evan Laird6703f422018-10-11 13:24:05 -0400791 break;
792 default:
793 out.setEmpty();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100794 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100795 }
796
797 private void localBounds(Rect out) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200798 DisplayCutout displayCutout = mInfo.displayCutout;
799 boundsFromDirection(displayCutout, getGravity(displayCutout), out);
800 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100801
Adrian Roos0098aee2019-06-03 16:47:02 +0200802 private int getGravity(DisplayCutout displayCutout) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100803 if (mStart) {
804 if (displayCutout.getSafeInsetLeft() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200805 return Gravity.LEFT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100806 } else if (displayCutout.getSafeInsetTop() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200807 return Gravity.TOP;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100808 }
809 } else {
810 if (displayCutout.getSafeInsetRight() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200811 return Gravity.RIGHT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100812 } else if (displayCutout.getSafeInsetBottom() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200813 return Gravity.BOTTOM;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100814 }
815 }
Adrian Roos0098aee2019-06-03 16:47:02 +0200816 return Gravity.NO_GRAVITY;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100817 }
Evan Lairdb0506ca2018-03-15 10:34:31 -0400818
819 @Override
820 public boolean shouldInterceptTouch() {
821 return mInfo.displayCutout != null && getVisibility() == VISIBLE;
822 }
823
824 @Override
825 public Region getInterceptRegion() {
826 if (mInfo.displayCutout == null) {
827 return null;
828 }
829
Adrian Roos134e1cb2018-05-16 17:04:29 +0200830 View rootView = getRootView();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200831 Region cutoutBounds = rectsToRegion(
832 mInfo.displayCutout.getBoundingRects());
Adrian Roos134e1cb2018-05-16 17:04:29 +0200833
834 // Transform to window's coordinate space
835 rootView.getLocationOnScreen(mLocation);
836 cutoutBounds.translate(-mLocation[0], -mLocation[1]);
837
838 // Intersect with window's frame
839 cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
840 rootView.getBottom(), Region.Op.INTERSECT);
841
842 return cutoutBounds;
Evan Lairdb0506ca2018-03-15 10:34:31 -0400843 }
Adrian Roos5b518852018-01-23 17:23:38 +0100844 }
Beverlye91f0d02018-05-15 14:40:47 -0400845
846 private boolean isLandscape(int rotation) {
847 return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
848 RotationUtils.ROTATION_SEASCAPE;
849 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700850
851 /**
852 * A pre-draw listener, that cancels the draw and restarts the traversal with the updated
853 * window attributes.
854 */
855 private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
856
857 private final View mView;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200858 private final int mTargetRotation;
Vishnu Nair83537a72018-07-19 21:27:48 -0700859
Adrian Roosfaa102f2018-08-02 15:56:15 +0200860 private RestartingPreDrawListener(View view, int targetRotation) {
861 mView = view;
862 mTargetRotation = targetRotation;
863 }
864
865 @Override
866 public boolean onPreDraw() {
867 mView.getViewTreeObserver().removeOnPreDrawListener(this);
868
869 if (mTargetRotation == mRotation) {
870 if (DEBUG) {
871 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
872 + " already in target rot "
873 + mTargetRotation + ", allow draw without restarting it");
874 }
875 return true;
876 }
877
878 mPendingRotationChange = false;
879 // This changes the window attributes - we need to restart the traversal for them to
880 // take effect.
881 updateOrientation();
882 if (DEBUG) {
883 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
884 + " restarting listener fired, restarting draw for rot " + mRotation);
885 }
886 mView.invalidate();
887 return false;
888 }
889 }
890
891 /**
892 * A pre-draw listener, that validates that the rotation we draw in matches the displays
893 * rotation before continuing the draw.
894 *
895 * This is to prevent a race condition, where we have not received the display changed event
896 * yet, and would thus draw in an old orientation.
897 */
898 private class ValidatingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
899
900 private final View mView;
901
902 public ValidatingPreDrawListener(View view) {
Adrian Roos61f557a2018-08-02 15:56:15 +0200903 mView = view;
Adrian Roos61f557a2018-08-02 15:56:15 +0200904 }
905
906 @Override
907 public boolean onPreDraw() {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200908 final int displayRotation = RotationUtils.getExactRotation(mContext);
909 if (displayRotation != mRotation && !mPendingRotationChange) {
910 if (DEBUG) {
911 Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot "
912 + displayRotation + ". Restarting draw");
913 }
914 mView.invalidate();
915 return false;
916 }
917 return true;
Adrian Roos61f557a2018-08-02 15:56:15 +0200918 }
919 }
Adrian Roos5b518852018-01-23 17:23:38 +0100920}