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