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