• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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