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