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; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 21 import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; 22 import static com.android.systemui.SysUiServiceProvider.getComponent; 23 24 import android.content.Context; 25 import android.hardware.display.DisplayManager; 26 import android.os.Handler; 27 import android.os.RemoteException; 28 import android.util.Log; 29 import android.util.SparseArray; 30 import android.view.Display; 31 import android.view.IWindowManager; 32 import android.view.View; 33 import android.view.WindowManagerGlobal; 34 35 import androidx.annotation.Nullable; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.statusbar.RegisterStatusBarResult; 39 import com.android.systemui.Dependency; 40 import com.android.systemui.plugins.DarkIconDispatcher; 41 import com.android.systemui.statusbar.CommandQueue.Callbacks; 42 import com.android.systemui.statusbar.phone.AutoHideController; 43 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; 44 import com.android.systemui.statusbar.phone.LightBarController; 45 import com.android.systemui.statusbar.phone.NavigationBarFragment; 46 import com.android.systemui.statusbar.phone.NavigationBarView; 47 import com.android.systemui.statusbar.policy.BatteryController; 48 49 import javax.inject.Inject; 50 import javax.inject.Named; 51 import javax.inject.Singleton; 52 53 54 /** A controller to handle navigation bars. */ 55 @Singleton 56 public class NavigationBarController implements Callbacks { 57 58 private static final String TAG = NavigationBarController.class.getSimpleName(); 59 60 private final Context mContext; 61 private final Handler mHandler; 62 private final DisplayManager mDisplayManager; 63 64 /** A displayId - nav bar maps. */ 65 @VisibleForTesting 66 SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); 67 68 @Inject NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler)69 public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { 70 mContext = context; 71 mHandler = handler; 72 mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); 73 CommandQueue commandQueue = getComponent(mContext, CommandQueue.class); 74 if (commandQueue != null) { 75 commandQueue.addCallback(this); 76 } 77 } 78 79 @Override onDisplayRemoved(int displayId)80 public void onDisplayRemoved(int displayId) { 81 removeNavigationBar(displayId); 82 } 83 84 @Override onDisplayReady(int displayId)85 public void onDisplayReady(int displayId) { 86 Display display = mDisplayManager.getDisplay(displayId); 87 createNavigationBar(display, null); 88 } 89 90 // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to 91 // CarStatusBar because they have their own nav bar. Think about a better way for it. 92 /** 93 * Creates navigation bars when car/status bar initializes. 94 * 95 * @param includeDefaultDisplay {@code true} to create navigation bar on default display. 96 */ createNavigationBars(final boolean includeDefaultDisplay, RegisterStatusBarResult result)97 public void createNavigationBars(final boolean includeDefaultDisplay, 98 RegisterStatusBarResult result) { 99 Display[] displays = mDisplayManager.getDisplays(); 100 for (Display display : displays) { 101 if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) { 102 createNavigationBar(display, result); 103 } 104 } 105 } 106 107 /** 108 * Adds a navigation bar on default display or an external display if the display supports 109 * system decorations. 110 * 111 * @param display the display to add navigation bar on. 112 */ 113 @VisibleForTesting createNavigationBar(Display display, RegisterStatusBarResult result)114 void createNavigationBar(Display display, RegisterStatusBarResult result) { 115 if (display == null) { 116 return; 117 } 118 119 final int displayId = display.getDisplayId(); 120 final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY; 121 final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); 122 123 try { 124 if (!wms.hasNavigationBar(displayId)) { 125 return; 126 } 127 } catch (RemoteException e) { 128 // Cannot get wms, just return with warning message. 129 Log.w(TAG, "Cannot get WindowManager."); 130 return; 131 } 132 final Context context = isOnDefaultDisplay 133 ? mContext 134 : mContext.createDisplayContext(display); 135 NavigationBarFragment.create(context, (tag, fragment) -> { 136 NavigationBarFragment navBar = (NavigationBarFragment) fragment; 137 138 // Unfortunately, we still need it because status bar needs LightBarController 139 // before notifications creation. We cannot directly use getLightBarController() 140 // from NavigationBarFragment directly. 141 LightBarController lightBarController = isOnDefaultDisplay 142 ? Dependency.get(LightBarController.class) 143 : new LightBarController(context, 144 Dependency.get(DarkIconDispatcher.class), 145 Dependency.get(BatteryController.class)); 146 navBar.setLightBarController(lightBarController); 147 148 // TODO(b/118592525): to support multi-display, we start to add something which is 149 // per-display, while others may be global. I think it's time to add 150 // a new class maybe named DisplayDependency to solve per-display 151 // Dependency problem. 152 AutoHideController autoHideController = isOnDefaultDisplay 153 ? Dependency.get(AutoHideController.class) 154 : new AutoHideController(context, mHandler); 155 navBar.setAutoHideController(autoHideController); 156 navBar.restoreSystemUiVisibilityState(); 157 mNavigationBars.append(displayId, navBar); 158 159 if (result != null) { 160 navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken, 161 result.mImeWindowVis, result.mImeBackDisposition, 162 result.mShowImeSwitcher); 163 } 164 }); 165 } 166 removeNavigationBar(int displayId)167 private void removeNavigationBar(int displayId) { 168 NavigationBarFragment navBar = mNavigationBars.get(displayId); 169 if (navBar != null) { 170 View navigationWindow = navBar.getView().getRootView(); 171 WindowManagerGlobal.getInstance() 172 .removeView(navigationWindow, true /* immediate */); 173 mNavigationBars.remove(displayId); 174 } 175 } 176 177 /** @see NavigationBarFragment#checkNavBarModes() */ checkNavBarModes(int displayId)178 public void checkNavBarModes(int displayId) { 179 NavigationBarFragment navBar = mNavigationBars.get(displayId); 180 if (navBar != null) { 181 navBar.checkNavBarModes(); 182 } 183 } 184 185 /** @see NavigationBarFragment#finishBarAnimations() */ finishBarAnimations(int displayId)186 public void finishBarAnimations(int displayId) { 187 NavigationBarFragment navBar = mNavigationBars.get(displayId); 188 if (navBar != null) { 189 navBar.finishBarAnimations(); 190 } 191 } 192 193 /** @see NavigationBarFragment#touchAutoDim() */ touchAutoDim(int displayId)194 public void touchAutoDim(int displayId) { 195 NavigationBarFragment navBar = mNavigationBars.get(displayId); 196 if (navBar != null) { 197 navBar.touchAutoDim(); 198 } 199 } 200 201 /** @see NavigationBarFragment#transitionTo(int, boolean) */ transitionTo(int displayId, @TransitionMode int barMode, boolean animate)202 public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) { 203 NavigationBarFragment navBar = mNavigationBars.get(displayId); 204 if (navBar != null) { 205 navBar.transitionTo(barMode, animate); 206 } 207 } 208 209 /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */ disableAnimationsDuringHide(int displayId, long delay)210 public void disableAnimationsDuringHide(int displayId, long delay) { 211 NavigationBarFragment navBar = mNavigationBars.get(displayId); 212 if (navBar != null) { 213 navBar.disableAnimationsDuringHide(delay); 214 } 215 } 216 217 /** @return {@link NavigationBarView} on the default display. */ getDefaultNavigationBarView()218 public @Nullable NavigationBarView getDefaultNavigationBarView() { 219 return getNavigationBarView(DEFAULT_DISPLAY); 220 } 221 222 /** 223 * @param displayId the ID of display which Navigation bar is on 224 * @return {@link NavigationBarView} on the display with {@code displayId}. 225 * {@code null} if no navigation bar on that display. 226 */ getNavigationBarView(int displayId)227 public @Nullable NavigationBarView getNavigationBarView(int displayId) { 228 NavigationBarFragment navBar = mNavigationBars.get(displayId); 229 return (navBar == null) ? null : (NavigationBarView) navBar.getView(); 230 } 231 232 /** @return {@link NavigationBarFragment} on the default display. */ getDefaultNavigationBarFragment()233 public NavigationBarFragment getDefaultNavigationBarFragment() { 234 return mNavigationBars.get(DEFAULT_DISPLAY); 235 } 236 } 237