blob: 8a99e451934e217260dbcf0cbab236106b0b2994 [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;
Adrian Roos5b518852018-01-23 17:23:38 +010070import com.android.systemui.fragments.FragmentHostManager;
71import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
72import com.android.systemui.plugins.qs.QS;
73import com.android.systemui.qs.SecureSetting;
74import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
75import com.android.systemui.statusbar.phone.StatusBar;
76import com.android.systemui.tuner.TunablePadding;
77import com.android.systemui.tuner.TunerService;
78import com.android.systemui.tuner.TunerService.Tunable;
Beverlye91f0d02018-05-15 14:40:47 -040079import com.android.systemui.util.leak.RotationUtils;
Adrian Roos5b518852018-01-23 17:23:38 +010080
Issei Suzuki43190bd2018-08-20 17:28:41 +020081import java.util.ArrayList;
82import java.util.List;
83
Dave Mankoff4ddc25b2019-10-23 15:46:08 -040084import javax.inject.Inject;
85import javax.inject.Singleton;
86
87import dagger.Lazy;
88
Adrian Roos5b518852018-01-23 17:23:38 +010089/**
90 * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
91 * for antialiasing and emulation purposes.
92 */
Dave Mankoff4ddc25b2019-10-23 15:46:08 -040093@Singleton
shawnlin87af5382019-09-13 14:13:13 +080094public class ScreenDecorations extends SystemUI implements Tunable {
Adrian Roosfaa102f2018-08-02 15:56:15 +020095 private static final boolean DEBUG = false;
96 private static final String TAG = "ScreenDecorations";
97
Adrian Roos5b518852018-01-23 17:23:38 +010098 public static final String SIZE = "sysui_rounded_size";
99 public static final String PADDING = "sysui_rounded_content_padding";
Adrian Roos56d1a2c2018-03-08 23:22:19 +0100100 private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
101 SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
James O'Leary4335c702019-05-29 12:38:51 -0400102 private static final boolean VERBOSE = false;
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400103 private final Lazy<StatusBar> mStatusBarLazy;
Adrian Roos5b518852018-01-23 17:23:38 +0100104
Beverlye91f0d02018-05-15 14:40:47 -0400105 private DisplayManager mDisplayManager;
106 private DisplayManager.DisplayListener mDisplayListener;
107
James O'Leary4335c702019-05-29 12:38:51 -0400108 @VisibleForTesting
109 protected int mRoundedDefault;
110 @VisibleForTesting
111 protected int mRoundedDefaultTop;
112 @VisibleForTesting
113 protected int mRoundedDefaultBottom;
Adrian Roos5b518852018-01-23 17:23:38 +0100114 private View mOverlay;
115 private View mBottomOverlay;
116 private float mDensity;
117 private WindowManager mWindowManager;
Beverlye91f0d02018-05-15 14:40:47 -0400118 private int mRotation;
Adrian Roos9acf8132018-05-31 19:54:53 +0200119 private DisplayCutoutView mCutoutTop;
120 private DisplayCutoutView mCutoutBottom;
Beverly374acde2018-06-13 10:49:26 -0400121 private SecureSetting mColorInversionSetting;
Vishnu Nair83537a72018-07-19 21:27:48 -0700122 private boolean mPendingRotationChange;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200123 private Handler mHandler;
Adrian Roos5b518852018-01-23 17:23:38 +0100124
Issei Suzuki43190bd2018-08-20 17:28:41 +0200125 /**
126 * Converts a set of {@link Rect}s into a {@link Region}
127 *
128 * @hide
129 */
130 public static Region rectsToRegion(List<Rect> rects) {
131 Region result = Region.obtain();
132 if (rects != null) {
133 for (Rect r : rects) {
134 if (r != null && !r.isEmpty()) {
135 result.op(r, Region.Op.UNION);
136 }
137 }
138 }
139 return result;
140 }
141
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400142 @Inject
143 public ScreenDecorations(Context context, Lazy<StatusBar> statusBarLazy) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400144 super(context);
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400145 mStatusBarLazy = statusBarLazy;
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400146 }
147
Adrian Roos5b518852018-01-23 17:23:38 +0100148 @Override
149 public void start() {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200150 mHandler = startHandlerThread();
151 mHandler.post(this::startOnScreenDecorationsThread);
152 setupStatusBarPaddingIfNeeded();
James O'Leary4335c702019-05-29 12:38:51 -0400153 }
154
Adrian Roosfaa102f2018-08-02 15:56:15 +0200155 @VisibleForTesting
156 Handler startHandlerThread() {
157 HandlerThread thread = new HandlerThread("ScreenDecorations");
158 thread.start();
159 return thread.getThreadHandler();
160 }
161
162 private void startOnScreenDecorationsThread() {
163 mRotation = RotationUtils.getExactRotation(mContext);
Adrian Roos5b518852018-01-23 17:23:38 +0100164 mWindowManager = mContext.getSystemService(WindowManager.class);
Adrian Roos64c9d902018-08-20 13:43:38 +0200165 updateRoundedCornerRadii();
shawnlin87af5382019-09-13 14:13:13 +0800166 if (hasRoundedCorners() || shouldDrawCutout()) {
Adrian Roos5b518852018-01-23 17:23:38 +0100167 setupDecorations();
168 }
Beverlya5f7a302018-04-25 09:19:05 -0400169
Beverlye91f0d02018-05-15 14:40:47 -0400170 mDisplayListener = new DisplayManager.DisplayListener() {
171 @Override
172 public void onDisplayAdded(int displayId) {
173 // do nothing
174 }
175
176 @Override
177 public void onDisplayRemoved(int displayId) {
178 // do nothing
179 }
180
181 @Override
182 public void onDisplayChanged(int displayId) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200183 final int newRotation = RotationUtils.getExactRotation(mContext);
184 if (mOverlay != null && mBottomOverlay != null && mRotation != newRotation) {
Vishnu Nair83537a72018-07-19 21:27:48 -0700185 // We cannot immediately update the orientation. Otherwise
186 // WindowManager is still deferring layout until it has finished dispatching
187 // the config changes, which may cause divergence between what we draw
188 // (new orientation), and where we are placed on the screen (old orientation).
189 // Instead we wait until either:
190 // - we are trying to redraw. This because WM resized our window and told us to.
191 // - the config change has been dispatched, so WM is no longer deferring layout.
192 mPendingRotationChange = true;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200193 if (DEBUG) {
194 Log.i(TAG, "Rotation changed, deferring " + newRotation + ", staying at "
195 + mRotation);
196 }
197
Vishnu Nair83537a72018-07-19 21:27:48 -0700198 mOverlay.getViewTreeObserver().addOnPreDrawListener(
Adrian Roosfaa102f2018-08-02 15:56:15 +0200199 new RestartingPreDrawListener(mOverlay, newRotation));
Vishnu Nair83537a72018-07-19 21:27:48 -0700200 mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
Adrian Roosfaa102f2018-08-02 15:56:15 +0200201 new RestartingPreDrawListener(mBottomOverlay, newRotation));
Vishnu Nair83537a72018-07-19 21:27:48 -0700202 }
Beverlye91f0d02018-05-15 14:40:47 -0400203 updateOrientation();
204 }
205 };
206
Beverlye91f0d02018-05-15 14:40:47 -0400207 mDisplayManager = (DisplayManager) mContext.getSystemService(
208 Context.DISPLAY_SERVICE);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200209 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
210 updateOrientation();
Adrian Roos5b518852018-01-23 17:23:38 +0100211 }
212
213 private void setupDecorations() {
214 mOverlay = LayoutInflater.from(mContext)
215 .inflate(R.layout.rounded_corners, null);
Adrian Roos9acf8132018-05-31 19:54:53 +0200216 mCutoutTop = new DisplayCutoutView(mContext, true,
Vishnu Nair83537a72018-07-19 21:27:48 -0700217 this::updateWindowVisibilities, this);
James O'Leary4335c702019-05-29 12:38:51 -0400218 ((ViewGroup) mOverlay).addView(mCutoutTop);
Adrian Roos5b518852018-01-23 17:23:38 +0100219 mBottomOverlay = LayoutInflater.from(mContext)
220 .inflate(R.layout.rounded_corners, null);
Adrian Roos9acf8132018-05-31 19:54:53 +0200221 mCutoutBottom = new DisplayCutoutView(mContext, false,
Vishnu Nair83537a72018-07-19 21:27:48 -0700222 this::updateWindowVisibilities, this);
James O'Leary4335c702019-05-29 12:38:51 -0400223 ((ViewGroup) mBottomOverlay).addView(mCutoutBottom);
Adrian Roos5b518852018-01-23 17:23:38 +0100224
225 mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
226 mOverlay.setAlpha(0);
John Recka2d20b42018-10-01 12:21:55 -0700227 mOverlay.setForceDarkAllowed(false);
Adrian Roos5b518852018-01-23 17:23:38 +0100228
229 mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
230 mBottomOverlay.setAlpha(0);
John Recka2d20b42018-10-01 12:21:55 -0700231 mBottomOverlay.setForceDarkAllowed(false);
Adrian Roos5b518852018-01-23 17:23:38 +0100232
233 updateViews();
234
235 mWindowManager.addView(mOverlay, getWindowLayoutParams());
236 mWindowManager.addView(mBottomOverlay, getBottomLayoutParams());
237
238 DisplayMetrics metrics = new DisplayMetrics();
239 mWindowManager.getDefaultDisplay().getMetrics(metrics);
240 mDensity = metrics.density;
241
Adrian Roosfaa102f2018-08-02 15:56:15 +0200242 Dependency.get(Dependency.MAIN_HANDLER).post(
243 () -> Dependency.get(TunerService.class).addTunable(this, SIZE));
Adrian Roos5b518852018-01-23 17:23:38 +0100244
245 // Watch color inversion and invert the overlay as needed.
Adrian Roosfaa102f2018-08-02 15:56:15 +0200246 mColorInversionSetting = new SecureSetting(mContext, mHandler,
Adrian Roos5b518852018-01-23 17:23:38 +0100247 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
248 @Override
249 protected void handleValueChanged(int value, boolean observedChange) {
Beverly374acde2018-06-13 10:49:26 -0400250 updateColorInversion(value);
Adrian Roos5b518852018-01-23 17:23:38 +0100251 }
252 };
Beverly374acde2018-06-13 10:49:26 -0400253 mColorInversionSetting.setListening(true);
254 mColorInversionSetting.onChange(false);
255
256 IntentFilter filter = new IntentFilter();
257 filter.addAction(Intent.ACTION_USER_SWITCHED);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200258 mContext.registerReceiver(mIntentReceiver, filter, null /* permission */, mHandler);
Adrian Roos5b518852018-01-23 17:23:38 +0100259
260 mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
261 @Override
262 public void onLayoutChange(View v, int left, int top, int right, int bottom,
263 int oldLeft,
264 int oldTop, int oldRight, int oldBottom) {
265 mOverlay.removeOnLayoutChangeListener(this);
266 mOverlay.animate()
267 .alpha(1)
268 .setDuration(1000)
269 .start();
270 mBottomOverlay.animate()
271 .alpha(1)
272 .setDuration(1000)
273 .start();
274 }
275 });
Adrian Roosfaa102f2018-08-02 15:56:15 +0200276
277 mOverlay.getViewTreeObserver().addOnPreDrawListener(
278 new ValidatingPreDrawListener(mOverlay));
279 mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
280 new ValidatingPreDrawListener(mBottomOverlay));
Adrian Roos5b518852018-01-23 17:23:38 +0100281 }
282
Beverly374acde2018-06-13 10:49:26 -0400283 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
284 @Override
285 public void onReceive(Context context, Intent intent) {
286 String action = intent.getAction();
287 if (action.equals(Intent.ACTION_USER_SWITCHED)) {
288 int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
289 ActivityManager.getCurrentUser());
290 // update color inversion setting to the new user
291 mColorInversionSetting.setUserId(newUserId);
292 updateColorInversion(mColorInversionSetting.getValue());
293 }
294 }
295 };
296
297 private void updateColorInversion(int colorsInvertedValue) {
298 int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
299 ColorStateList tintList = ColorStateList.valueOf(tint);
300 ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
301 ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
302 ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
303 ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
304 mCutoutTop.setColor(tint);
305 mCutoutBottom.setColor(tint);
306 }
307
Adrian Roos5b518852018-01-23 17:23:38 +0100308 @Override
309 protected void onConfigurationChanged(Configuration newConfig) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200310 mHandler.post(() -> {
311 int oldRotation = mRotation;
312 mPendingRotationChange = false;
313 updateOrientation();
Adrian Roos64c9d902018-08-20 13:43:38 +0200314 updateRoundedCornerRadii();
Adrian Roosfaa102f2018-08-02 15:56:15 +0200315 if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
316 if (shouldDrawCutout() && mOverlay == null) {
317 setupDecorations();
318 }
319 if (mOverlay != null) {
320 // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(),
321 // which ensures that the forced seamless rotation will end, even if we updated
322 // the rotation before window manager was ready (and was still waiting for sending
323 // the updated rotation).
324 updateLayoutParams();
325 }
326 });
Beverlye91f0d02018-05-15 14:40:47 -0400327 }
328
Adrian Roosfaa102f2018-08-02 15:56:15 +0200329 private void updateOrientation() {
330 Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
331 "must call on " + mHandler.getLooper().getThread()
332 + ", but was " + Thread.currentThread());
Vishnu Nair83537a72018-07-19 21:27:48 -0700333 if (mPendingRotationChange) {
334 return;
335 }
Beverlye91f0d02018-05-15 14:40:47 -0400336 int newRotation = RotationUtils.getExactRotation(mContext);
337 if (newRotation != mRotation) {
338 mRotation = newRotation;
Adrian Roos5b518852018-01-23 17:23:38 +0100339
340 if (mOverlay != null) {
341 updateLayoutParams();
342 updateViews();
343 }
Adrian Roos5b518852018-01-23 17:23:38 +0100344 }
345 }
346
Adrian Roos64c9d902018-08-20 13:43:38 +0200347 private void updateRoundedCornerRadii() {
348 final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500349 com.android.internal.R.dimen.rounded_corner_radius);
Adrian Roos64c9d902018-08-20 13:43:38 +0200350 final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500351 com.android.internal.R.dimen.rounded_corner_radius_top);
Adrian Roos64c9d902018-08-20 13:43:38 +0200352 final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
Beverly4d1113d2019-01-03 14:45:10 -0500353 com.android.internal.R.dimen.rounded_corner_radius_bottom);
Adrian Roos64c9d902018-08-20 13:43:38 +0200354
355 final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
356 || mRoundedDefaultBottom != newRoundedDefaultBottom
357 || mRoundedDefaultTop != newRoundedDefaultTop;
358
359 if (roundedCornersChanged) {
360 mRoundedDefault = newRoundedDefault;
361 mRoundedDefaultTop = newRoundedDefaultTop;
362 mRoundedDefaultBottom = newRoundedDefaultBottom;
363 onTuningChanged(SIZE, null);
364 }
365 }
366
Adrian Roos5b518852018-01-23 17:23:38 +0100367 private void updateViews() {
368 View topLeft = mOverlay.findViewById(R.id.left);
369 View topRight = mOverlay.findViewById(R.id.right);
370 View bottomLeft = mBottomOverlay.findViewById(R.id.left);
371 View bottomRight = mBottomOverlay.findViewById(R.id.right);
Beverlye91f0d02018-05-15 14:40:47 -0400372
373 if (mRotation == RotationUtils.ROTATION_NONE) {
374 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
375 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
376 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
377 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
378 } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
379 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
380 updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270);
James O'Leary4335c702019-05-29 12:38:51 -0400381 updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);
Beverlye91f0d02018-05-15 14:40:47 -0400382 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
383 } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
384 updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
385 updateView(topRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
386 updateView(bottomLeft, Gravity.TOP | Gravity.LEFT, 0);
387 updateView(bottomRight, Gravity.TOP | Gravity.RIGHT, 90);
388 } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) {
389 updateView(topLeft, Gravity.BOTTOM | Gravity.RIGHT, 180);
390 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
391 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
392 updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0);
Adrian Roos5b518852018-01-23 17:23:38 +0100393 }
Adrian Roos5b518852018-01-23 17:23:38 +0100394
Adrian Roos9acf8132018-05-31 19:54:53 +0200395 mCutoutTop.setRotation(mRotation);
396 mCutoutBottom.setRotation(mRotation);
397
Adrian Roos5b518852018-01-23 17:23:38 +0100398 updateWindowVisibilities();
399 }
400
401 private void updateView(View v, int gravity, int rotation) {
James O'Leary4335c702019-05-29 12:38:51 -0400402 ((FrameLayout.LayoutParams) v.getLayoutParams()).gravity = gravity;
Adrian Roos5b518852018-01-23 17:23:38 +0100403 v.setRotation(rotation);
404 }
405
406 private void updateWindowVisibilities() {
407 updateWindowVisibility(mOverlay);
408 updateWindowVisibility(mBottomOverlay);
409 }
410
411 private void updateWindowVisibility(View overlay) {
412 boolean visibleForCutout = shouldDrawCutout()
413 && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
Beverlya5f7a302018-04-25 09:19:05 -0400414 boolean visibleForRoundedCorners = hasRoundedCorners();
shawnlin87af5382019-09-13 14:13:13 +0800415 overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
Adrian Roos5b518852018-01-23 17:23:38 +0100416 ? View.VISIBLE : View.GONE);
417 }
418
Beverlya5f7a302018-04-25 09:19:05 -0400419 private boolean hasRoundedCorners() {
420 return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
421 }
422
Adrian Roos5b518852018-01-23 17:23:38 +0100423 private boolean shouldDrawCutout() {
Adrian Roosc41b32b2018-04-12 10:13:48 +0200424 return shouldDrawCutout(mContext);
425 }
426
427 static boolean shouldDrawCutout(Context context) {
428 return context.getResources().getBoolean(
Adrian Roos5b518852018-01-23 17:23:38 +0100429 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
430 }
431
Adrian Roosfaa102f2018-08-02 15:56:15 +0200432
433 private void setupStatusBarPaddingIfNeeded() {
434 // TODO: This should be moved to a more appropriate place, as it is not related to the
435 // screen decorations overlay.
436 int padding = mContext.getResources().getDimensionPixelSize(
437 R.dimen.rounded_corner_content_padding);
438 if (padding != 0) {
439 setupStatusBarPadding(padding);
440 }
441
442 }
443
444 private void setupStatusBarPadding(int padding) {
Adrian Roos5b518852018-01-23 17:23:38 +0100445 // Add some padding to all the content near the edge of the screen.
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400446 StatusBar statusBar = mStatusBarLazy.get();
447 View statusBarWindow = statusBar.getStatusBarWindow();
448 if (statusBarWindow != null) {
449 TunablePadding.addTunablePadding(statusBarWindow.findViewById(R.id.keyguard_header),
450 PADDING, padding, FLAG_END);
Adrian Roos5b518852018-01-23 17:23:38 +0100451
Dave Mankoff4ddc25b2019-10-23 15:46:08 -0400452 FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBarWindow);
Adrian Roos5b518852018-01-23 17:23:38 +0100453 fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
454 new TunablePaddingTagListener(padding, R.id.status_bar));
455 fragmentHostManager.addTagListener(QS.TAG,
456 new TunablePaddingTagListener(padding, R.id.header));
457 }
458 }
459
460 @VisibleForTesting
461 WindowManager.LayoutParams getWindowLayoutParams() {
462 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
463 ViewGroup.LayoutParams.MATCH_PARENT,
464 LayoutParams.WRAP_CONTENT,
465 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
Evan Lairdb0506ca2018-03-15 10:34:31 -0400466 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
Adrian Roos5b518852018-01-23 17:23:38 +0100467 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
468 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
469 | WindowManager.LayoutParams.FLAG_SLIPPERY
470 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
471 PixelFormat.TRANSLUCENT);
Roshan Piusa3f89c62019-10-11 08:50:53 -0700472 lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
Robert Carr772e8bc2018-03-14 11:51:23 -0700473 | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
474
Adrian Roos56d1a2c2018-03-08 23:22:19 +0100475 if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
476 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
477 }
Robert Carr772e8bc2018-03-14 11:51:23 -0700478
Adrian Roos5b518852018-01-23 17:23:38 +0100479 lp.setTitle("ScreenDecorOverlay");
Beverlye91f0d02018-05-15 14:40:47 -0400480 if (mRotation == RotationUtils.ROTATION_SEASCAPE
481 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
482 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
483 } else {
484 lp.gravity = Gravity.TOP | Gravity.LEFT;
485 }
Adrian Roos5b518852018-01-23 17:23:38 +0100486 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Beverlye91f0d02018-05-15 14:40:47 -0400487 if (isLandscape(mRotation)) {
Adrian Roos5b518852018-01-23 17:23:38 +0100488 lp.width = WRAP_CONTENT;
489 lp.height = MATCH_PARENT;
490 }
Peiyong Lin75045382019-03-04 19:22:33 -0800491 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
Adrian Roos5b518852018-01-23 17:23:38 +0100492 return lp;
493 }
494
495 private WindowManager.LayoutParams getBottomLayoutParams() {
496 WindowManager.LayoutParams lp = getWindowLayoutParams();
497 lp.setTitle("ScreenDecorOverlayBottom");
Beverlye91f0d02018-05-15 14:40:47 -0400498 if (mRotation == RotationUtils.ROTATION_SEASCAPE
499 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
500 lp.gravity = Gravity.TOP | Gravity.LEFT;
501 } else {
502 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
503 }
Adrian Roos5b518852018-01-23 17:23:38 +0100504 return lp;
505 }
506
507 private void updateLayoutParams() {
508 mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
509 mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
510 }
511
512 @Override
513 public void onTuningChanged(String key, String newValue) {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200514 mHandler.post(() -> {
515 if (mOverlay == null) return;
516 if (SIZE.equals(key)) {
517 int size = mRoundedDefault;
518 int sizeTop = mRoundedDefaultTop;
519 int sizeBottom = mRoundedDefaultBottom;
520 if (newValue != null) {
521 try {
522 size = (int) (Integer.parseInt(newValue) * mDensity);
523 } catch (Exception e) {
524 }
Beverlya5f7a302018-04-25 09:19:05 -0400525 }
Adrian Roos72940642018-08-09 15:23:56 +0000526
Adrian Roosfaa102f2018-08-02 15:56:15 +0200527 if (sizeTop == 0) {
528 sizeTop = size;
529 }
530 if (sizeBottom == 0) {
531 sizeBottom = size;
532 }
Adrian Roos72940642018-08-09 15:23:56 +0000533
Adrian Roosfaa102f2018-08-02 15:56:15 +0200534 setSize(mOverlay.findViewById(R.id.left), sizeTop);
535 setSize(mOverlay.findViewById(R.id.right), sizeTop);
536 setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
537 setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
538 }
539 });
Adrian Roos5b518852018-01-23 17:23:38 +0100540 }
541
542 private void setSize(View view, int pixelSize) {
543 LayoutParams params = view.getLayoutParams();
544 params.width = pixelSize;
545 params.height = pixelSize;
546 view.setLayoutParams(params);
547 }
548
549 @VisibleForTesting
550 static class TunablePaddingTagListener implements FragmentListener {
551
552 private final int mPadding;
553 private final int mId;
554 private TunablePadding mTunablePadding;
555
556 public TunablePaddingTagListener(int padding, int id) {
557 mPadding = padding;
558 mId = id;
559 }
560
561 @Override
562 public void onFragmentViewCreated(String tag, Fragment fragment) {
563 if (mTunablePadding != null) {
564 mTunablePadding.destroy();
565 }
566 View view = fragment.getView();
567 if (mId != 0) {
568 view = view.findViewById(mId);
569 }
570 mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
571 FLAG_START | FLAG_END);
572 }
573 }
574
Evan Lairdb0506ca2018-03-15 10:34:31 -0400575 public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
576 RegionInterceptableView {
Adrian Roos5b518852018-01-23 17:23:38 +0100577
578 private final DisplayInfo mInfo = new DisplayInfo();
579 private final Paint mPaint = new Paint();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200580 private final List<Rect> mBounds = new ArrayList();
Adrian Roos5b518852018-01-23 17:23:38 +0100581 private final Rect mBoundingRect = new Rect();
582 private final Path mBoundingPath = new Path();
583 private final int[] mLocation = new int[2];
Adrian Roos9acf8132018-05-31 19:54:53 +0200584 private final boolean mInitialStart;
Adrian Roos5b518852018-01-23 17:23:38 +0100585 private final Runnable mVisibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700586 private final ScreenDecorations mDecorations;
Adrian Roos8b29a842018-05-31 14:14:13 +0200587 private int mColor = Color.BLACK;
Adrian Roos9acf8132018-05-31 19:54:53 +0200588 private boolean mStart;
589 private int mRotation;
Adrian Roos5b518852018-01-23 17:23:38 +0100590
591 public DisplayCutoutView(Context context, boolean start,
Vishnu Nair83537a72018-07-19 21:27:48 -0700592 Runnable visibilityChangedListener, ScreenDecorations decorations) {
Adrian Roos5b518852018-01-23 17:23:38 +0100593 super(context);
Adrian Roos9acf8132018-05-31 19:54:53 +0200594 mInitialStart = start;
Adrian Roos5b518852018-01-23 17:23:38 +0100595 mVisibilityChangedListener = visibilityChangedListener;
Vishnu Nair83537a72018-07-19 21:27:48 -0700596 mDecorations = decorations;
Adrian Roos5b518852018-01-23 17:23:38 +0100597 setId(R.id.display_cutout);
Adrian Roosfaa102f2018-08-02 15:56:15 +0200598 if (DEBUG) {
599 getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
600 (mInitialStart ? "OverlayTop" : "OverlayBottom")
601 + " drawn in rot " + mRotation));
602 }
Adrian Roos5b518852018-01-23 17:23:38 +0100603 }
604
Adrian Roos8b29a842018-05-31 14:14:13 +0200605 public void setColor(int color) {
606 mColor = color;
607 invalidate();
608 }
609
Adrian Roos5b518852018-01-23 17:23:38 +0100610 @Override
611 protected void onAttachedToWindow() {
612 super.onAttachedToWindow();
613 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
614 getHandler());
615 update();
616 }
617
618 @Override
619 protected void onDetachedFromWindow() {
620 super.onDetachedFromWindow();
621 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
622 }
623
624 @Override
625 protected void onDraw(Canvas canvas) {
626 super.onDraw(canvas);
627 getLocationOnScreen(mLocation);
628 canvas.translate(-mLocation[0], -mLocation[1]);
629 if (!mBoundingPath.isEmpty()) {
Adrian Roos8b29a842018-05-31 14:14:13 +0200630 mPaint.setColor(mColor);
Adrian Roos5b518852018-01-23 17:23:38 +0100631 mPaint.setStyle(Paint.Style.FILL);
Adrian Roos51072a82018-04-10 15:17:08 -0700632 mPaint.setAntiAlias(true);
Adrian Roos5b518852018-01-23 17:23:38 +0100633 canvas.drawPath(mBoundingPath, mPaint);
634 }
635 }
636
637 @Override
638 public void onDisplayAdded(int displayId) {
639 }
640
641 @Override
642 public void onDisplayRemoved(int displayId) {
643 }
644
645 @Override
646 public void onDisplayChanged(int displayId) {
647 if (displayId == getDisplay().getDisplayId()) {
648 update();
649 }
650 }
651
Adrian Roos9acf8132018-05-31 19:54:53 +0200652 public void setRotation(int rotation) {
653 mRotation = rotation;
654 update();
655 }
656
657 private boolean isStart() {
658 final boolean flipped = (mRotation == RotationUtils.ROTATION_SEASCAPE
659 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN);
660 return flipped ? !mInitialStart : mInitialStart;
661 }
662
Adrian Roos5b518852018-01-23 17:23:38 +0100663 private void update() {
Vishnu Nair83537a72018-07-19 21:27:48 -0700664 if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
Adrian Roos9acf8132018-05-31 19:54:53 +0200665 return;
666 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700667 mStart = isStart();
Adrian Roos5b518852018-01-23 17:23:38 +0100668 requestLayout();
669 getDisplay().getDisplayInfo(mInfo);
Issei Suzuki43190bd2018-08-20 17:28:41 +0200670 mBounds.clear();
Adrian Roos5b518852018-01-23 17:23:38 +0100671 mBoundingRect.setEmpty();
672 mBoundingPath.reset();
673 int newVisible;
Adrian Roosc41b32b2018-04-12 10:13:48 +0200674 if (shouldDrawCutout(getContext()) && hasCutout()) {
Issei Suzuki43190bd2018-08-20 17:28:41 +0200675 mBounds.addAll(mInfo.displayCutout.getBoundingRects());
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100676 localBounds(mBoundingRect);
Adrian Roos0098aee2019-06-03 16:47:02 +0200677 updateGravity();
Adrian Roos51072a82018-04-10 15:17:08 -0700678 updateBoundingPath();
Adrian Roos1b028282018-03-14 14:43:03 +0100679 invalidate();
Adrian Roos5b518852018-01-23 17:23:38 +0100680 newVisible = VISIBLE;
681 } else {
682 newVisible = GONE;
683 }
684 if (newVisible != getVisibility()) {
685 setVisibility(newVisible);
686 mVisibilityChangedListener.run();
687 }
688 }
689
Adrian Roos51072a82018-04-10 15:17:08 -0700690 private void updateBoundingPath() {
691 int lw = mInfo.logicalWidth;
692 int lh = mInfo.logicalHeight;
693
694 boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
695
696 int dw = flipped ? lh : lw;
697 int dh = flipped ? lw : lh;
698
Adrian Roosa99f5d62018-04-16 16:03:04 +0200699 mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
Adrian Roos51072a82018-04-10 15:17:08 -0700700 Matrix m = new Matrix();
701 transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
702 mBoundingPath.transform(m);
703 }
704
705 private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
706 @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
707 switch (rotation) {
708 case ROTATION_0:
709 out.reset();
710 break;
711 case ROTATION_90:
712 out.setRotate(270);
713 out.postTranslate(0, physicalWidth);
714 break;
715 case ROTATION_180:
716 out.setRotate(180);
717 out.postTranslate(physicalWidth, physicalHeight);
718 break;
719 case ROTATION_270:
720 out.setRotate(90);
721 out.postTranslate(physicalHeight, 0);
722 break;
723 default:
724 throw new IllegalArgumentException("Unknown rotation: " + rotation);
725 }
726 }
727
Adrian Roos0098aee2019-06-03 16:47:02 +0200728 private void updateGravity() {
729 LayoutParams lp = getLayoutParams();
730 if (lp instanceof FrameLayout.LayoutParams) {
731 FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) lp;
732 int newGravity = getGravity(mInfo.displayCutout);
733 if (flp.gravity != newGravity) {
734 flp.gravity = newGravity;
735 setLayoutParams(flp);
736 }
737 }
738 }
739
Adrian Roos5b518852018-01-23 17:23:38 +0100740 private boolean hasCutout() {
Adrian Roos24264212018-02-19 16:26:15 +0100741 final DisplayCutout displayCutout = mInfo.displayCutout;
742 if (displayCutout == null) {
Adrian Roos5b518852018-01-23 17:23:38 +0100743 return false;
744 }
Adrian Roos5b518852018-01-23 17:23:38 +0100745 if (mStart) {
746 return displayCutout.getSafeInsetLeft() > 0
747 || displayCutout.getSafeInsetTop() > 0;
748 } else {
749 return displayCutout.getSafeInsetRight() > 0
750 || displayCutout.getSafeInsetBottom() > 0;
751 }
752 }
753
754 @Override
755 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100756 if (mBounds.isEmpty()) {
Adrian Roos5b518852018-01-23 17:23:38 +0100757 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
758 return;
759 }
760 setMeasuredDimension(
761 resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
762 resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
763 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100764
Jorim Jaggi71aaa402018-06-06 17:22:56 +0200765 public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
766 Rect out) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100767 switch (gravity) {
768 case Gravity.TOP:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200769 out.set(displayCutout.getBoundingRectTop());
Evan Laird6703f422018-10-11 13:24:05 -0400770 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100771 case Gravity.LEFT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200772 out.set(displayCutout.getBoundingRectLeft());
Evan Laird6703f422018-10-11 13:24:05 -0400773 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100774 case Gravity.BOTTOM:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200775 out.set(displayCutout.getBoundingRectBottom());
Evan Laird6703f422018-10-11 13:24:05 -0400776 break;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100777 case Gravity.RIGHT:
Issei Suzuki43190bd2018-08-20 17:28:41 +0200778 out.set(displayCutout.getBoundingRectRight());
Evan Laird6703f422018-10-11 13:24:05 -0400779 break;
780 default:
781 out.setEmpty();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100782 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100783 }
784
785 private void localBounds(Rect out) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200786 DisplayCutout displayCutout = mInfo.displayCutout;
787 boundsFromDirection(displayCutout, getGravity(displayCutout), out);
788 }
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100789
Adrian Roos0098aee2019-06-03 16:47:02 +0200790 private int getGravity(DisplayCutout displayCutout) {
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100791 if (mStart) {
792 if (displayCutout.getSafeInsetLeft() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200793 return Gravity.LEFT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100794 } else if (displayCutout.getSafeInsetTop() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200795 return Gravity.TOP;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100796 }
797 } else {
798 if (displayCutout.getSafeInsetRight() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200799 return Gravity.RIGHT;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100800 } else if (displayCutout.getSafeInsetBottom() > 0) {
Adrian Roos0098aee2019-06-03 16:47:02 +0200801 return Gravity.BOTTOM;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100802 }
803 }
Adrian Roos0098aee2019-06-03 16:47:02 +0200804 return Gravity.NO_GRAVITY;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100805 }
Evan Lairdb0506ca2018-03-15 10:34:31 -0400806
807 @Override
808 public boolean shouldInterceptTouch() {
809 return mInfo.displayCutout != null && getVisibility() == VISIBLE;
810 }
811
812 @Override
813 public Region getInterceptRegion() {
814 if (mInfo.displayCutout == null) {
815 return null;
816 }
817
Adrian Roos134e1cb2018-05-16 17:04:29 +0200818 View rootView = getRootView();
Issei Suzuki43190bd2018-08-20 17:28:41 +0200819 Region cutoutBounds = rectsToRegion(
820 mInfo.displayCutout.getBoundingRects());
Adrian Roos134e1cb2018-05-16 17:04:29 +0200821
822 // Transform to window's coordinate space
823 rootView.getLocationOnScreen(mLocation);
824 cutoutBounds.translate(-mLocation[0], -mLocation[1]);
825
826 // Intersect with window's frame
827 cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
828 rootView.getBottom(), Region.Op.INTERSECT);
829
830 return cutoutBounds;
Evan Lairdb0506ca2018-03-15 10:34:31 -0400831 }
Adrian Roos5b518852018-01-23 17:23:38 +0100832 }
Beverlye91f0d02018-05-15 14:40:47 -0400833
834 private boolean isLandscape(int rotation) {
835 return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
836 RotationUtils.ROTATION_SEASCAPE;
837 }
Vishnu Nair83537a72018-07-19 21:27:48 -0700838
839 /**
840 * A pre-draw listener, that cancels the draw and restarts the traversal with the updated
841 * window attributes.
842 */
843 private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
844
845 private final View mView;
Adrian Roosfaa102f2018-08-02 15:56:15 +0200846 private final int mTargetRotation;
Vishnu Nair83537a72018-07-19 21:27:48 -0700847
Adrian Roosfaa102f2018-08-02 15:56:15 +0200848 private RestartingPreDrawListener(View view, int targetRotation) {
849 mView = view;
850 mTargetRotation = targetRotation;
851 }
852
853 @Override
854 public boolean onPreDraw() {
855 mView.getViewTreeObserver().removeOnPreDrawListener(this);
856
857 if (mTargetRotation == mRotation) {
858 if (DEBUG) {
859 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
860 + " already in target rot "
861 + mTargetRotation + ", allow draw without restarting it");
862 }
863 return true;
864 }
865
866 mPendingRotationChange = false;
867 // This changes the window attributes - we need to restart the traversal for them to
868 // take effect.
869 updateOrientation();
870 if (DEBUG) {
871 Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
872 + " restarting listener fired, restarting draw for rot " + mRotation);
873 }
874 mView.invalidate();
875 return false;
876 }
877 }
878
879 /**
880 * A pre-draw listener, that validates that the rotation we draw in matches the displays
881 * rotation before continuing the draw.
882 *
883 * This is to prevent a race condition, where we have not received the display changed event
884 * yet, and would thus draw in an old orientation.
885 */
886 private class ValidatingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
887
888 private final View mView;
889
890 public ValidatingPreDrawListener(View view) {
Adrian Roos61f557a2018-08-02 15:56:15 +0200891 mView = view;
Adrian Roos61f557a2018-08-02 15:56:15 +0200892 }
893
894 @Override
895 public boolean onPreDraw() {
Adrian Roosfaa102f2018-08-02 15:56:15 +0200896 final int displayRotation = RotationUtils.getExactRotation(mContext);
897 if (displayRotation != mRotation && !mPendingRotationChange) {
898 if (DEBUG) {
899 Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot "
900 + displayRotation + ". Restarting draw");
901 }
902 mView.invalidate();
903 return false;
904 }
905 return true;
Adrian Roos61f557a2018-08-02 15:56:15 +0200906 }
907 }
Adrian Roos5b518852018-01-23 17:23:38 +0100908}