• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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