1 /* 2 * Copyright (C) 2024 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.car.systembar; 18 19 import static android.content.Intent.ACTION_OVERLAY_CHANGED; 20 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 21 22 import static com.android.systemui.car.Flags.configAwareSystemui; 23 import static com.android.systemui.car.systembar.CarSystemBarViewController.BUTTON_TYPE_KEYGUARD; 24 import static com.android.systemui.car.systembar.CarSystemBarViewController.BUTTON_TYPE_NAVIGATION; 25 import static com.android.systemui.car.systembar.CarSystemBarViewController.BUTTON_TYPE_OCCLUSION; 26 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 27 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 28 29 import android.app.ActivityManager; 30 import android.app.ActivityManager.RunningTaskInfo; 31 import android.app.StatusBarManager.Disable2Flags; 32 import android.app.StatusBarManager.DisableFlags; 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.res.Configuration; 38 import android.graphics.Rect; 39 import android.inputmethodservice.InputMethodService; 40 import android.os.Build; 41 import android.os.Bundle; 42 import android.os.Handler; 43 import android.os.PatternMatcher; 44 import android.os.RemoteException; 45 import android.util.ArraySet; 46 import android.util.Log; 47 import android.util.SparseArray; 48 import android.util.SparseBooleanArray; 49 import android.view.View; 50 import android.view.ViewGroup; 51 import android.view.WindowInsets; 52 import android.view.WindowInsets.Type.InsetsType; 53 import android.view.WindowInsetsController; 54 import android.view.WindowManager; 55 import android.widget.Toast; 56 57 import androidx.annotation.Nullable; 58 import androidx.annotation.VisibleForTesting; 59 60 import com.android.internal.statusbar.IStatusBarService; 61 import com.android.internal.statusbar.LetterboxDetails; 62 import com.android.internal.statusbar.RegisterStatusBarResult; 63 import com.android.internal.view.AppearanceRegion; 64 import com.android.systemui.car.CarDeviceProvisionedController; 65 import com.android.systemui.car.CarDeviceProvisionedListener; 66 import com.android.systemui.car.displaycompat.ToolbarController; 67 import com.android.systemui.car.hvac.HvacPanelOverlayViewController; 68 import com.android.systemui.car.keyguard.KeyguardSystemBarPresenter; 69 import com.android.systemui.car.notification.NotificationPanelViewController; 70 import com.android.systemui.dagger.SysUISingleton; 71 import com.android.systemui.dagger.qualifiers.Main; 72 import com.android.systemui.plugins.DarkIconDispatcher; 73 import com.android.systemui.settings.DisplayTracker; 74 import com.android.systemui.settings.UserTracker; 75 import com.android.systemui.shared.statusbar.phone.BarTransitions; 76 import com.android.systemui.shared.system.TaskStackChangeListener; 77 import com.android.systemui.shared.system.TaskStackChangeListeners; 78 import com.android.systemui.statusbar.AutoHideUiElement; 79 import com.android.systemui.statusbar.CommandQueue; 80 import com.android.systemui.statusbar.phone.AutoHideController; 81 import com.android.systemui.statusbar.phone.LightBarController; 82 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; 83 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher; 84 import com.android.systemui.statusbar.policy.ConfigurationController; 85 import com.android.systemui.statusbar.policy.KeyguardStateController; 86 import com.android.systemui.util.concurrency.DelayableExecutor; 87 88 import dagger.Lazy; 89 90 import java.util.ArrayList; 91 import java.util.HashMap; 92 import java.util.Locale; 93 import java.util.Map; 94 import java.util.Set; 95 96 /** A single class which controls the system bar views. */ 97 @SysUISingleton 98 public class CarSystemBarControllerImpl implements CarSystemBarController, 99 CommandQueue.Callbacks, ConfigurationController.ConfigurationListener, 100 KeyguardSystemBarPresenter { 101 private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG; 102 103 private static final String TAG = CarSystemBarController.class.getSimpleName(); 104 105 private static final String OVERLAY_FILTER_DATA_SCHEME = "package"; 106 107 private static final int MAX_RETRIES_FOR_WINDOW_CONTEXT_UPDATE_CHECK = 3; 108 109 private static final long RETRY_DELAY_FOR_WINDOW_CONTEXT_UPDATE_CHECK = 500; 110 111 private final Context mContext; 112 private final CarSystemBarViewFactory mCarSystemBarViewFactory; 113 private final SystemBarConfigs mSystemBarConfigs; 114 private final SysuiDarkIconDispatcher mStatusBarIconController; 115 private final CarDeviceProvisionedController mCarDeviceProvisionedController; 116 private final CommandQueue mCommandQueue; 117 private final AutoHideController mAutoHideController; 118 private final ButtonSelectionStateListener mButtonSelectionStateListener; 119 private final DelayableExecutor mExecutor; 120 private final IStatusBarService mBarService; 121 private final DisplayTracker mDisplayTracker; 122 private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy; 123 private final Lazy<PhoneStatusBarPolicy> mIconPolicyLazy; 124 private final ConfigurationController mConfigurationController; 125 private final CarSystemBarRestartTracker mCarSystemBarRestartTracker; 126 private final int mDisplayId; 127 @Nullable 128 private final ToolbarController mDisplayCompatToolbarController; 129 130 protected final UserTracker mUserTracker; 131 132 private HvacPanelOverlayViewController mHvacPanelOverlayViewController; 133 private NotificationPanelViewController mNotificationPanelViewController; 134 135 // Saved StatusBarManager.DisableFlags 136 private int mStatusBarState; 137 // Saved StatusBarManager.Disable2Flags 138 private int mStatusBarState2; 139 private int mLockTaskMode; 140 141 // If the nav bar should be hidden when the soft keyboard is visible. 142 // contains: Map<@SystemBarSide Integer, Boolean> 143 private final SparseBooleanArray mHideBarForKeyboardMap = new SparseBooleanArray(); 144 // System bar windows. 145 // contains: Map<@SystemBarSide Integer, ViewGroup> 146 private final SparseArray<ViewGroup> mSystemBarWindowMap = new SparseArray<>(); 147 // System bar views. 148 // contains: Map<@SystemBarSide Integer, CarSystemBarViewController> 149 private final SparseArray<CarSystemBarViewController> mSystemBarViewControllerMap = 150 new SparseArray<>(); 151 // If the system bar is attached to the window or not. 152 // contains: Map<@SystemBarSide Integer, Boolean> 153 private final SparseBooleanArray mSystemBarAttachedMap = new SparseBooleanArray(); 154 // If the system bar is enabled or not. 155 // contains: Map<@SystemBarSide Integer, Boolean> 156 private final SparseBooleanArray mSystemBarEnabledMap = new SparseBooleanArray(); 157 // Set of View.OnTouchListener on each system bar. 158 // contains: Map<@SystemBarSide Integer, Set<View.OnTouchListener>> 159 private final SparseArray<Set<View.OnTouchListener>> mBarTouchListenersMap = new SparseArray(); 160 161 // To be attached to the navigation bars such that they can close the notification panel if 162 // it's open. 163 private boolean mDeviceIsSetUpForUser = true; 164 private boolean mIsUserSetupInProgress = false; 165 private int mWindowContextUpdateCheckRetryCount = 0; 166 167 private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0]; 168 @BarTransitions.TransitionMode 169 private int mStatusBarMode; 170 @BarTransitions.TransitionMode 171 private int mSystemBarMode; 172 private boolean mStatusBarTransientShown; 173 private boolean mNavBarTransientShown; 174 private Handler mHandler; 175 176 private boolean mIsUiModeNight = false; 177 178 private Locale mCurrentLocale; 179 180 private final Runnable mWindowContextUpdateCheckRunnable = new Runnable() { 181 @Override 182 public void run() { 183 if (checkSystemBarWindowContextsAreUpdated()) { 184 // cache the current state 185 Map<Integer, Bundle> cachedSystemBarCurrentState = cacheSystemBarCurrentState(); 186 187 resetSystemBarContent(/* isProvisionedStateChange= */ false); 188 189 // retrieve the previous state 190 restoreSystemBarSavedState(cachedSystemBarCurrentState); 191 mWindowContextUpdateCheckRetryCount = 0; 192 } else if (mWindowContextUpdateCheckRetryCount 193 == MAX_RETRIES_FOR_WINDOW_CONTEXT_UPDATE_CHECK) { 194 resetSystemBarContext(); 195 196 // cache the current state 197 Map<Integer, Bundle> cachedSystemBarCurrentState = cacheSystemBarCurrentState(); 198 199 resetSystemBarContent(/* isProvisionedStateChange= */ false); 200 201 // retrieve the previous state 202 restoreSystemBarSavedState(cachedSystemBarCurrentState); 203 } else { 204 mWindowContextUpdateCheckRetryCount++; 205 mHandler.postDelayed(this, RETRY_DELAY_FOR_WINDOW_CONTEXT_UPDATE_CHECK); 206 } 207 } 208 }; 209 CarSystemBarControllerImpl(Context context, UserTracker userTracker, CarSystemBarViewFactory carSystemBarViewFactory, SystemBarConfigs systemBarConfigs, LightBarController lightBarController, DarkIconDispatcher darkIconDispatcher, WindowManager windowManager, CarDeviceProvisionedController deviceProvisionedController, CommandQueue commandQueue, AutoHideController autoHideController, ButtonSelectionStateListener buttonSelectionStateListener, @Main DelayableExecutor mainExecutor, IStatusBarService barService, Lazy<KeyguardStateController> keyguardStateControllerLazy, Lazy<PhoneStatusBarPolicy> iconPolicyLazy, ConfigurationController configurationController, CarSystemBarRestartTracker restartTracker, DisplayTracker displayTracker, @Nullable ToolbarController toolbarController, @Main Handler handler)210 public CarSystemBarControllerImpl(Context context, 211 UserTracker userTracker, 212 CarSystemBarViewFactory carSystemBarViewFactory, 213 SystemBarConfigs systemBarConfigs, 214 // TODO(b/156052638): Should not need to inject LightBarController 215 LightBarController lightBarController, 216 DarkIconDispatcher darkIconDispatcher, 217 WindowManager windowManager, 218 CarDeviceProvisionedController deviceProvisionedController, 219 CommandQueue commandQueue, 220 AutoHideController autoHideController, 221 ButtonSelectionStateListener buttonSelectionStateListener, 222 @Main DelayableExecutor mainExecutor, 223 IStatusBarService barService, 224 Lazy<KeyguardStateController> keyguardStateControllerLazy, 225 Lazy<PhoneStatusBarPolicy> iconPolicyLazy, 226 ConfigurationController configurationController, 227 CarSystemBarRestartTracker restartTracker, 228 DisplayTracker displayTracker, 229 @Nullable ToolbarController toolbarController, 230 @Main Handler handler) { 231 mContext = context; 232 mUserTracker = userTracker; 233 mCarSystemBarViewFactory = carSystemBarViewFactory; 234 mSystemBarConfigs = systemBarConfigs; 235 mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; 236 mCarDeviceProvisionedController = deviceProvisionedController; 237 mCommandQueue = commandQueue; 238 mAutoHideController = autoHideController; 239 mButtonSelectionStateListener = buttonSelectionStateListener; 240 mExecutor = mainExecutor; 241 mBarService = barService; 242 mKeyguardStateControllerLazy = keyguardStateControllerLazy; 243 mIconPolicyLazy = iconPolicyLazy; 244 mDisplayId = context.getDisplayId(); 245 mDisplayTracker = displayTracker; 246 mIsUiModeNight = mContext.getResources().getConfiguration().isNightModeActive(); 247 mCurrentLocale = mContext.getResources().getConfiguration().getLocales().get(0); 248 mConfigurationController = configurationController; 249 mCarSystemBarRestartTracker = restartTracker; 250 mDisplayCompatToolbarController = toolbarController; 251 mHandler = handler; 252 } 253 254 /** 255 * Initializes the SystemBars 256 */ init()257 public void init() { 258 259 resetSystemBarConfigs(); 260 261 // Set initial state. 262 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 263 mHideBarForKeyboardMap.put(side, mSystemBarConfigs.getHideForKeyboardBySide(side)); 264 }); 265 266 // Connect into the status bar manager service 267 mCommandQueue.addCallback(this); 268 269 mAutoHideController.setStatusBar(new AutoHideUiElement() { 270 @Override 271 public void synchronizeState() { 272 // No op. 273 } 274 275 @Override 276 public boolean isVisible() { 277 return mStatusBarTransientShown; 278 } 279 280 @Override 281 public void hide() { 282 clearTransient(); 283 } 284 }); 285 286 mAutoHideController.setNavigationBar(new AutoHideUiElement() { 287 @Override 288 public void synchronizeState() { 289 // No op. 290 } 291 292 @Override 293 public boolean isVisible() { 294 return mNavBarTransientShown; 295 } 296 297 @Override 298 public void hide() { 299 clearTransient(); 300 } 301 }); 302 303 mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup(); 304 mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress(); 305 mCarDeviceProvisionedController.addCallback( 306 new CarDeviceProvisionedListener() { 307 @Override 308 public void onUserSetupInProgressChanged() { 309 mExecutor.execute(() -> resetSystemBarContentIfNecessary()); 310 } 311 312 @Override 313 public void onUserSetupChanged() { 314 mExecutor.execute(() -> resetSystemBarContentIfNecessary()); 315 } 316 317 @Override 318 public void onUserSwitched() { 319 mExecutor.execute(() -> resetSystemBarContentIfNecessary()); 320 } 321 }); 322 323 mConfigurationController.addCallback(/* listener= */ this); 324 registerOverlayChangeBroadcastReceiver(); 325 326 createSystemBar(); 327 328 TaskStackChangeListeners.getInstance().registerTaskStackListener( 329 mButtonSelectionStateListener); 330 TaskStackChangeListeners.getInstance().registerTaskStackListener( 331 new TaskStackChangeListener() { 332 @Override 333 public void onTaskMovedToFront(RunningTaskInfo taskInfo) { 334 if (mDisplayCompatToolbarController != null) { 335 mDisplayCompatToolbarController.update(taskInfo); 336 } 337 } 338 }); 339 340 // Lastly, call to the icon policy to install/update all the icons. 341 // Must be called on the main thread due to the use of observeForever() in 342 // mIconPolicy.init(). 343 mExecutor.execute(() -> { 344 mIconPolicyLazy.get().init(); 345 }); 346 } 347 348 /** 349 * We register for soft keyboard visibility events such that we can hide the navigation bar 350 * giving more screen space to the IME. Note: this is optional and controlled by 351 * {@code com.android.internal.R.bool.config_hideNavBarForKeyboard}. 352 */ 353 @Override setImeWindowStatus(int displayId, int visibility, int backDisposition, boolean showImeSwitcher)354 public void setImeWindowStatus(int displayId, int visibility, int backDisposition, 355 boolean showImeSwitcher) { 356 if (mContext.getDisplayId() != displayId) { 357 return; 358 } 359 360 boolean isKeyboardVisible = (visibility & InputMethodService.IME_VISIBLE) != 0; 361 362 updateKeyboardVisibility(isKeyboardVisible); 363 } 364 365 @Override onSystemBarAttributesChanged( int displayId, @WindowInsetsController.Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @WindowInsetsController.Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName, LetterboxDetails[] letterboxDetails)366 public void onSystemBarAttributesChanged( 367 int displayId, 368 @WindowInsetsController.Appearance int appearance, 369 AppearanceRegion[] appearanceRegions, 370 boolean navbarColorManagedByIme, 371 @WindowInsetsController.Behavior int behavior, 372 @InsetsType int requestedVisibleTypes, 373 String packageName, 374 LetterboxDetails[] letterboxDetails) { 375 if (displayId != mDisplayId) { 376 return; 377 } 378 boolean barModeChanged = updateStatusBarMode( 379 mStatusBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT); 380 int numStacks = appearanceRegions.length; 381 boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks; 382 for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) { 383 stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]); 384 } 385 if (stackAppearancesChanged || barModeChanged) { 386 mAppearanceRegions = appearanceRegions; 387 updateStatusBarAppearance(); 388 } 389 } 390 391 @Override disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2, boolean animate)392 public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2, 393 boolean animate) { 394 if (displayId != mDisplayId) { 395 return; 396 } 397 setSystemBarStates(state1, state2); 398 } 399 400 @Override showTransient(int displayId, int types, boolean isGestureOnSystemBar)401 public void showTransient(int displayId, int types, boolean isGestureOnSystemBar) { 402 if (displayId != mDisplayId) { 403 return; 404 } 405 if ((types & WindowInsets.Type.statusBars()) != 0) { 406 if (!mStatusBarTransientShown) { 407 mStatusBarTransientShown = true; 408 handleTransientChanged(); 409 } 410 } 411 if ((types & WindowInsets.Type.navigationBars()) != 0) { 412 if (!mNavBarTransientShown) { 413 mNavBarTransientShown = true; 414 handleTransientChanged(); 415 } 416 } 417 } 418 419 @Override abortTransient(int displayId, int types)420 public void abortTransient(int displayId, int types) { 421 if (displayId != mDisplayId) { 422 return; 423 } 424 if ((types & (WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars())) == 0) { 425 return; 426 } 427 clearTransient(); 428 } 429 430 @Override onConfigChanged(Configuration newConfig)431 public void onConfigChanged(Configuration newConfig) { 432 Locale oldLocale = mCurrentLocale; 433 mCurrentLocale = newConfig.getLocales().get(0); 434 435 boolean isConfigNightMode = newConfig.isNightModeActive(); 436 if (isConfigNightMode == mIsUiModeNight 437 && ((mCurrentLocale != null && mCurrentLocale.equals(oldLocale)) 438 || mCurrentLocale == oldLocale)) { 439 return; 440 } 441 442 // Refresh UI on Night mode or system language changes. 443 if (isConfigNightMode != mIsUiModeNight) { 444 mIsUiModeNight = isConfigNightMode; 445 } 446 447 if (mWindowContextUpdateCheckRunnable != null) { 448 mHandler.removeCallbacks(mWindowContextUpdateCheckRunnable); 449 mWindowContextUpdateCheckRetryCount = 0; 450 } 451 mHandler.post(mWindowContextUpdateCheckRunnable); 452 } 453 454 checkSystemBarWindowContextsAreUpdated()455 private boolean checkSystemBarWindowContextsAreUpdated() { 456 return mSystemBarConfigs.getSystemBarSidesByZOrder().stream().allMatch(side -> { 457 Configuration windowConfig = mSystemBarConfigs.getWindowContextBySide( 458 side).getResources().getConfiguration(); 459 Locale locale = windowConfig.getLocales().get(0); 460 return windowConfig.isNightModeActive() == mIsUiModeNight && ( 461 (locale != null && locale.equals(mCurrentLocale)) || locale == mCurrentLocale); 462 }); 463 } 464 cacheSystemBarCurrentState()465 private Map<Integer, Bundle> cacheSystemBarCurrentState() { 466 Map<Integer, Bundle> savedStates = mSystemBarConfigs.getSystemBarSidesByZOrder().stream() 467 .collect(HashMap::new, 468 (map, side) -> { 469 Bundle bundle = new Bundle(); 470 getBarViewController(side, isDeviceSetupForUser()) 471 .onSaveInstanceState(bundle); 472 map.put(side, bundle); 473 }, 474 HashMap::putAll); 475 return savedStates; 476 } 477 restoreSystemBarSavedState(Map<Integer, Bundle> savedStates)478 private void restoreSystemBarSavedState(Map<Integer, Bundle> savedStates) { 479 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 480 getBarViewController(side, isDeviceSetupForUser()) 481 .onRestoreInstanceState(savedStates.get(side)); 482 }); 483 } 484 readConfigs()485 private void readConfigs() { 486 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 487 mSystemBarEnabledMap.put(side, mSystemBarConfigs.getEnabledStatusBySide(side)); 488 }); 489 } 490 491 /** Toggles the right nav bar visibility. */ 492 @VisibleForTesting setWindowVisibility(ViewGroup window, @View.Visibility int visibility)493 boolean setWindowVisibility(ViewGroup window, @View.Visibility int visibility) { 494 if (window == null) { 495 return false; 496 } 497 498 if (window.getVisibility() == visibility) { 499 return false; 500 } 501 502 window.setVisibility(visibility); 503 return true; 504 } 505 506 /** 507 * Sets the system bar states - {@code StatusBarManager.DisableFlags}, 508 * {@code StatusBarManager.Disable2Flags}, lock task mode. When there is a change in state, 509 * and refreshes the system bars. 510 * 511 * @param state {@code StatusBarManager.DisableFlags} 512 * @param state2 {@code StatusBarManager.Disable2Flags} 513 */ 514 @VisibleForTesting setSystemBarStates(int state, int state2)515 void setSystemBarStates(int state, int state2) { 516 int diff = (state ^ mStatusBarState) | (state2 ^ mStatusBarState2); 517 int lockTaskMode = getLockTaskModeState(); 518 if (diff == 0 && mLockTaskMode == lockTaskMode) { 519 if (DEBUG) { 520 Log.d(TAG, "setSystemBarStates(): status bar states unchanged: state: " 521 + state + " state2: " + state2 + " lockTaskMode: " + mLockTaskMode); 522 } 523 return; 524 } 525 mStatusBarState = state; 526 mStatusBarState2 = state2; 527 mLockTaskMode = lockTaskMode; 528 } 529 530 @VisibleForTesting getStatusBarState()531 int getStatusBarState() { 532 return mStatusBarState; 533 } 534 535 @VisibleForTesting getStatusBarState2()536 int getStatusBarState2() { 537 return mStatusBarState2; 538 } 539 getLockTaskModeState()540 private int getLockTaskModeState() { 541 return mContext.getSystemService(ActivityManager.class).getLockTaskModeState(); 542 } 543 showAdminSupportDetailsDialog()544 private void showAdminSupportDetailsDialog() { 545 // TODO(b/205891123): launch AdminSupportDetailsDialog after moving 546 // AdminSupportDetailsDialog out of CarSettings since CarSettings is not and should not 547 // be allowlisted for lock task mode. 548 Toast.makeText(mContext, "This action is unavailable for your profile", 549 Toast.LENGTH_LONG).show(); 550 } 551 552 @VisibleForTesting 553 @Nullable getBarWindow(@ystemBarSide int side)554 ViewGroup getBarWindow(@SystemBarSide int side) { 555 return mSystemBarEnabledMap.get(side) ? mCarSystemBarViewFactory 556 .getSystemBarWindow(side) : null; 557 } 558 559 @VisibleForTesting 560 @Nullable getBarViewController(@ystemBarSide int side, boolean isSetUp)561 CarSystemBarViewController getBarViewController(@SystemBarSide int side, boolean isSetUp) { 562 563 if (!mSystemBarEnabledMap.get(side)) { 564 return null; 565 } 566 567 CarSystemBarViewController viewController = mCarSystemBarViewFactory 568 .getSystemBarViewController(side, isSetUp); 569 Set<View.OnTouchListener> statusBarTouchListeners = mBarTouchListenersMap.get(side); 570 viewController.setSystemBarTouchListeners( 571 statusBarTouchListeners != null ? statusBarTouchListeners : new ArraySet<>()); 572 573 mSystemBarViewControllerMap.put(side, viewController); 574 return viewController; 575 } 576 577 @Override registerBarTouchListener(@ystemBarSide int side, View.OnTouchListener listener)578 public void registerBarTouchListener(@SystemBarSide int side, View.OnTouchListener listener) { 579 if (mBarTouchListenersMap.get(side) == null) { 580 mBarTouchListenersMap.put(side, new ArraySet<>()); 581 } 582 boolean setModified = mBarTouchListenersMap.get(side).add(listener); 583 if (setModified && mSystemBarViewControllerMap.get(side) != null) { 584 mSystemBarViewControllerMap.get(side) 585 .setSystemBarTouchListeners(mBarTouchListenersMap.get(side)); 586 } 587 } 588 589 /** 590 * Shows all of the navigation buttons on the valid instances of {@link CarSystemBarView}. 591 */ 592 @Override showAllNavigationButtons()593 public void showAllNavigationButtons() { 594 showAllNavigationButtons(isDeviceSetupForUser()); 595 } 596 597 // TODO(b/368407601): can we remove this? 598 @VisibleForTesting showAllNavigationButtons(boolean isSetup)599 void showAllNavigationButtons(boolean isSetup) { 600 checkAllBars(isSetup); 601 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 602 if (mSystemBarViewControllerMap.get(side) != null) { 603 mSystemBarViewControllerMap.get(side) 604 .showButtonsOfType(BUTTON_TYPE_NAVIGATION); 605 } 606 }); 607 } 608 609 /** 610 * Shows all of the keyguard specific buttons on the valid instances of 611 * {@link CarSystemBarView}. 612 */ 613 @Override showAllKeyguardButtons()614 public void showAllKeyguardButtons() { 615 showAllKeyguardButtons(isDeviceSetupForUser()); 616 } 617 618 // TODO(b/368407601): can we remove this? 619 @VisibleForTesting showAllKeyguardButtons(boolean isSetUp)620 void showAllKeyguardButtons(boolean isSetUp) { 621 checkAllBars(isSetUp); 622 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 623 if (mSystemBarViewControllerMap.get(side) != null) { 624 mSystemBarViewControllerMap.get(side) 625 .showButtonsOfType(BUTTON_TYPE_KEYGUARD); 626 } 627 }); 628 } 629 630 /** 631 * Shows all of the occlusion state buttons on the valid instances of 632 * {@link CarSystemBarView}. 633 */ 634 @Override showAllOcclusionButtons()635 public void showAllOcclusionButtons() { 636 showAllOcclusionButtons(isDeviceSetupForUser()); 637 } 638 639 // TODO(b/368407601): can we remove this? 640 @VisibleForTesting showAllOcclusionButtons(boolean isSetUp)641 void showAllOcclusionButtons(boolean isSetUp) { 642 checkAllBars(isSetUp); 643 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 644 if (mSystemBarViewControllerMap.get(side) != null) { 645 mSystemBarViewControllerMap.get(side) 646 .showButtonsOfType(BUTTON_TYPE_OCCLUSION); 647 } 648 }); 649 } 650 checkAllBars(boolean isSetUp)651 private void checkAllBars(boolean isSetUp) { 652 mSystemBarViewControllerMap.clear(); 653 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 654 mSystemBarViewControllerMap.put(side, getBarViewController(side, isSetUp)); 655 }); 656 } 657 658 /** 659 * Invalidate SystemBarConfigs and fetch again from Resources. 660 * TODO(): b/260206944, Can remove this after we have a fix for overlaid resources not applied. 661 */ 662 @VisibleForTesting resetSystemBarConfigs()663 void resetSystemBarConfigs() { 664 mSystemBarConfigs.resetSystemBarConfigs(); 665 mCarSystemBarViewFactory.resetSystemBarWindowCache(); 666 readConfigs(); 667 } 668 669 /** 670 * Invalidate SystemBar window context and recreates from application context. 671 */ resetSystemBarContext()672 void resetSystemBarContext() { 673 mSystemBarConfigs.resetSystemBarWindowContext(); 674 } 675 updateKeyboardVisibility(boolean isKeyboardVisible)676 protected void updateKeyboardVisibility(boolean isKeyboardVisible) { 677 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 678 if (mHideBarForKeyboardMap.get(side)) { 679 setWindowVisibility(getBarWindow(side), 680 isKeyboardVisible ? View.GONE : View.VISIBLE); 681 } 682 }); 683 } 684 createSystemBar()685 protected void createSystemBar() { 686 RegisterStatusBarResult result = null; 687 try { 688 // Register only for Primary User. 689 result = mBarService.registerStatusBar(mCommandQueue); 690 691 onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, 692 result.mNavbarColorManagedByIme, result.mBehavior, 693 result.mRequestedVisibleTypes, 694 result.mPackageName, result.mLetterboxDetails); 695 696 setImeWindowStatus(mDisplayId, result.mImeWindowVis, result.mImeBackDisposition, 697 result.mShowImeSwitcher); 698 699 // Set up the initial icon state 700 int numIcons = result.mIcons.size(); 701 for (int i = 0; i < numIcons; i++) { 702 mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i)); 703 } 704 } catch (RemoteException ex) { 705 ex.rethrowFromSystemServer(); 706 } 707 708 // Try setting up the initial state of the nav bar if applicable. 709 if (result != null) { 710 setImeWindowStatus(mDisplayTracker.getDefaultDisplayId(), result.mImeWindowVis, 711 result.mImeBackDisposition, result.mShowImeSwitcher); 712 } 713 714 createNavBar(); 715 } 716 createNavBar()717 protected void createNavBar() { 718 buildNavBarWindows(); 719 buildNavBarContent(); 720 attachNavBarWindows(); 721 } 722 buildNavBarWindows()723 private void buildNavBarWindows() { 724 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 725 mSystemBarWindowMap.put(side, getBarWindow(side)); 726 }); 727 728 if (mDisplayCompatToolbarController != null) { 729 if (mSystemBarConfigs 730 .isLeftDisplayCompatToolbarEnabled()) { 731 mDisplayCompatToolbarController.init(mSystemBarWindowMap.get(LEFT)); 732 } else if (mSystemBarConfigs 733 .isRightDisplayCompatToolbarEnabled()) { 734 mDisplayCompatToolbarController.init(mSystemBarWindowMap.get(RIGHT)); 735 } 736 } 737 } 738 buildNavBarContent()739 private void buildNavBarContent() { 740 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 741 CarSystemBarViewController viewController = getBarViewController(side, 742 isDeviceSetupForUser()); 743 ViewGroup systemBarWindow = mSystemBarWindowMap.get(side); 744 if (viewController != null && systemBarWindow != null) { 745 systemBarWindow.addView(viewController.getView()); 746 } 747 }); 748 } 749 attachNavBarWindows()750 private void attachNavBarWindows() { 751 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 752 ViewGroup barWindow = mSystemBarWindowMap.get(side); 753 boolean isBarAttached = mSystemBarAttachedMap.get(side); 754 boolean isBarEnabled = mSystemBarConfigs.getEnabledStatusBySide(side); 755 if (DEBUG) { 756 Log.d(TAG, "Side = " + side 757 + ", SystemBarWindow = " + barWindow 758 + ", SystemBarAttached=" + isBarAttached 759 + ", enabled=" + isBarEnabled); 760 } 761 if (barWindow != null && !isBarAttached && isBarEnabled) { 762 WindowManager wm = getWindowManagerForSide(side); 763 if (wm != null) { 764 wm.addView(barWindow, mSystemBarConfigs.getLayoutParamsBySide(side)); 765 mSystemBarAttachedMap.put(side, true); 766 } 767 768 } 769 }); 770 } 771 getWindowManagerForSide(@ystemBarSide int side)772 private WindowManager getWindowManagerForSide(@SystemBarSide int side) { 773 Context windowContext = mSystemBarConfigs.getWindowContextBySide(side); 774 if (windowContext == null) { 775 return null; 776 } 777 return windowContext.getSystemService(WindowManager.class); 778 } 779 registerOverlayChangeBroadcastReceiver()780 private void registerOverlayChangeBroadcastReceiver() { 781 if (!configAwareSystemui()) { 782 if (DEBUG) { 783 Log.d(TAG, "Ignore overlay change for car systemui"); 784 } 785 return; 786 } 787 IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); 788 overlayFilter.addDataScheme(OVERLAY_FILTER_DATA_SCHEME); 789 overlayFilter.addDataSchemeSpecificPart(mContext.getPackageName(), 790 PatternMatcher.PATTERN_LITERAL); 791 BroadcastReceiver receiver = new BroadcastReceiver() { 792 @Override 793 public void onReceive(Context context, Intent intent) { 794 for (int i = 0; i < mSystemBarAttachedMap.size(); i++) { 795 if (mSystemBarAttachedMap.valueAt(i)) { 796 restartSystemBars(); 797 break; 798 } 799 } 800 } 801 }; 802 mContext.registerReceiver(receiver, overlayFilter, /* broadcastPermission= */ 803 null, /* handler= */ null); 804 } 805 resetSystemBarContentIfNecessary()806 private void resetSystemBarContentIfNecessary() { 807 boolean currentUserSetup = mCarDeviceProvisionedController.isCurrentUserSetup(); 808 boolean currentUserSetupInProgress = mCarDeviceProvisionedController 809 .isCurrentUserSetupInProgress(); 810 if (mIsUserSetupInProgress != currentUserSetupInProgress 811 || mDeviceIsSetUpForUser != currentUserSetup) { 812 mDeviceIsSetUpForUser = currentUserSetup; 813 mIsUserSetupInProgress = currentUserSetupInProgress; 814 resetSystemBarContent(/* isProvisionedStateChange= */ true); 815 } 816 } 817 818 /** 819 * Remove all content from navbars and rebuild them. Used to allow for different nav bars 820 * before and after the device is provisioned. . Also for change of density and font size. 821 */ resetSystemBarContent(boolean isProvisionedStateChange)822 private void resetSystemBarContent(boolean isProvisionedStateChange) { 823 mCarSystemBarRestartTracker.notifyPendingRestart(/* recreateWindows= */ false, 824 isProvisionedStateChange); 825 826 if (!isProvisionedStateChange) { 827 mCarSystemBarViewFactory.resetSystemBarViewCache(); 828 } 829 clearSystemBarWindow(/* removeUnusedWindow= */ false); 830 831 buildNavBarContent(); 832 // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need 833 // to correctly respect that state. 834 if (mKeyguardStateControllerLazy.get().isShowing()) { 835 showAllKeyguardButtons(isDeviceSetupForUser()); 836 } else { 837 showAllNavigationButtons(isDeviceSetupForUser()); 838 } 839 840 // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the 841 // selection state that reflects the current task stack. 842 mButtonSelectionStateListener.onTaskStackChanged(); 843 844 mCarSystemBarRestartTracker.notifyRestartComplete(/* windowRecreated= */ false, 845 isProvisionedStateChange); 846 } 847 isDeviceSetupForUser()848 private boolean isDeviceSetupForUser() { 849 return mDeviceIsSetUpForUser && !mIsUserSetupInProgress; 850 } 851 updateStatusBarAppearance()852 private void updateStatusBarAppearance() { 853 int numStacks = mAppearanceRegions.length; 854 final ArrayList<Rect> lightBarBounds = new ArrayList<>(); 855 856 for (int i = 0; i < numStacks; i++) { 857 final AppearanceRegion ar = mAppearanceRegions[i]; 858 if (isLight(ar.getAppearance())) { 859 lightBarBounds.add(ar.getBounds()); 860 } 861 } 862 863 // If all stacks are light, all icons become dark. 864 if (lightBarBounds.size() == numStacks) { 865 mStatusBarIconController.setIconsDarkArea(null); 866 mStatusBarIconController.getTransitionsController().setIconsDark( 867 /* dark= */ true, /* animate= */ false); 868 } else if (lightBarBounds.isEmpty()) { 869 // If no one is light, all icons become white. 870 mStatusBarIconController.getTransitionsController().setIconsDark( 871 /* dark= */ false, /* animate= */ false); 872 } else { 873 // Not the same for every stack, update icons in area only. 874 mStatusBarIconController.setIconsDarkArea(lightBarBounds); 875 mStatusBarIconController.getTransitionsController().setIconsDark( 876 /* dark= */ true, /* animate= */ false); 877 } 878 } 879 isLight(int appearance)880 private static boolean isLight(int appearance) { 881 return (appearance & APPEARANCE_LIGHT_STATUS_BARS) != 0; 882 } 883 handleTransientChanged()884 private void handleTransientChanged() { 885 updateStatusBarMode(mStatusBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT); 886 updateNavBarMode(mNavBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT); 887 } 888 889 // Returns true if the status bar mode has changed. updateStatusBarMode(int barMode)890 private boolean updateStatusBarMode(int barMode) { 891 if (mStatusBarMode != barMode) { 892 mStatusBarMode = barMode; 893 mAutoHideController.touchAutoHide(); 894 return true; 895 } 896 return false; 897 } 898 899 // Returns true if the nav bar mode has changed. updateNavBarMode(int barMode)900 private boolean updateNavBarMode(int barMode) { 901 if (mSystemBarMode != barMode) { 902 mSystemBarMode = barMode; 903 mAutoHideController.touchAutoHide(); 904 return true; 905 } 906 return false; 907 } 908 909 @VisibleForTesting restartSystemBars()910 void restartSystemBars() { 911 mCarSystemBarRestartTracker.notifyPendingRestart(/* recreateWindows= */ true, 912 /* provisionedStateChanged= */ false); 913 914 resetSystemBarConfigs(); 915 clearSystemBarWindow(/* removeUnusedWindow= */ true); 916 buildNavBarWindows(); 917 buildNavBarContent(); 918 attachNavBarWindows(); 919 920 mCarSystemBarRestartTracker.notifyRestartComplete(/* windowRecreated= */ true, 921 /* provisionedStateChanged= */ false); 922 } 923 clearSystemBarWindow(boolean removeUnusedWindow)924 private void clearSystemBarWindow(boolean removeUnusedWindow) { 925 mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(side -> { 926 ViewGroup barWindow = getBarWindow(side); 927 if (barWindow != null) { 928 barWindow.removeAllViews(); 929 if (removeUnusedWindow) { 930 WindowManager wm = getWindowManagerForSide(side); 931 if (wm != null) { 932 wm.removeViewImmediate(barWindow); 933 } 934 mSystemBarAttachedMap.put(side, false); 935 } 936 mSystemBarViewControllerMap.remove(side); 937 } 938 }); 939 } 940 941 @VisibleForTesting getIsUiModeNight()942 boolean getIsUiModeNight() { 943 return mIsUiModeNight; 944 } 945 clearTransient()946 private void clearTransient() { 947 if (mStatusBarTransientShown) { 948 mStatusBarTransientShown = false; 949 handleTransientChanged(); 950 } 951 if (mNavBarTransientShown) { 952 mNavBarTransientShown = false; 953 handleTransientChanged(); 954 } 955 } 956 957 @VisibleForTesting isStatusBarTransientShown()958 boolean isStatusBarTransientShown() { 959 return mStatusBarTransientShown; 960 } 961 962 @VisibleForTesting isNavBarTransientShown()963 boolean isNavBarTransientShown() { 964 return mNavBarTransientShown; 965 } 966 } 967