• 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.systemui.statusbar.phone;
18 
19 import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
20 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
21 
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.os.Handler;
25 import android.os.RemoteException;
26 import android.util.Log;
27 import android.view.IWindowManager;
28 import android.view.MotionEvent;
29 import android.view.View;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.systemui.Dependency;
33 import com.android.systemui.SysUiServiceProvider;
34 import com.android.systemui.statusbar.CommandQueue;
35 import com.android.systemui.statusbar.NotificationRemoteInputManager;
36 
37 import javax.inject.Inject;
38 import javax.inject.Named;
39 
40 /** A controller to control all auto-hide things. */
41 public class AutoHideController implements CommandQueue.Callbacks {
42     private static final String TAG = "AutoHideController";
43 
44     private final IWindowManager mWindowManagerService;
45 
46     private final Handler mHandler;
47     private final NotificationRemoteInputManager mRemoteInputManager;
48     private final CommandQueue mCommandQueue;
49     private StatusBar mStatusBar;
50     private NavigationBarFragment mNavigationBar;
51 
52     @VisibleForTesting
53     int mDisplayId;
54     @VisibleForTesting
55     int mSystemUiVisibility;
56     // last value sent to window manager
57     private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
58 
59     private boolean mAutoHideSuspended;
60 
61     private static final long AUTOHIDE_TIMEOUT_MS = 2250;
62 
63     private final Runnable mAutoHide = () -> {
64         int requested = mSystemUiVisibility & ~getTransientMask();
65         if (mSystemUiVisibility != requested) {
66             notifySystemUiVisibilityChanged(requested);
67         }
68     };
69 
70     @Inject
AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler)71     public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
72         mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
73         mCommandQueue.addCallback(this);
74         mHandler = handler;
75         mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
76         mWindowManagerService = Dependency.get(IWindowManager.class);
77 
78         mDisplayId = context.getDisplayId();
79     }
80 
81     @Override
onDisplayRemoved(int displayId)82     public void onDisplayRemoved(int displayId) {
83         if (displayId == mDisplayId) {
84             mCommandQueue.removeCallback(this);
85         }
86     }
87 
setStatusBar(StatusBar statusBar)88     void setStatusBar(StatusBar statusBar) {
89         mStatusBar = statusBar;
90     }
91 
setNavigationBar(NavigationBarFragment navigationBar)92     void setNavigationBar(NavigationBarFragment navigationBar) {
93         mNavigationBar = navigationBar;
94     }
95 
96     @Override
setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean navbarColorManagedByIme)97     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
98             int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
99             boolean navbarColorManagedByIme) {
100         if (displayId != mDisplayId) {
101             return;
102         }
103         int oldVal = mSystemUiVisibility;
104         int newVal = (oldVal & ~mask) | (vis & mask);
105         int diff = newVal ^ oldVal;
106 
107         if (diff != 0) {
108             mSystemUiVisibility = newVal;
109 
110             // ready to unhide
111             if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
112                 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
113             }
114 
115             if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
116                 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
117             }
118 
119             // Re-send setSystemUiVisibility to update un-hide status.
120             if (mSystemUiVisibility != newVal) {
121                 mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
122                         fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
123                         dockedStackBounds, navbarColorManagedByIme);
124             }
125 
126             notifySystemUiVisibilityChanged(mSystemUiVisibility);
127         }
128     }
129 
130     @VisibleForTesting
notifySystemUiVisibilityChanged(int vis)131     void notifySystemUiVisibilityChanged(int vis) {
132         try {
133             if (mLastDispatchedSystemUiVisibility != vis) {
134                 mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
135                 mLastDispatchedSystemUiVisibility = vis;
136             }
137         } catch (RemoteException ex) {
138             Log.w(TAG, "Cannot get WindowManager");
139         }
140     }
141 
resumeSuspendedAutoHide()142     void resumeSuspendedAutoHide() {
143         if (mAutoHideSuspended) {
144             scheduleAutoHide();
145             Runnable checkBarModesRunnable = getCheckBarModesRunnable();
146             if (checkBarModesRunnable != null) {
147                 mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
148             }
149         }
150     }
151 
suspendAutoHide()152     void suspendAutoHide() {
153         mHandler.removeCallbacks(mAutoHide);
154         Runnable checkBarModesRunnable = getCheckBarModesRunnable();
155         if (checkBarModesRunnable != null) {
156             mHandler.removeCallbacks(checkBarModesRunnable);
157         }
158         mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
159     }
160 
touchAutoHide()161     void touchAutoHide() {
162         // update transient bar auto hide
163         if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
164                 || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
165             scheduleAutoHide();
166         } else {
167             cancelAutoHide();
168         }
169     }
170 
getCheckBarModesRunnable()171     private Runnable getCheckBarModesRunnable() {
172         if (hasStatusBar()) {
173             return () -> mStatusBar.checkBarModes();
174         } else if (hasNavigationBar()) {
175             return () -> mNavigationBar.checkNavBarModes();
176         } else {
177             return null;
178         }
179     }
180 
cancelAutoHide()181     private void cancelAutoHide() {
182         mAutoHideSuspended = false;
183         mHandler.removeCallbacks(mAutoHide);
184     }
185 
scheduleAutoHide()186     private void scheduleAutoHide() {
187         cancelAutoHide();
188         mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
189     }
190 
checkUserAutoHide(MotionEvent event)191     void checkUserAutoHide(MotionEvent event) {
192         boolean shouldAutoHide =
193                 (mSystemUiVisibility & getTransientMask()) != 0  // a transient bar is revealed.
194                 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
195                 && event.getX() == 0 && event.getY() == 0;
196         if (hasStatusBar()) {
197             // a touch outside both bars
198             shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
199         }
200         if (shouldAutoHide) {
201             userAutoHide();
202         }
203     }
204 
userAutoHide()205     private void userAutoHide() {
206         cancelAutoHide();
207         mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
208     }
209 
getTransientMask()210     private int getTransientMask() {
211         int mask = 0;
212         if (hasStatusBar()) {
213             mask |= View.STATUS_BAR_TRANSIENT;
214         }
215         if (hasNavigationBar()) {
216             mask |= View.NAVIGATION_BAR_TRANSIENT;
217         }
218         return mask;
219     }
220 
hasNavigationBar()221     boolean hasNavigationBar() {
222         return mNavigationBar != null;
223     }
224 
225     @VisibleForTesting
hasStatusBar()226     boolean hasStatusBar() {
227         return mStatusBar != null;
228     }
229 }
230