• 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 package com.android.launcher3.uioverrides.touchcontrollers;
17 
18 import static android.view.MotionEvent.ACTION_CANCEL;
19 import static android.view.MotionEvent.ACTION_DOWN;
20 import static android.view.MotionEvent.ACTION_MOVE;
21 import static android.view.MotionEvent.ACTION_UP;
22 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
23 
24 import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
25 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
26 
27 import android.graphics.PointF;
28 import android.util.SparseArray;
29 import android.view.MotionEvent;
30 import android.view.ViewConfiguration;
31 import android.view.Window;
32 import android.view.WindowManager;
33 
34 import com.android.launcher3.AbstractFloatingView;
35 import com.android.launcher3.DeviceProfile;
36 import com.android.launcher3.Launcher;
37 import com.android.launcher3.LauncherState;
38 import com.android.launcher3.util.TouchController;
39 import com.android.quickstep.SystemUiProxy;
40 
41 import java.io.PrintWriter;
42 
43 /**
44  * TouchController for handling touch events that get sent to the StatusBar. Once the
45  * Once the event delta mDownY passes the touch slop, the events start getting forwarded.
46  * All events are offset by initial Y value of the pointer.
47  */
48 public class StatusBarTouchController implements TouchController {
49 
50     private static final String TAG = "StatusBarController";
51 
52     private final Launcher mLauncher;
53     private final SystemUiProxy mSystemUiProxy;
54     private final float mTouchSlop;
55     private int mLastAction;
56     private final SparseArray<PointF> mDownEvents;
57 
58     /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
59     private boolean mCanIntercept;
60 
StatusBarTouchController(Launcher l)61     public StatusBarTouchController(Launcher l) {
62         mLauncher = l;
63         mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
64         // Guard against TAPs by increasing the touch slop.
65         mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
66         mDownEvents = new SparseArray<>();
67     }
68 
69     @Override
dump(String prefix, PrintWriter writer)70     public void dump(String prefix, PrintWriter writer) {
71         writer.println(prefix + "mCanIntercept:" + mCanIntercept);
72         writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
73         writer.println(prefix + "mSysUiProxy available:"
74                 + SystemUiProxy.INSTANCE.get(mLauncher).isActive());
75     }
76 
dispatchTouchEvent(MotionEvent ev)77     private void dispatchTouchEvent(MotionEvent ev) {
78         if (mSystemUiProxy.isActive()) {
79             mLastAction = ev.getActionMasked();
80             mSystemUiProxy.onStatusBarTouchEvent(ev);
81         }
82     }
83 
84     @Override
onControllerInterceptTouchEvent(MotionEvent ev)85     public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
86         int action = ev.getActionMasked();
87         int idx = ev.getActionIndex();
88         int pid = ev.getPointerId(idx);
89         if (action == ACTION_DOWN) {
90             mCanIntercept = canInterceptTouch(ev);
91             if (!mCanIntercept) {
92                 return false;
93             }
94             mDownEvents.clear();
95             mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
96         } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
97             // Check!! should only set it only when threshold is not entered.
98             mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
99         }
100         if (!mCanIntercept) {
101             return false;
102         }
103         if (action == ACTION_MOVE && mDownEvents.contains(pid)) {
104             float dy = ev.getY(idx) - mDownEvents.get(pid).y;
105             float dx = ev.getX(idx) - mDownEvents.get(pid).x;
106             // Currently input dispatcher will not do touch transfer if there are more than
107             // one touch pointer. Hence, even if slope passed, only set the slippery flag
108             // when there is single touch event. (context: InputDispatcher.cpp line 1445)
109             if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) {
110                 ev.setAction(ACTION_DOWN);
111                 dispatchTouchEvent(ev);
112                 setWindowSlippery(true);
113                 return true;
114             }
115             if (Math.abs(dx) > mTouchSlop) {
116                 mCanIntercept = false;
117             }
118         }
119         return false;
120     }
121 
122     @Override
onControllerTouchEvent(MotionEvent ev)123     public final boolean onControllerTouchEvent(MotionEvent ev) {
124         int action = ev.getAction();
125         if (action == ACTION_UP || action == ACTION_CANCEL) {
126             dispatchTouchEvent(ev);
127             mLauncher.getStatsLogManager().logger()
128                     .log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
129             setWindowSlippery(false);
130             return true;
131         }
132         return true;
133     }
134 
135     /**
136      * FLAG_SLIPPERY enables touches to slide out of a window into neighboring
137      * windows in mid-gesture instead of being captured for the duration of
138      * the gesture.
139      *
140      * This flag changes the behavior of touch focus for this window only.
141      * Touches can slide out of the window but they cannot necessarily slide
142      * back in (unless the other window with touch focus permits it).
143      */
setWindowSlippery(boolean enable)144     private void setWindowSlippery(boolean enable) {
145         Window w = mLauncher.getWindow();
146         WindowManager.LayoutParams wlp = w.getAttributes();
147         if (enable) {
148             wlp.flags |= FLAG_SLIPPERY;
149         } else {
150             wlp.flags &= ~FLAG_SLIPPERY;
151         }
152         w.setAttributes(wlp);
153     }
154 
canInterceptTouch(MotionEvent ev)155     private boolean canInterceptTouch(MotionEvent ev) {
156         if (isTrackpadScroll(ev) || !mLauncher.isInState(LauncherState.NORMAL)
157                 || AbstractFloatingView.getTopOpenViewWithType(mLauncher,
158                 AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null) {
159             return false;
160         } else {
161             // For NORMAL state, only listen if the event originated above the navbar height
162             DeviceProfile dp = mLauncher.getDeviceProfile();
163             if (ev.getY() > (mLauncher.getDragLayer().getHeight() - dp.getInsets().bottom)) {
164                 return false;
165             }
166         }
167         return SystemUiProxy.INSTANCE.get(mLauncher).isActive();
168     }
169 }