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