1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.views; 18 19 import static android.view.MotionEvent.ACTION_CANCEL; 20 import static android.view.MotionEvent.ACTION_DOWN; 21 import static android.view.MotionEvent.ACTION_UP; 22 23 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; 24 25 import android.annotation.TargetApi; 26 import android.app.WallpaperInfo; 27 import android.app.WallpaperManager; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.graphics.Insets; 32 import android.graphics.Rect; 33 import android.graphics.RectF; 34 import android.os.Build; 35 import android.util.AttributeSet; 36 import android.util.Log; 37 import android.util.Property; 38 import android.view.MotionEvent; 39 import android.view.View; 40 import android.view.ViewDebug; 41 import android.view.ViewGroup; 42 import android.view.WindowInsets; 43 import android.view.accessibility.AccessibilityEvent; 44 import android.widget.FrameLayout; 45 46 import androidx.annotation.Nullable; 47 48 import com.android.launcher3.AbstractFloatingView; 49 import com.android.launcher3.InsettableFrameLayout; 50 import com.android.launcher3.R; 51 import com.android.launcher3.Utilities; 52 import com.android.launcher3.testing.TestProtocol; 53 import com.android.launcher3.util.MultiValueAlpha; 54 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; 55 import com.android.launcher3.util.SimpleBroadcastReceiver; 56 import com.android.launcher3.util.TouchController; 57 58 import java.io.PrintWriter; 59 import java.util.ArrayList; 60 61 /** 62 * A viewgroup with utility methods for drag-n-drop and touch interception 63 */ 64 public abstract class BaseDragLayer<T extends Context & ActivityContext> 65 extends InsettableFrameLayout { 66 67 public static final Property<LayoutParams, Integer> LAYOUT_X = 68 new Property<LayoutParams, Integer>(Integer.TYPE, "x") { 69 @Override 70 public Integer get(LayoutParams lp) { 71 return lp.x; 72 } 73 74 @Override 75 public void set(LayoutParams lp, Integer x) { 76 lp.x = x; 77 } 78 }; 79 80 public static final Property<LayoutParams, Integer> LAYOUT_Y = 81 new Property<LayoutParams, Integer>(Integer.TYPE, "y") { 82 @Override 83 public Integer get(LayoutParams lp) { 84 return lp.y; 85 } 86 87 @Override 88 public void set(LayoutParams lp, Integer y) { 89 lp.y = y; 90 } 91 }; 92 93 // Touch coming from normal view system is being dispatched. 94 private static final int TOUCH_DISPATCHING_FROM_VIEW = 1 << 0; 95 // Touch is being dispatched through the normal view dispatch system, and started at the 96 // system gesture region. In this case we prevent internal gesture handling and only allow 97 // normal view event handling. 98 private static final int TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION = 1 << 1; 99 // Touch coming from InputMonitor proxy is being dispatched 'only to gestures'. Note that both 100 // this and view-system can be active at the same time where view-system would go to the views, 101 // and this would go to the gestures. 102 // Note that this is not set when events are coming from proxy, but going through full dispatch 103 // process (both views and gestures) to allow view-system to easily take over in case it 104 // comes later. 105 private static final int TOUCH_DISPATCHING_FROM_PROXY = 1 << 2; 106 // ACTION_DOWN has been dispatched to child views and ACTION_UP or ACTION_CANCEL is pending. 107 // Note that the event source can either be view-dispatching or proxy-dispatching based on if 108 // TOUCH_DISPATCHING_VIEW is present or not. 109 private static final int TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS = 1 << 3; 110 111 protected final float[] mTmpXY = new float[2]; 112 protected final float[] mTmpRectPoints = new float[4]; 113 protected final Rect mHitRect = new Rect(); 114 115 @ViewDebug.ExportedProperty(category = "launcher") 116 private final RectF mSystemGestureRegion = new RectF(); 117 private int mTouchDispatchState = 0; 118 119 protected final T mActivity; 120 private final MultiValueAlpha mMultiValueAlpha; 121 private final WallpaperManager mWallpaperManager; 122 private final SimpleBroadcastReceiver mWallpaperChangeReceiver = 123 new SimpleBroadcastReceiver(this::onWallpaperChanged); 124 private final String[] mWallpapersWithoutSysuiScrims; 125 126 // All the touch controllers for the view 127 protected TouchController[] mControllers; 128 // Touch controller which is currently active for the normal view dispatch 129 protected TouchController mActiveController; 130 // Touch controller which is being used for the proxy events 131 protected TouchController mProxyTouchController; 132 133 private TouchCompleteListener mTouchCompleteListener; 134 135 protected boolean mAllowSysuiScrims = true; 136 BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount)137 public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) { 138 super(context, attrs); 139 mActivity = (T) ActivityContext.lookupContext(context); 140 mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount); 141 mWallpaperManager = context.getSystemService(WallpaperManager.class); 142 mWallpapersWithoutSysuiScrims = getResources().getStringArray( 143 R.array.live_wallpapers_remove_sysui_scrims); 144 } 145 146 /** 147 * Called to reinitialize touch controllers. 148 */ recreateControllers()149 public abstract void recreateControllers(); 150 151 /** 152 * Same as {@link #isEventOverView(View, MotionEvent, View)} where evView == this drag layer. 153 */ isEventOverView(View view, MotionEvent ev)154 public boolean isEventOverView(View view, MotionEvent ev) { 155 getDescendantRectRelativeToSelf(view, mHitRect); 156 return mHitRect.contains((int) ev.getX(), (int) ev.getY()); 157 } 158 159 /** 160 * Given a motion event in evView's coordinates, return whether the event is within another 161 * view's bounds. 162 */ isEventOverView(View view, MotionEvent ev, View evView)163 public boolean isEventOverView(View view, MotionEvent ev, View evView) { 164 int[] xy = new int[] {(int) ev.getX(), (int) ev.getY()}; 165 getDescendantCoordRelativeToSelf(evView, xy); 166 getDescendantRectRelativeToSelf(view, mHitRect); 167 return mHitRect.contains(xy[0], xy[1]); 168 } 169 170 @Override onInterceptTouchEvent(MotionEvent ev)171 public boolean onInterceptTouchEvent(MotionEvent ev) { 172 int action = ev.getAction(); 173 174 if (action == ACTION_UP || action == ACTION_CANCEL) { 175 if (mTouchCompleteListener != null) { 176 mTouchCompleteListener.onTouchComplete(); 177 } 178 mTouchCompleteListener = null; 179 } else if (action == MotionEvent.ACTION_DOWN) { 180 mActivity.finishAutoCancelActionMode(); 181 } 182 return findActiveController(ev); 183 } 184 isEventInLauncher(MotionEvent ev)185 private boolean isEventInLauncher(MotionEvent ev) { 186 final float x = ev.getX(); 187 final float y = ev.getY(); 188 189 return x >= mSystemGestureRegion.left && x < getWidth() - mSystemGestureRegion.right 190 && y >= mSystemGestureRegion.top && y < getHeight() - mSystemGestureRegion.bottom; 191 } 192 findControllerToHandleTouch(MotionEvent ev)193 private TouchController findControllerToHandleTouch(MotionEvent ev) { 194 if (TestProtocol.sDebugTracing) { 195 Log.d(TestProtocol.PAUSE_NOT_DETECTED, "findControllerToHandleTouch ev=" + ev 196 + ", isEventInLauncher=" + isEventInLauncher(ev) 197 + ", topOpenView=" + AbstractFloatingView.getTopOpenView(mActivity)); 198 } 199 200 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); 201 if (topView != null 202 && (isEventInLauncher(ev) || topView.canInterceptEventsInSystemGestureRegion()) 203 && topView.onControllerInterceptTouchEvent(ev)) { 204 return topView; 205 } 206 207 for (TouchController controller : mControllers) { 208 if (controller.onControllerInterceptTouchEvent(ev)) { 209 return controller; 210 } 211 } 212 return null; 213 } 214 findActiveController(MotionEvent ev)215 protected boolean findActiveController(MotionEvent ev) { 216 mActiveController = null; 217 if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION 218 | TOUCH_DISPATCHING_FROM_PROXY)) == 0) { 219 // Only look for controllers if we are not dispatching from gesture area and proxy is 220 // not active 221 mActiveController = findControllerToHandleTouch(ev); 222 } 223 return mActiveController != null; 224 } 225 226 @Override onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)227 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { 228 // Shortcuts can appear above folder 229 View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity, 230 AbstractFloatingView.TYPE_ACCESSIBLE); 231 if (topView != null) { 232 if (child == topView) { 233 return super.onRequestSendAccessibilityEvent(child, event); 234 } 235 // Skip propagating onRequestSendAccessibilityEvent for all other children 236 // which are not topView 237 return false; 238 } 239 return super.onRequestSendAccessibilityEvent(child, event); 240 } 241 242 @Override addChildrenForAccessibility(ArrayList<View> childrenForAccessibility)243 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { 244 View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity, 245 AbstractFloatingView.TYPE_ACCESSIBLE); 246 if (topView != null) { 247 // Only add the top view as a child for accessibility when it is open 248 addAccessibleChildToList(topView, childrenForAccessibility); 249 } else { 250 super.addChildrenForAccessibility(childrenForAccessibility); 251 } 252 } 253 addAccessibleChildToList(View child, ArrayList<View> outList)254 protected void addAccessibleChildToList(View child, ArrayList<View> outList) { 255 if (child.isImportantForAccessibility()) { 256 outList.add(child); 257 } else { 258 child.addChildrenForAccessibility(outList); 259 } 260 } 261 262 @Override onViewRemoved(View child)263 public void onViewRemoved(View child) { 264 super.onViewRemoved(child); 265 if (child instanceof AbstractFloatingView) { 266 // Handles the case where the view is removed without being properly closed. 267 // This can happen if something goes wrong during a state change/transition. 268 AbstractFloatingView floatingView = (AbstractFloatingView) child; 269 if (floatingView.isOpen()) { 270 postDelayed(() -> floatingView.close(false), getSingleFrameMs(getContext())); 271 } 272 } 273 } 274 275 @Override onTouchEvent(MotionEvent ev)276 public boolean onTouchEvent(MotionEvent ev) { 277 int action = ev.getAction(); 278 if (action == ACTION_UP || action == ACTION_CANCEL) { 279 if (mTouchCompleteListener != null) { 280 mTouchCompleteListener.onTouchComplete(); 281 } 282 mTouchCompleteListener = null; 283 } 284 285 if (mActiveController != null) { 286 return mActiveController.onControllerTouchEvent(ev); 287 } else { 288 // In case no child view handled the touch event, we may not get onIntercept anymore 289 return findActiveController(ev); 290 } 291 } 292 293 @Override dispatchTouchEvent(MotionEvent ev)294 public boolean dispatchTouchEvent(MotionEvent ev) { 295 switch (ev.getAction()) { 296 case ACTION_DOWN: { 297 if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) { 298 // Cancel the previous touch 299 int action = ev.getAction(); 300 ev.setAction(ACTION_CANCEL); 301 super.dispatchTouchEvent(ev); 302 ev.setAction(action); 303 } 304 mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW 305 | TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS; 306 307 if (isEventInLauncher(ev)) { 308 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION; 309 } else { 310 mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION; 311 } 312 break; 313 } 314 case ACTION_CANCEL: 315 case ACTION_UP: 316 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION; 317 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW; 318 mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS; 319 break; 320 } 321 super.dispatchTouchEvent(ev); 322 323 // We want to get all events so that mTouchDispatchSource is maintained properly 324 return true; 325 } 326 327 /** 328 * Proxies the touch events to the gesture handlers 329 */ proxyTouchEvent(MotionEvent ev, boolean allowViewDispatch)330 public boolean proxyTouchEvent(MotionEvent ev, boolean allowViewDispatch) { 331 int actionMasked = ev.getActionMasked(); 332 boolean isViewDispatching = (mTouchDispatchState & TOUCH_DISPATCHING_FROM_VIEW) != 0; 333 334 // Only do view dispatch if another view-dispatching is not running, or we already started 335 // proxy-dispatching before. Note that view-dispatching can always take over the proxy 336 // dispatching at anytime, but not vice-versa. 337 allowViewDispatch = allowViewDispatch && !isViewDispatching 338 && (actionMasked == ACTION_DOWN 339 || ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0)); 340 341 if (allowViewDispatch) { 342 mTouchDispatchState |= TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS; 343 super.dispatchTouchEvent(ev); 344 345 if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) { 346 mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS; 347 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY; 348 } 349 return true; 350 } else { 351 boolean handled; 352 if (mProxyTouchController != null) { 353 handled = mProxyTouchController.onControllerTouchEvent(ev); 354 } else { 355 if (actionMasked == ACTION_DOWN) { 356 if (isViewDispatching && mActiveController != null) { 357 // A controller is already active, we can't initiate our own controller 358 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY; 359 } else { 360 // We will control the handler via proxy 361 mTouchDispatchState |= TOUCH_DISPATCHING_FROM_PROXY; 362 } 363 } 364 if ((mTouchDispatchState & TOUCH_DISPATCHING_FROM_PROXY) != 0) { 365 mProxyTouchController = findControllerToHandleTouch(ev); 366 } 367 handled = mProxyTouchController != null; 368 } 369 if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) { 370 mProxyTouchController = null; 371 mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY; 372 } 373 return handled; 374 } 375 } 376 377 /** 378 * Determine the rect of the descendant in this DragLayer's coordinates 379 * 380 * @param descendant The descendant whose coordinates we want to find. 381 * @param r The rect into which to place the results. 382 * @return The factor by which this descendant is scaled relative to this DragLayer. 383 */ getDescendantRectRelativeToSelf(View descendant, Rect r)384 public float getDescendantRectRelativeToSelf(View descendant, Rect r) { 385 mTmpRectPoints[0] = 0; 386 mTmpRectPoints[1] = 0; 387 mTmpRectPoints[2] = descendant.getWidth(); 388 mTmpRectPoints[3] = descendant.getHeight(); 389 float s = getDescendantCoordRelativeToSelf(descendant, mTmpRectPoints); 390 r.left = Math.round(Math.min(mTmpRectPoints[0], mTmpRectPoints[2])); 391 r.top = Math.round(Math.min(mTmpRectPoints[1], mTmpRectPoints[3])); 392 r.right = Math.round(Math.max(mTmpRectPoints[0], mTmpRectPoints[2])); 393 r.bottom = Math.round(Math.max(mTmpRectPoints[1], mTmpRectPoints[3])); 394 return s; 395 } 396 getLocationInDragLayer(View child, int[] loc)397 public float getLocationInDragLayer(View child, int[] loc) { 398 loc[0] = 0; 399 loc[1] = 0; 400 return getDescendantCoordRelativeToSelf(child, loc); 401 } 402 getDescendantCoordRelativeToSelf(View descendant, int[] coord)403 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { 404 mTmpXY[0] = coord[0]; 405 mTmpXY[1] = coord[1]; 406 float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); 407 Utilities.roundArray(mTmpXY, coord); 408 return scale; 409 } 410 getDescendantCoordRelativeToSelf(View descendant, float[] coord)411 public float getDescendantCoordRelativeToSelf(View descendant, float[] coord) { 412 return getDescendantCoordRelativeToSelf(descendant, coord, false); 413 } 414 415 /** 416 * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's 417 * coordinates. 418 * 419 * @param descendant The descendant to which the passed coordinate is relative. 420 * @param coord The coordinate that we want mapped. 421 * @param includeRootScroll Whether or not to account for the scroll of the root descendant: 422 * sometimes this is relevant as in a child's coordinates within the root descendant. 423 * @return The factor by which this descendant is scaled relative to this DragLayer. Caution 424 * this scale factor is assumed to be equal in X and Y, and so if at any point this 425 * assumption fails, we will need to return a pair of scale factors. 426 */ getDescendantCoordRelativeToSelf(View descendant, float[] coord, boolean includeRootScroll)427 public float getDescendantCoordRelativeToSelf(View descendant, float[] coord, 428 boolean includeRootScroll) { 429 return Utilities.getDescendantCoordRelativeToAncestor(descendant, this, 430 coord, includeRootScroll); 431 } 432 433 /** 434 * Inverse of {@link #getDescendantCoordRelativeToSelf(View, float[])}. 435 */ mapCoordInSelfToDescendant(View descendant, float[] coord)436 public void mapCoordInSelfToDescendant(View descendant, float[] coord) { 437 Utilities.mapCoordInSelfToDescendant(descendant, this, coord); 438 } 439 440 /** 441 * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. 442 */ mapCoordInSelfToDescendant(View descendant, int[] coord)443 public void mapCoordInSelfToDescendant(View descendant, int[] coord) { 444 mTmpXY[0] = coord[0]; 445 mTmpXY[1] = coord[1]; 446 Utilities.mapCoordInSelfToDescendant(descendant, this, mTmpXY); 447 Utilities.roundArray(mTmpXY, coord); 448 } 449 getViewRectRelativeToSelf(View v, Rect r)450 public void getViewRectRelativeToSelf(View v, Rect r) { 451 int[] loc = new int[2]; 452 getLocationInWindow(loc); 453 int x = loc[0]; 454 int y = loc[1]; 455 456 v.getLocationInWindow(loc); 457 int vX = loc[0]; 458 int vY = loc[1]; 459 460 int left = vX - x; 461 int top = vY - y; 462 r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); 463 } 464 465 @Override dispatchUnhandledMove(View focused, int direction)466 public boolean dispatchUnhandledMove(View focused, int direction) { 467 // Consume the unhandled move if a container is open, to avoid switching pages underneath. 468 return AbstractFloatingView.getTopOpenView(mActivity) != null; 469 } 470 471 @Override onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)472 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 473 View topView = AbstractFloatingView.getTopOpenView(mActivity); 474 if (topView != null) { 475 return topView.requestFocus(direction, previouslyFocusedRect); 476 } else { 477 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 478 } 479 } 480 481 @Override addFocusables(ArrayList<View> views, int direction, int focusableMode)482 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 483 View topView = AbstractFloatingView.getTopOpenView(mActivity); 484 if (topView != null) { 485 topView.addFocusables(views, direction); 486 } else { 487 super.addFocusables(views, direction, focusableMode); 488 } 489 } 490 setTouchCompleteListener(TouchCompleteListener listener)491 public void setTouchCompleteListener(TouchCompleteListener listener) { 492 mTouchCompleteListener = listener; 493 } 494 495 public interface TouchCompleteListener { onTouchComplete()496 void onTouchComplete(); 497 } 498 499 @Override generateLayoutParams(AttributeSet attrs)500 public LayoutParams generateLayoutParams(AttributeSet attrs) { 501 return new LayoutParams(getContext(), attrs); 502 } 503 504 @Override generateDefaultLayoutParams()505 protected LayoutParams generateDefaultLayoutParams() { 506 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 507 } 508 509 // Override to allow type-checking of LayoutParams. 510 @Override checkLayoutParams(ViewGroup.LayoutParams p)511 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 512 return p instanceof LayoutParams; 513 } 514 515 @Override generateLayoutParams(ViewGroup.LayoutParams p)516 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 517 return new LayoutParams(p); 518 } 519 getAlphaProperty(int index)520 public AlphaProperty getAlphaProperty(int index) { 521 return mMultiValueAlpha.getProperty(index); 522 } 523 dump(String prefix, PrintWriter writer)524 public void dump(String prefix, PrintWriter writer) { 525 writer.println(prefix + "DragLayer:"); 526 if (mActiveController != null) { 527 writer.println(prefix + "\tactiveController: " + mActiveController); 528 mActiveController.dump(prefix + "\t", writer); 529 } 530 writer.println(prefix + "\tdragLayerAlpha : " + mMultiValueAlpha ); 531 } 532 533 public static class LayoutParams extends InsettableFrameLayout.LayoutParams { 534 public int x, y; 535 public boolean customPosition = false; 536 LayoutParams(Context c, AttributeSet attrs)537 public LayoutParams(Context c, AttributeSet attrs) { 538 super(c, attrs); 539 } 540 LayoutParams(int width, int height)541 public LayoutParams(int width, int height) { 542 super(width, height); 543 } 544 LayoutParams(ViewGroup.LayoutParams lp)545 public LayoutParams(ViewGroup.LayoutParams lp) { 546 super(lp); 547 } 548 } 549 onLayout(boolean changed, int l, int t, int r, int b)550 protected void onLayout(boolean changed, int l, int t, int r, int b) { 551 super.onLayout(changed, l, t, r, b); 552 int count = getChildCount(); 553 for (int i = 0; i < count; i++) { 554 View child = getChildAt(i); 555 final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); 556 if (flp instanceof LayoutParams) { 557 final LayoutParams lp = (LayoutParams) flp; 558 if (lp.customPosition) { 559 child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); 560 } 561 } 562 } 563 } 564 565 @Override 566 @TargetApi(Build.VERSION_CODES.Q) dispatchApplyWindowInsets(WindowInsets insets)567 public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { 568 if (Utilities.ATLEAST_Q) { 569 Insets gestureInsets = insets.getMandatorySystemGestureInsets(); 570 mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top, 571 gestureInsets.right, gestureInsets.bottom); 572 } 573 return super.dispatchApplyWindowInsets(insets); 574 } 575 576 @Override onAttachedToWindow()577 protected void onAttachedToWindow() { 578 super.onAttachedToWindow(); 579 mWallpaperChangeReceiver.register(mActivity, Intent.ACTION_WALLPAPER_CHANGED); 580 onWallpaperChanged(null); 581 } 582 583 @Override onDetachedFromWindow()584 protected void onDetachedFromWindow() { 585 super.onDetachedFromWindow(); 586 mActivity.unregisterReceiver(mWallpaperChangeReceiver); 587 } 588 onWallpaperChanged(Intent unusedBroadcastIntent)589 private void onWallpaperChanged(Intent unusedBroadcastIntent) { 590 WallpaperInfo newWallpaperInfo = mWallpaperManager.getWallpaperInfo(); 591 boolean oldAllowSysuiScrims = mAllowSysuiScrims; 592 mAllowSysuiScrims = computeAllowSysuiScrims(newWallpaperInfo); 593 if (mAllowSysuiScrims != oldAllowSysuiScrims) { 594 // Reapply insets so scrim can be removed or re-added if necessary. 595 setInsets(mInsets); 596 } 597 } 598 599 /** 600 * Determines whether we can scrim the status bar and nav bar for the given wallpaper by 601 * checking against a list of live wallpapers that we don't show the scrims on. 602 */ computeAllowSysuiScrims(@ullable WallpaperInfo newWallpaperInfo)603 private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) { 604 if (newWallpaperInfo == null) { 605 // New wallpaper is static, not live. Thus, blacklist isn't applicable. 606 return true; 607 } 608 ComponentName newWallpaper = newWallpaperInfo.getComponent(); 609 for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) { 610 if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) { 611 // New wallpaper is blacklisted from showing a scrim. 612 return false; 613 } 614 } 615 return true; 616 } 617 } 618