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 android.content.Context;
20 import android.os.Handler;
21 import android.os.RemoteException;
22 import android.util.Log;
23 import android.view.IWindowManager;
24 import android.view.MotionEvent;
25 
26 import com.android.systemui.dagger.qualifiers.Main;
27 import com.android.systemui.statusbar.AutoHideUiElement;
28 
29 import javax.inject.Inject;
30 
31 /** A controller to control all auto-hide things. Also see {@link AutoHideUiElement}. */
32 public class AutoHideController {
33     private static final String TAG = "AutoHideController";
34     private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
35 
36     private final IWindowManager mWindowManagerService;
37     private final Handler mHandler;
38 
39     private AutoHideUiElement mStatusBar;
40     /** For tablets, this will represent the Taskbar */
41     private AutoHideUiElement mNavigationBar;
42     private int mDisplayId;
43 
44     private boolean mAutoHideSuspended;
45 
46     private final Runnable mAutoHide = () -> {
47         if (isAnyTransientBarShown()) {
48             hideTransientBars();
49         }
50     };
51 
52     @Inject
AutoHideController(Context context, @Main Handler handler, IWindowManager iWindowManager)53     public AutoHideController(Context context, @Main Handler handler,
54             IWindowManager iWindowManager) {
55         mHandler = handler;
56         mWindowManagerService = iWindowManager;
57 
58         mDisplayId = context.getDisplayId();
59     }
60 
61     /**
62      * Sets a {@link AutoHideUiElement} status bar that should be controlled by the
63      * {@link AutoHideController}.
64      */
setStatusBar(AutoHideUiElement element)65     public void setStatusBar(AutoHideUiElement element) {
66         mStatusBar = element;
67     }
68 
69     /**
70      * Sets a {@link AutoHideUiElement} navigation bar that should be controlled by the
71      * {@link AutoHideController}.
72      */
setNavigationBar(AutoHideUiElement element)73     public void setNavigationBar(AutoHideUiElement element) {
74         mNavigationBar = element;
75     }
76 
hideTransientBars()77     private void hideTransientBars() {
78         try {
79             mWindowManagerService.hideTransientBars(mDisplayId);
80         } catch (RemoteException ex) {
81             Log.w(TAG, "Cannot get WindowManager");
82         }
83 
84         if (mStatusBar != null) {
85             mStatusBar.hide();
86         }
87 
88         if (mNavigationBar != null) {
89             mNavigationBar.hide();
90         }
91     }
92 
resumeSuspendedAutoHide()93     public void resumeSuspendedAutoHide() {
94         if (mAutoHideSuspended) {
95             scheduleAutoHide();
96             Runnable checkBarModesRunnable = getCheckBarModesRunnable();
97             if (checkBarModesRunnable != null) {
98                 mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
99             }
100         }
101     }
102 
suspendAutoHide()103     public void suspendAutoHide() {
104         mHandler.removeCallbacks(mAutoHide);
105         Runnable checkBarModesRunnable = getCheckBarModesRunnable();
106         if (checkBarModesRunnable != null) {
107             mHandler.removeCallbacks(checkBarModesRunnable);
108         }
109         mAutoHideSuspended = isAnyTransientBarShown();
110     }
111 
112     /** Schedules or cancels auto hide behavior based on current system bar state. */
touchAutoHide()113     public void touchAutoHide() {
114         // update transient bar auto hide
115         if (isAnyTransientBarShown()) {
116             scheduleAutoHide();
117         } else {
118             cancelAutoHide();
119         }
120     }
121 
getCheckBarModesRunnable()122     private Runnable getCheckBarModesRunnable() {
123         if (mStatusBar != null) {
124             return () -> mStatusBar.synchronizeState();
125         } else if (mNavigationBar != null) {
126             return () -> mNavigationBar.synchronizeState();
127         } else {
128             return null;
129         }
130     }
131 
cancelAutoHide()132     private void cancelAutoHide() {
133         mAutoHideSuspended = false;
134         mHandler.removeCallbacks(mAutoHide);
135     }
136 
scheduleAutoHide()137     private void scheduleAutoHide() {
138         cancelAutoHide();
139         mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS);
140     }
141 
checkUserAutoHide(MotionEvent event)142     public void checkUserAutoHide(MotionEvent event) {
143         boolean shouldHide = isAnyTransientBarShown()
144                 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
145                 && event.getX() == 0 && event.getY() == 0;
146 
147         if (mStatusBar != null) {
148             shouldHide &= mStatusBar.shouldHideOnTouch();
149         }
150         if (mNavigationBar != null) {
151             shouldHide &= mNavigationBar.shouldHideOnTouch();
152         }
153 
154         if (shouldHide) {
155             userAutoHide();
156         }
157     }
158 
userAutoHide()159     private void userAutoHide() {
160         cancelAutoHide();
161         mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
162     }
163 
isAnyTransientBarShown()164     private boolean isAnyTransientBarShown() {
165         if (mStatusBar != null && mStatusBar.isVisible()) {
166             return true;
167         }
168 
169         if (mNavigationBar != null && mNavigationBar.isVisible()) {
170             return true;
171         }
172 
173         return false;
174     }
175 
176     /**
177      * Injectable factory for creating a {@link AutoHideController}.
178      */
179     public static class Factory {
180         private final Handler mHandler;
181         private final IWindowManager mIWindowManager;
182 
183         @Inject
Factory(@ain Handler handler, IWindowManager iWindowManager)184         public Factory(@Main Handler handler, IWindowManager iWindowManager) {
185             mHandler = handler;
186             mIWindowManager = iWindowManager;
187         }
188 
189         /** Create an {@link AutoHideController} */
create(Context context)190         public AutoHideController create(Context context) {
191             return new AutoHideController(context, mHandler, mIWindowManager);
192         }
193     }
194 }
195