Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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.navigationbar.car; |
| 18 | |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 19 | import android.content.Context; |
| 20 | import android.graphics.PixelFormat; |
| 21 | import android.inputmethodservice.InputMethodService; |
| 22 | import android.os.Handler; |
| 23 | import android.os.IBinder; |
| 24 | import android.os.RemoteException; |
| 25 | import android.os.ServiceManager; |
| 26 | import android.view.Display; |
| 27 | import android.view.Gravity; |
Heemin Seog | 8d6ec6a7 | 2019-10-25 14:34:11 -0700 | [diff] [blame] | 28 | import android.view.View; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 29 | import android.view.ViewGroup; |
| 30 | import android.view.WindowManager; |
| 31 | |
| 32 | import com.android.internal.statusbar.IStatusBarService; |
| 33 | import com.android.internal.statusbar.RegisterStatusBarResult; |
| 34 | import com.android.systemui.R; |
| 35 | import com.android.systemui.SystemUI; |
Dave Mankoff | f473681 | 2019-10-18 17:25:50 -0400 | [diff] [blame] | 36 | import com.android.systemui.dagger.qualifiers.MainHandler; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 37 | import com.android.systemui.shared.system.ActivityManagerWrapper; |
| 38 | import com.android.systemui.statusbar.CommandQueue; |
| 39 | import com.android.systemui.statusbar.NavigationBarController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 40 | import com.android.systemui.statusbar.policy.DeviceProvisionedController; |
| 41 | import com.android.systemui.statusbar.policy.KeyguardStateController; |
| 42 | |
| 43 | import java.io.FileDescriptor; |
| 44 | import java.io.PrintWriter; |
| 45 | |
| 46 | import javax.inject.Inject; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 47 | |
| 48 | import dagger.Lazy; |
| 49 | |
| 50 | /** Navigation bars customized for the automotive use case. */ |
| 51 | public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks { |
| 52 | |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 53 | private final CarNavigationBarController mCarNavigationBarController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 54 | private final WindowManager mWindowManager; |
| 55 | private final DeviceProvisionedController mDeviceProvisionedController; |
Heemin Seog | 4a68948 | 2019-11-04 10:37:43 -0800 | [diff] [blame] | 56 | private final CommandQueue mCommandQueue; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 57 | private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListener; |
| 58 | private final Handler mMainHandler; |
| 59 | private final Lazy<KeyguardStateController> mKeyguardStateController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 60 | private final Lazy<NavigationBarController> mNavigationBarController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 61 | |
| 62 | private IStatusBarService mBarService; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 63 | private ActivityManagerWrapper mActivityManagerWrapper; |
| 64 | |
| 65 | // If the nav bar should be hidden when the soft keyboard is visible. |
| 66 | private boolean mHideNavBarForKeyboard; |
| 67 | private boolean mBottomNavBarVisible; |
| 68 | |
| 69 | // Nav bar views. |
| 70 | private ViewGroup mNavigationBarWindow; |
| 71 | private ViewGroup mLeftNavigationBarWindow; |
| 72 | private ViewGroup mRightNavigationBarWindow; |
| 73 | private CarNavigationBarView mNavigationBarView; |
| 74 | private CarNavigationBarView mLeftNavigationBarView; |
| 75 | private CarNavigationBarView mRightNavigationBarView; |
| 76 | |
| 77 | // To be attached to the navigation bars such that they can close the notification panel if |
| 78 | // it's open. |
| 79 | private boolean mDeviceIsSetUpForUser = true; |
| 80 | |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 81 | @Inject |
| 82 | public CarNavigationBar(Context context, |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 83 | CarNavigationBarController carNavigationBarController, |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 84 | WindowManager windowManager, |
| 85 | DeviceProvisionedController deviceProvisionedController, |
Heemin Seog | 4a68948 | 2019-11-04 10:37:43 -0800 | [diff] [blame] | 86 | CommandQueue commandQueue, |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 87 | Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener, |
Dave Mankoff | f473681 | 2019-10-18 17:25:50 -0400 | [diff] [blame] | 88 | @MainHandler Handler mainHandler, |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 89 | Lazy<KeyguardStateController> keyguardStateController, |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 90 | Lazy<NavigationBarController> navigationBarController) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 91 | super(context); |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 92 | mCarNavigationBarController = carNavigationBarController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 93 | mWindowManager = windowManager; |
| 94 | mDeviceProvisionedController = deviceProvisionedController; |
Heemin Seog | 4a68948 | 2019-11-04 10:37:43 -0800 | [diff] [blame] | 95 | mCommandQueue = commandQueue; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 96 | mFacetButtonTaskStackListener = facetButtonTaskStackListener; |
| 97 | mMainHandler = mainHandler; |
| 98 | mKeyguardStateController = keyguardStateController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 99 | mNavigationBarController = navigationBarController; |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | @Override |
| 103 | public void start() { |
| 104 | // Set initial state. |
| 105 | mHideNavBarForKeyboard = mContext.getResources().getBoolean( |
| 106 | com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); |
| 107 | mBottomNavBarVisible = false; |
| 108 | |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 109 | // Get bar service. |
| 110 | mBarService = IStatusBarService.Stub.asInterface( |
| 111 | ServiceManager.getService(Context.STATUS_BAR_SERVICE)); |
| 112 | |
| 113 | // Connect into the status bar manager service |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 114 | mCommandQueue.addCallback(this); |
| 115 | |
| 116 | RegisterStatusBarResult result = null; |
| 117 | try { |
| 118 | result = mBarService.registerStatusBar(mCommandQueue); |
| 119 | } catch (RemoteException ex) { |
| 120 | ex.rethrowFromSystemServer(); |
| 121 | } |
| 122 | |
| 123 | mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup(); |
| 124 | mDeviceProvisionedController.addCallback( |
| 125 | new DeviceProvisionedController.DeviceProvisionedListener() { |
| 126 | @Override |
| 127 | public void onUserSetupChanged() { |
| 128 | mMainHandler.post(() -> restartNavBarsIfNecessary()); |
| 129 | } |
| 130 | |
| 131 | @Override |
| 132 | public void onUserSwitched() { |
| 133 | mMainHandler.post(() -> restartNavBarsIfNecessary()); |
| 134 | } |
| 135 | }); |
| 136 | |
| 137 | createNavigationBar(result); |
| 138 | |
| 139 | mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); |
| 140 | mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListener.get()); |
| 141 | |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 142 | mCarNavigationBarController.connectToHvac(); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | private void restartNavBarsIfNecessary() { |
| 146 | boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); |
| 147 | if (mDeviceIsSetUpForUser != currentUserSetup) { |
| 148 | mDeviceIsSetUpForUser = currentUserSetup; |
| 149 | restartNavBars(); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Remove all content from navbars and rebuild them. Used to allow for different nav bars |
| 155 | * before and after the device is provisioned. . Also for change of density and font size. |
| 156 | */ |
| 157 | private void restartNavBars() { |
| 158 | // remove and reattach all hvac components such that we don't keep a reference to unused |
| 159 | // ui elements |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 160 | mCarNavigationBarController.removeAllFromHvac(); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 161 | |
| 162 | if (mNavigationBarWindow != null) { |
| 163 | mNavigationBarWindow.removeAllViews(); |
| 164 | mNavigationBarView = null; |
| 165 | } |
| 166 | |
| 167 | if (mLeftNavigationBarWindow != null) { |
| 168 | mLeftNavigationBarWindow.removeAllViews(); |
| 169 | mLeftNavigationBarView = null; |
| 170 | } |
| 171 | |
| 172 | if (mRightNavigationBarWindow != null) { |
| 173 | mRightNavigationBarWindow.removeAllViews(); |
| 174 | mRightNavigationBarView = null; |
| 175 | } |
| 176 | |
| 177 | buildNavBarContent(); |
| 178 | // If the UI was rebuilt (day/night change) while the keyguard was up we need to |
| 179 | // correctly respect that state. |
| 180 | if (mKeyguardStateController.get().isShowing()) { |
| 181 | updateNavBarForKeyguardContent(); |
| 182 | } |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | private void createNavigationBar(RegisterStatusBarResult result) { |
| 186 | buildNavBarWindows(); |
| 187 | buildNavBarContent(); |
| 188 | attachNavBarWindows(); |
| 189 | |
| 190 | // Try setting up the initial state of the nav bar if applicable. |
| 191 | if (result != null) { |
| 192 | setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken, |
| 193 | result.mImeWindowVis, result.mImeBackDisposition, |
| 194 | result.mShowImeSwitcher); |
| 195 | } |
| 196 | |
| 197 | // There has been a car customized nav bar on the default display, so just create nav bars |
| 198 | // on external displays. |
| 199 | mNavigationBarController.get().createNavigationBars(/* includeDefaultDisplay= */ false, |
| 200 | result); |
| 201 | } |
| 202 | |
| 203 | private void buildNavBarWindows() { |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 204 | mNavigationBarWindow = mCarNavigationBarController.getBottomWindow(); |
| 205 | mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow(); |
| 206 | mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow(); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | private void buildNavBarContent() { |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 210 | mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser); |
| 211 | if (mNavigationBarView != null) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 212 | mNavigationBarWindow.addView(mNavigationBarView); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 213 | } |
| 214 | |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 215 | mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser); |
| 216 | if (mLeftNavigationBarView != null) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 217 | mLeftNavigationBarWindow.addView(mLeftNavigationBarView); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 218 | } |
| 219 | |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 220 | mRightNavigationBarView = mCarNavigationBarController.getRightBar(mDeviceIsSetUpForUser); |
| 221 | if (mRightNavigationBarView != null) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 222 | mRightNavigationBarWindow.addView(mRightNavigationBarView); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 223 | } |
| 224 | } |
| 225 | |
| 226 | private void attachNavBarWindows() { |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 227 | if (mNavigationBarWindow != null && !mBottomNavBarVisible) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 228 | mBottomNavBarVisible = true; |
| 229 | |
| 230 | WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| 231 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, |
| 232 | WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, |
| 233 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| 234 | | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| 235 | | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
| 236 | | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, |
| 237 | PixelFormat.TRANSLUCENT); |
| 238 | lp.setTitle("CarNavigationBar"); |
| 239 | lp.windowAnimations = 0; |
| 240 | mWindowManager.addView(mNavigationBarWindow, lp); |
| 241 | } |
| 242 | |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 243 | if (mLeftNavigationBarWindow != null) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 244 | int width = mContext.getResources().getDimensionPixelSize( |
| 245 | R.dimen.car_left_navigation_bar_width); |
| 246 | WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( |
| 247 | width, ViewGroup.LayoutParams.MATCH_PARENT, |
| 248 | WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, |
| 249 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| 250 | | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| 251 | | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
| 252 | | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, |
| 253 | PixelFormat.TRANSLUCENT); |
| 254 | leftlp.setTitle("LeftCarNavigationBar"); |
| 255 | leftlp.windowAnimations = 0; |
| 256 | leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; |
| 257 | leftlp.gravity = Gravity.LEFT; |
| 258 | mWindowManager.addView(mLeftNavigationBarWindow, leftlp); |
| 259 | } |
Heemin Seog | fcccab7 | 2019-10-22 08:30:23 -0700 | [diff] [blame] | 260 | if (mRightNavigationBarWindow != null) { |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 261 | int width = mContext.getResources().getDimensionPixelSize( |
| 262 | R.dimen.car_right_navigation_bar_width); |
| 263 | WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( |
| 264 | width, ViewGroup.LayoutParams.MATCH_PARENT, |
| 265 | WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, |
| 266 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| 267 | | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| 268 | | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
| 269 | | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, |
| 270 | PixelFormat.TRANSLUCENT); |
| 271 | rightlp.setTitle("RightCarNavigationBar"); |
| 272 | rightlp.windowAnimations = 0; |
| 273 | rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; |
| 274 | rightlp.gravity = Gravity.RIGHT; |
| 275 | mWindowManager.addView(mRightNavigationBarWindow, rightlp); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * We register for soft keyboard visibility events such that we can hide the navigation bar |
| 281 | * giving more screen space to the IME. Note: this is optional and controlled by |
| 282 | * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}. |
| 283 | */ |
| 284 | @Override |
| 285 | public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, |
| 286 | boolean showImeSwitcher) { |
| 287 | if (!mHideNavBarForKeyboard) { |
| 288 | return; |
| 289 | } |
| 290 | |
| 291 | if (mContext.getDisplay().getDisplayId() != displayId) { |
| 292 | return; |
| 293 | } |
| 294 | |
| 295 | boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; |
Heemin Seog | 8d6ec6a7 | 2019-10-25 14:34:11 -0700 | [diff] [blame] | 296 | mCarNavigationBarController.setBottomWindowVisibility( |
| 297 | isKeyboardVisible ? View.GONE : View.VISIBLE); |
Heemin Seog | 1a39dea | 2019-10-16 15:58:21 -0700 | [diff] [blame] | 298 | } |
| 299 | |
| 300 | private void updateNavBarForKeyguardContent() { |
| 301 | if (mNavigationBarView != null) { |
| 302 | mNavigationBarView.showKeyguardButtons(); |
| 303 | } |
| 304 | if (mLeftNavigationBarView != null) { |
| 305 | mLeftNavigationBarView.showKeyguardButtons(); |
| 306 | } |
| 307 | if (mRightNavigationBarView != null) { |
| 308 | mRightNavigationBarView.showKeyguardButtons(); |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | @Override |
| 313 | public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| 314 | pw.print(" mTaskStackListener="); |
| 315 | pw.println(mFacetButtonTaskStackListener.get()); |
| 316 | pw.print(" mNavigationBarView="); |
| 317 | pw.println(mNavigationBarView); |
| 318 | } |
| 319 | } |