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