1 /*
2  * Copyright (C) 2020 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.SystemBarConfigs.BOTTOM;
24 import static com.android.systemui.car.systembar.SystemBarConfigs.LEFT;
25 import static com.android.systemui.car.systembar.SystemBarConfigs.RIGHT;
26 import static com.android.systemui.car.systembar.SystemBarConfigs.TOP;
27 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
28 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
29 
30 import android.annotation.Nullable;
31 import android.app.ActivityManager.RunningTaskInfo;
32 import android.app.StatusBarManager.Disable2Flags;
33 import android.app.StatusBarManager.DisableFlags;
34 import android.app.UiModeManager;
35 import android.content.BroadcastReceiver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.content.res.Configuration;
40 import android.graphics.Rect;
41 import android.inputmethodservice.InputMethodService;
42 import android.os.IBinder;
43 import android.os.PatternMatcher;
44 import android.os.RemoteException;
45 import android.os.UserHandle;
46 import android.util.Log;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.view.WindowInsets;
50 import android.view.WindowInsets.Type.InsetsType;
51 import android.view.WindowInsetsController;
52 import android.view.WindowManager;
53 
54 import androidx.annotation.VisibleForTesting;
55 
56 import com.android.internal.statusbar.IStatusBarService;
57 import com.android.internal.statusbar.LetterboxDetails;
58 import com.android.internal.statusbar.RegisterStatusBarResult;
59 import com.android.internal.view.AppearanceRegion;
60 import com.android.systemui.CoreStartable;
61 import com.android.systemui.R;
62 import com.android.systemui.car.CarDeviceProvisionedController;
63 import com.android.systemui.car.CarDeviceProvisionedListener;
64 import com.android.systemui.car.displaycompat.ToolbarController;
65 import com.android.systemui.car.hvac.HvacController;
66 import com.android.systemui.car.users.CarSystemUIUserUtil;
67 import com.android.systemui.dagger.SysUISingleton;
68 import com.android.systemui.dagger.qualifiers.Main;
69 import com.android.systemui.dagger.qualifiers.UiBackground;
70 import com.android.systemui.plugins.DarkIconDispatcher;
71 import com.android.systemui.settings.DisplayTracker;
72 import com.android.systemui.shared.system.TaskStackChangeListener;
73 import com.android.systemui.shared.system.TaskStackChangeListeners;
74 import com.android.systemui.statusbar.AutoHideUiElement;
75 import com.android.systemui.statusbar.CommandQueue;
76 import com.android.systemui.statusbar.phone.AutoHideController;
77 import com.android.systemui.statusbar.phone.BarTransitions;
78 import com.android.systemui.statusbar.phone.LightBarController;
79 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
80 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
81 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher;
82 import com.android.systemui.statusbar.policy.ConfigurationController;
83 import com.android.systemui.statusbar.policy.KeyguardStateController;
84 import com.android.systemui.util.concurrency.DelayableExecutor;
85 import com.android.systemui.wm.MDSystemBarsController;
86 
87 import dagger.Lazy;
88 
89 import java.io.PrintWriter;
90 import java.util.ArrayList;
91 import java.util.Locale;
92 import java.util.Optional;
93 import java.util.concurrent.Executor;
94 
95 import javax.inject.Inject;
96 
97 /** Navigation bars customized for the automotive use case. */
98 @SysUISingleton
99 public class CarSystemBar implements CoreStartable, CommandQueue.Callbacks,
100         ConfigurationController.ConfigurationListener,
101         MDSystemBarsController.Listener {
102     private static final String TAG = CarSystemBar.class.getSimpleName();
103     private static final String OVERLAY_FILTER_DATA_SCHEME = "package";
104 
105     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
106 
107     private final Context mContext;
108     private final CarSystemBarController mCarSystemBarController;
109     private final SysuiDarkIconDispatcher mStatusBarIconController;
110     private final WindowManager mWindowManager;
111     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
112     private final CommandQueue mCommandQueue;
113     private final AutoHideController mAutoHideController;
114     private final ButtonSelectionStateListener mButtonSelectionStateListener;
115     private final DelayableExecutor mExecutor;
116     private final Executor mUiBgExecutor;
117     private final IStatusBarService mBarService;
118     private final DisplayTracker mDisplayTracker;
119     private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
120     private final Lazy<PhoneStatusBarPolicy> mIconPolicyLazy;
121     private final HvacController mHvacController;
122     private final ConfigurationController mConfigurationController;
123     private final CarSystemBarRestartTracker mCarSystemBarRestartTracker;
124     private final int mDisplayId;
125     private final SystemBarConfigs mSystemBarConfigs;
126     @Nullable
127     private final ToolbarController mDisplayCompatToolbarController;
128     private UiModeManager mUiModeManager;
129     private StatusBarSignalPolicy mSignalPolicy;
130 
131     // If the nav bar should be hidden when the soft keyboard is visible.
132     private boolean mHideTopBarForKeyboard;
133     private boolean mHideLeftBarForKeyboard;
134     private boolean mHideRightBarForKeyboard;
135     private boolean mHideBottomBarForKeyboard;
136     // Nav bar views.
137     private ViewGroup mTopSystemBarWindow;
138     private ViewGroup mBottomSystemBarWindow;
139     private ViewGroup mLeftSystemBarWindow;
140     private ViewGroup mRightSystemBarWindow;
141     private CarSystemBarView mTopSystemBarView;
142     private CarSystemBarView mBottomSystemBarView;
143     private CarSystemBarView mLeftSystemBarView;
144     private CarSystemBarView mRightSystemBarView;
145     private boolean mTopSystemBarAttached;
146     private boolean mBottomSystemBarAttached;
147     private boolean mLeftSystemBarAttached;
148     private boolean mRightSystemBarAttached;
149 
150     // To be attached to the navigation bars such that they can close the notification panel if
151     // it's open.
152     private boolean mDeviceIsSetUpForUser = true;
153     private boolean mIsUserSetupInProgress = false;
154 
155     private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
156     @BarTransitions.TransitionMode
157     private int mStatusBarMode;
158     @BarTransitions.TransitionMode
159     private int mSystemBarMode;
160     private boolean mStatusBarTransientShown;
161     private boolean mNavBarTransientShown;
162 
163     private boolean mIsUiModeNight = false;
164     private MDSystemBarsController mMDSystemBarsController;
165 
166     private Locale mCurrentLocale;
167 
168     @Inject
CarSystemBar(Context context, CarSystemBarController carSystemBarController, LightBarController lightBarController, DarkIconDispatcher darkIconDispatcher, WindowManager windowManager, CarDeviceProvisionedController deviceProvisionedController, CommandQueue commandQueue, AutoHideController autoHideController, ButtonSelectionStateListener buttonSelectionStateListener, @Main DelayableExecutor mainExecutor, @UiBackground Executor uiBgExecutor, IStatusBarService barService, Lazy<KeyguardStateController> keyguardStateControllerLazy, Lazy<PhoneStatusBarPolicy> iconPolicyLazy, HvacController hvacController, StatusBarSignalPolicy signalPolicy, SystemBarConfigs systemBarConfigs, ConfigurationController configurationController, CarSystemBarRestartTracker restartTracker, DisplayTracker displayTracker, Optional<MDSystemBarsController> mdSystemBarsController, @Nullable ToolbarController toolbarController )169     public CarSystemBar(Context context,
170             CarSystemBarController carSystemBarController,
171             // TODO(b/156052638): Should not need to inject LightBarController
172             LightBarController lightBarController,
173             DarkIconDispatcher darkIconDispatcher,
174             WindowManager windowManager,
175             CarDeviceProvisionedController deviceProvisionedController,
176             CommandQueue commandQueue,
177             AutoHideController autoHideController,
178             ButtonSelectionStateListener buttonSelectionStateListener,
179             @Main DelayableExecutor mainExecutor,
180             @UiBackground Executor uiBgExecutor,
181             IStatusBarService barService,
182             Lazy<KeyguardStateController> keyguardStateControllerLazy,
183             Lazy<PhoneStatusBarPolicy> iconPolicyLazy,
184             HvacController hvacController,
185             StatusBarSignalPolicy signalPolicy,
186             SystemBarConfigs systemBarConfigs,
187             ConfigurationController configurationController,
188             CarSystemBarRestartTracker restartTracker,
189             DisplayTracker displayTracker,
190             Optional<MDSystemBarsController> mdSystemBarsController,
191             @Nullable ToolbarController toolbarController
192     ) {
193         mContext = context;
194         mCarSystemBarController = carSystemBarController;
195         mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
196         mWindowManager = windowManager;
197         mCarDeviceProvisionedController = deviceProvisionedController;
198         mCommandQueue = commandQueue;
199         mAutoHideController = autoHideController;
200         mButtonSelectionStateListener = buttonSelectionStateListener;
201         mExecutor = mainExecutor;
202         mUiBgExecutor = uiBgExecutor;
203         mBarService = barService;
204         mKeyguardStateControllerLazy = keyguardStateControllerLazy;
205         mIconPolicyLazy = iconPolicyLazy;
206         mHvacController = hvacController;
207         mSystemBarConfigs = systemBarConfigs;
208         mSignalPolicy = signalPolicy;
209         mDisplayId = context.getDisplayId();
210         mUiModeManager = mContext.getSystemService(UiModeManager.class);
211         mDisplayTracker = displayTracker;
212         mIsUiModeNight = mContext.getResources().getConfiguration().isNightModeActive();
213         mMDSystemBarsController = mdSystemBarsController.orElse(null);
214         mCurrentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
215         mConfigurationController = configurationController;
216         mCarSystemBarRestartTracker = restartTracker;
217         mDisplayCompatToolbarController = toolbarController;
218     }
219 
registerOverlayChangeBroadcastReceiver()220     private void registerOverlayChangeBroadcastReceiver() {
221         if (!configAwareSystemui()) {
222             if (DEBUG) {
223                 Log.d(TAG, "Ignore overlay change for car systemui");
224             }
225             return;
226         }
227         IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
228         overlayFilter.addDataScheme(OVERLAY_FILTER_DATA_SCHEME);
229         overlayFilter.addDataSchemeSpecificPart(mContext.getPackageName(),
230                 PatternMatcher.PATTERN_LITERAL);
231         BroadcastReceiver receiver = new BroadcastReceiver() {
232             @Override
233             public void onReceive(Context context, Intent intent) {
234                 if (mTopSystemBarAttached || mBottomSystemBarAttached || mLeftSystemBarAttached
235                         || mRightSystemBarAttached) {
236                     restartSystemBars();
237                 }
238             }
239         };
240         mContext.registerReceiverAsUser(receiver, UserHandle.ALL,
241                 overlayFilter, /* broadcastPermission= */null, /* handler= */ null);
242     }
243 
244     @Override
start()245     public void start() {
246         // Set initial state.
247         mHideTopBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(TOP);
248         mHideBottomBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(BOTTOM);
249         mHideLeftBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(LEFT);
250         mHideRightBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(RIGHT);
251 
252         // Connect into the status bar manager service
253         mCommandQueue.addCallback(this);
254 
255         RegisterStatusBarResult result = null;
256         //Register only for Primary User.
257         if (!CarSystemUIUserUtil.isSecondaryMUMDSystemUI()) {
258             try {
259                 result = mBarService.registerStatusBar(mCommandQueue);
260             } catch (RemoteException ex) {
261                 ex.rethrowFromSystemServer();
262             }
263         } else if (mMDSystemBarsController != null) {
264             mMDSystemBarsController.addListener(this);
265         }
266 
267         if (result != null) {
268             onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
269                     result.mNavbarColorManagedByIme, result.mBehavior,
270                     result.mRequestedVisibleTypes,
271                     result.mPackageName, result.mLetterboxDetails);
272 
273             // StatusBarManagerService has a back up of IME token and it's restored here.
274             setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
275                     result.mImeBackDisposition, result.mShowImeSwitcher);
276 
277             // Set up the initial icon state
278             int numIcons = result.mIcons.size();
279             for (int i = 0; i < numIcons; i++) {
280                 mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
281             }
282         }
283 
284         mAutoHideController.setStatusBar(new AutoHideUiElement() {
285             @Override
286             public void synchronizeState() {
287                 // No op.
288             }
289 
290             @Override
291             public boolean isVisible() {
292                 return mStatusBarTransientShown;
293             }
294 
295             @Override
296             public void hide() {
297                 clearTransient();
298             }
299         });
300 
301         mAutoHideController.setNavigationBar(new AutoHideUiElement() {
302             @Override
303             public void synchronizeState() {
304                 // No op.
305             }
306 
307             @Override
308             public boolean isVisible() {
309                 return mNavBarTransientShown;
310             }
311 
312             @Override
313             public void hide() {
314                 clearTransient();
315             }
316         });
317 
318         mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
319         mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
320         mCarDeviceProvisionedController.addCallback(
321                 new CarDeviceProvisionedListener() {
322                     @Override
323                     public void onUserSetupInProgressChanged() {
324                         mExecutor.execute(() -> resetSystemBarContentIfNecessary());
325                     }
326 
327                     @Override
328                     public void onUserSetupChanged() {
329                         mExecutor.execute(() -> resetSystemBarContentIfNecessary());
330                     }
331 
332                     @Override
333                     public void onUserSwitched() {
334                         mExecutor.execute(() -> resetSystemBarContentIfNecessary());
335                     }
336                 });
337 
338         mConfigurationController.addCallback(/* listener= */ this);
339         registerOverlayChangeBroadcastReceiver();
340 
341         createSystemBar(result);
342 
343         TaskStackChangeListeners.getInstance().registerTaskStackListener(
344                 mButtonSelectionStateListener);
345         TaskStackChangeListeners.getInstance().registerTaskStackListener(
346                 new TaskStackChangeListener() {
347                     @Override
348                     public void onLockTaskModeChanged(int mode) {
349                         mCarSystemBarController.refreshSystemBar();
350                     }
351 
352                     @Override
353                     public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
354                         if (mDisplayCompatToolbarController != null) {
355                             mDisplayCompatToolbarController.update(taskInfo);
356                         }
357                     }
358                 });
359 
360         // Lastly, call to the icon policy to install/update all the icons.
361         // Must be called on the main thread due to the use of observeForever() in
362         // mIconPolicy.init().
363         mExecutor.execute(() -> {
364             mIconPolicyLazy.get().init();
365         });
366     }
367 
resetSystemBarContentIfNecessary()368     private void resetSystemBarContentIfNecessary() {
369         boolean currentUserSetup = mCarDeviceProvisionedController.isCurrentUserSetup();
370         boolean currentUserSetupInProgress = mCarDeviceProvisionedController
371                 .isCurrentUserSetupInProgress();
372         if (mIsUserSetupInProgress != currentUserSetupInProgress
373                 || mDeviceIsSetUpForUser != currentUserSetup) {
374             mDeviceIsSetUpForUser = currentUserSetup;
375             mIsUserSetupInProgress = currentUserSetupInProgress;
376             resetSystemBarContent(/* isProvisionedStateChange= */ true);
377         }
378     }
379 
380     /**
381      * Remove all content from navbars and rebuild them. Used to allow for different nav bars
382      * before and after the device is provisioned. . Also for change of density and font size.
383      */
resetSystemBarContent(boolean isProvisionedStateChange)384     private void resetSystemBarContent(boolean isProvisionedStateChange) {
385         mCarSystemBarRestartTracker.notifyPendingRestart(/* recreateWindows= */ false,
386                 isProvisionedStateChange);
387 
388         if (!isProvisionedStateChange) {
389             mCarSystemBarController.resetViewCache();
390         }
391         // remove and reattach all components such that we don't keep a reference to unused ui
392         // elements
393         mCarSystemBarController.removeAll();
394         clearSystemBarWindow(/* removeUnusedWindow= */ false);
395 
396         buildNavBarContent();
397         // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need
398         // to correctly respect that state.
399         if (mKeyguardStateControllerLazy.get().isShowing()) {
400             mCarSystemBarController.showAllKeyguardButtons(isDeviceSetupForUser());
401         } else {
402             mCarSystemBarController.showAllNavigationButtons(isDeviceSetupForUser());
403         }
404 
405         // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the
406         // selection state that reflects the current task stack.
407         mButtonSelectionStateListener.onTaskStackChanged();
408 
409         mCarSystemBarRestartTracker.notifyRestartComplete(/* recreateWindows= */ false,
410                 isProvisionedStateChange);
411     }
412 
isDeviceSetupForUser()413     private boolean isDeviceSetupForUser() {
414         return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
415     }
416 
createSystemBar(RegisterStatusBarResult result)417     private void createSystemBar(RegisterStatusBarResult result) {
418         buildNavBarWindows();
419         buildNavBarContent();
420         attachNavBarWindows();
421 
422         // Try setting up the initial state of the nav bar if applicable.
423         if (result != null) {
424             setImeWindowStatus(mDisplayTracker.getDefaultDisplayId(), result.mImeToken,
425                     result.mImeWindowVis, result.mImeBackDisposition,
426                     result.mShowImeSwitcher);
427         }
428     }
429 
buildNavBarWindows()430     private void buildNavBarWindows() {
431         mTopSystemBarWindow = mCarSystemBarController.getTopWindow();
432         mBottomSystemBarWindow = mCarSystemBarController.getBottomWindow();
433         mLeftSystemBarWindow = mCarSystemBarController.getLeftWindow();
434         mRightSystemBarWindow = mCarSystemBarController.getRightWindow();
435 
436         if (mDisplayCompatToolbarController != null) {
437             if (mSystemBarConfigs
438                     .isLeftDisplayCompatToolbarEnabled()) {
439                 mDisplayCompatToolbarController.init(mLeftSystemBarWindow);
440             } else if (mSystemBarConfigs
441                     .isRightDisplayCompatToolbarEnabled()) {
442                 mDisplayCompatToolbarController.init(mRightSystemBarWindow);
443             }
444         }
445     }
446 
buildNavBarContent()447     private void buildNavBarContent() {
448         mTopSystemBarView = mCarSystemBarController.getTopBar(isDeviceSetupForUser());
449         if (mTopSystemBarView != null) {
450             mSystemBarConfigs.insetSystemBar(TOP, mTopSystemBarView);
451             mHvacController.registerHvacViews(mTopSystemBarView);
452             mTopSystemBarWindow.addView(mTopSystemBarView);
453         }
454 
455         mBottomSystemBarView = mCarSystemBarController.getBottomBar(isDeviceSetupForUser());
456         if (mBottomSystemBarView != null) {
457             mSystemBarConfigs.insetSystemBar(BOTTOM, mBottomSystemBarView);
458             mHvacController.registerHvacViews(mBottomSystemBarView);
459             mBottomSystemBarWindow.addView(mBottomSystemBarView);
460         }
461 
462         mLeftSystemBarView = mCarSystemBarController.getLeftBar(isDeviceSetupForUser());
463         if (mLeftSystemBarView != null) {
464             mSystemBarConfigs.insetSystemBar(LEFT, mLeftSystemBarView);
465             mHvacController.registerHvacViews(mLeftSystemBarView);
466             mLeftSystemBarWindow.addView(mLeftSystemBarView);
467         }
468 
469         mRightSystemBarView = mCarSystemBarController.getRightBar(isDeviceSetupForUser());
470         if (mRightSystemBarView != null) {
471             mSystemBarConfigs.insetSystemBar(RIGHT, mRightSystemBarView);
472             mHvacController.registerHvacViews(mRightSystemBarView);
473             mRightSystemBarWindow.addView(mRightSystemBarView);
474         }
475     }
476 
attachNavBarWindows()477     private void attachNavBarWindows() {
478         mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);
479     }
480 
481     @VisibleForTesting
getSystemBarWindowBySide(int side)482     ViewGroup getSystemBarWindowBySide(int side) {
483         switch (side) {
484             case TOP:
485                 return mTopSystemBarWindow;
486             case BOTTOM:
487                 return mBottomSystemBarWindow;
488             case LEFT:
489                 return mLeftSystemBarWindow;
490             case RIGHT:
491                 return mRightSystemBarWindow;
492             default:
493                 return null;
494         }
495     }
496 
attachNavBarBySide(int side)497     private void attachNavBarBySide(int side) {
498         switch (side) {
499             case TOP:
500                 if (DEBUG) {
501                     Log.d(TAG, "mTopSystemBarWindow = " + mTopSystemBarWindow
502                             + ", mTopSystemBarAttached=" + mTopSystemBarAttached
503                             + ", enabled=" + mSystemBarConfigs.getEnabledStatusBySide(TOP));
504                 }
505                 if (mTopSystemBarWindow != null && !mTopSystemBarAttached
506                         && mSystemBarConfigs.getEnabledStatusBySide(TOP)) {
507                     mWindowManager.addView(mTopSystemBarWindow,
508                             mSystemBarConfigs.getLayoutParamsBySide(TOP));
509                     mTopSystemBarAttached = true;
510                 }
511                 break;
512             case BOTTOM:
513                 if (DEBUG) {
514                     Log.d(TAG, "mBottomSystemBarWindow = " + mBottomSystemBarWindow
515                             + ", mBottomSystemBarAttached=" + mBottomSystemBarAttached
516                             + ", enabled=" + mSystemBarConfigs.getEnabledStatusBySide(BOTTOM));
517                 }
518                 if (mBottomSystemBarWindow != null && !mBottomSystemBarAttached
519                         && mSystemBarConfigs.getEnabledStatusBySide(BOTTOM)) {
520                     mWindowManager.addView(mBottomSystemBarWindow,
521                             mSystemBarConfigs.getLayoutParamsBySide(BOTTOM));
522                     mBottomSystemBarAttached = true;
523                 }
524                 break;
525             case LEFT:
526                 if (DEBUG) {
527                     Log.d(TAG, "mLeftSystemBarWindow = " + mLeftSystemBarWindow
528                             + ", mLeftSystemBarAttached=" + mLeftSystemBarAttached
529                             + ", enabled=" + mSystemBarConfigs.getEnabledStatusBySide(LEFT));
530                 }
531                 if (mLeftSystemBarWindow != null && !mLeftSystemBarAttached
532                         && mSystemBarConfigs.getEnabledStatusBySide(LEFT)) {
533                     mWindowManager.addView(mLeftSystemBarWindow,
534                             mSystemBarConfigs.getLayoutParamsBySide(LEFT));
535                     mLeftSystemBarAttached = true;
536                 }
537                 break;
538             case RIGHT:
539                 if (DEBUG) {
540                     Log.d(TAG, "mRightSystemBarWindow = " + mRightSystemBarWindow
541                             + ", mRightSystemBarAttached=" + mRightSystemBarAttached
542                             + ", "
543                             + "enabled=" + mSystemBarConfigs.getEnabledStatusBySide(RIGHT));
544                 }
545                 if (mRightSystemBarWindow != null && !mRightSystemBarAttached
546                         && mSystemBarConfigs.getEnabledStatusBySide(RIGHT)) {
547                     mWindowManager.addView(mRightSystemBarWindow,
548                             mSystemBarConfigs.getLayoutParamsBySide(RIGHT));
549                     mRightSystemBarAttached = true;
550                 }
551                 break;
552             default:
553                 return;
554         }
555     }
556 
557     /**
558      * We register for soft keyboard visibility events such that we can hide the navigation bar
559      * giving more screen space to the IME. Note: this is optional and controlled by
560      * {@code com.android.internal.R.bool.config_hideNavBarForKeyboard}.
561      */
562     @Override
setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)563     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
564             boolean showImeSwitcher) {
565         if (mContext.getDisplayId() != displayId) {
566             return;
567         }
568 
569         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
570 
571         updateKeyboardVisibility(isKeyboardVisible);
572     }
573 
updateKeyboardVisibility(boolean isKeyboardVisible)574     private void updateKeyboardVisibility(boolean isKeyboardVisible) {
575         if (mHideTopBarForKeyboard) {
576             mCarSystemBarController.setTopWindowVisibility(
577                     isKeyboardVisible ? View.GONE : View.VISIBLE);
578         }
579 
580         if (mHideBottomBarForKeyboard) {
581             mCarSystemBarController.setBottomWindowVisibility(
582                     isKeyboardVisible ? View.GONE : View.VISIBLE);
583         }
584 
585         if (mHideLeftBarForKeyboard) {
586             mCarSystemBarController.setLeftWindowVisibility(
587                     isKeyboardVisible ? View.GONE : View.VISIBLE);
588         }
589         if (mHideRightBarForKeyboard) {
590             mCarSystemBarController.setRightWindowVisibility(
591                     isKeyboardVisible ? View.GONE : View.VISIBLE);
592         }
593     }
594 
595     @Override
onSystemBarAttributesChanged( int displayId, @WindowInsetsController.Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @WindowInsetsController.Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName, LetterboxDetails[] letterboxDetails)596     public void onSystemBarAttributesChanged(
597             int displayId,
598             @WindowInsetsController.Appearance int appearance,
599             AppearanceRegion[] appearanceRegions,
600             boolean navbarColorManagedByIme,
601             @WindowInsetsController.Behavior int behavior,
602             @InsetsType int requestedVisibleTypes,
603             String packageName,
604             LetterboxDetails[] letterboxDetails) {
605         if (displayId != mDisplayId) {
606             return;
607         }
608         boolean barModeChanged = updateStatusBarMode(
609                 mStatusBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT);
610         int numStacks = appearanceRegions.length;
611         boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
612         for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
613             stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
614         }
615         if (stackAppearancesChanged || barModeChanged) {
616             mAppearanceRegions = appearanceRegions;
617             updateStatusBarAppearance();
618         }
619         mCarSystemBarController.refreshSystemBar();
620     }
621 
622     @Override
disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2, boolean animate)623     public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
624             boolean animate) {
625         if (displayId != mDisplayId) {
626             return;
627         }
628         mCarSystemBarController.setSystemBarStates(state1, state2);
629     }
630 
updateStatusBarAppearance()631     private void updateStatusBarAppearance() {
632         int numStacks = mAppearanceRegions.length;
633         final ArrayList<Rect> lightBarBounds = new ArrayList<>();
634 
635         for (int i = 0; i < numStacks; i++) {
636             final AppearanceRegion ar = mAppearanceRegions[i];
637             if (isLight(ar.getAppearance())) {
638                 lightBarBounds.add(ar.getBounds());
639             }
640         }
641 
642         // If all stacks are light, all icons become dark.
643         if (lightBarBounds.size() == numStacks) {
644             mStatusBarIconController.setIconsDarkArea(null);
645             mStatusBarIconController.getTransitionsController().setIconsDark(
646                     /* dark= */ true, /* animate= */ false);
647         } else if (lightBarBounds.isEmpty()) {
648             // If no one is light, all icons become white.
649             mStatusBarIconController.getTransitionsController().setIconsDark(
650                     /* dark= */ false, /* animate= */ false);
651         } else {
652             // Not the same for every stack, update icons in area only.
653             mStatusBarIconController.setIconsDarkArea(lightBarBounds);
654             mStatusBarIconController.getTransitionsController().setIconsDark(
655                     /* dark= */ true, /* animate= */ false);
656         }
657     }
658 
isLight(int appearance)659     private static boolean isLight(int appearance) {
660         return (appearance & APPEARANCE_LIGHT_STATUS_BARS) != 0;
661     }
662 
663     @Override
showTransient(int displayId, int types, boolean isGestureOnSystemBar)664     public void showTransient(int displayId, int types, boolean isGestureOnSystemBar) {
665         if (displayId != mDisplayId) {
666             return;
667         }
668         if ((types & WindowInsets.Type.statusBars()) != 0) {
669             if (!mStatusBarTransientShown) {
670                 mStatusBarTransientShown = true;
671                 handleTransientChanged();
672             }
673         }
674         if ((types & WindowInsets.Type.navigationBars()) != 0) {
675             if (!mNavBarTransientShown) {
676                 mNavBarTransientShown = true;
677                 handleTransientChanged();
678             }
679         }
680     }
681 
682     @Override
abortTransient(int displayId, int types)683     public void abortTransient(int displayId, int types) {
684         if (displayId != mDisplayId) {
685             return;
686         }
687         if ((types & (WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars())) == 0) {
688             return;
689         }
690         clearTransient();
691     }
692 
clearTransient()693     private void clearTransient() {
694         if (mStatusBarTransientShown) {
695             mStatusBarTransientShown = false;
696             handleTransientChanged();
697         }
698         if (mNavBarTransientShown) {
699             mNavBarTransientShown = false;
700             handleTransientChanged();
701         }
702     }
703 
704     @VisibleForTesting
isStatusBarTransientShown()705     boolean isStatusBarTransientShown() {
706         return mStatusBarTransientShown;
707     }
708 
709     @VisibleForTesting
isNavBarTransientShown()710     boolean isNavBarTransientShown() {
711         return mNavBarTransientShown;
712     }
713 
714     @VisibleForTesting
setSignalPolicy(StatusBarSignalPolicy signalPolicy)715     void setSignalPolicy(StatusBarSignalPolicy signalPolicy) {
716         mSignalPolicy = signalPolicy;
717     }
718 
719     @Override
dump(PrintWriter pw, String[] args)720     public void dump(PrintWriter pw, String[] args) {
721         pw.print("  mTaskStackListener=");
722         pw.println(mButtonSelectionStateListener);
723         pw.print("  mBottomSystemBarView=");
724         pw.println(mBottomSystemBarView);
725     }
726 
handleTransientChanged()727     private void handleTransientChanged() {
728         updateStatusBarMode(mStatusBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT);
729         updateNavBarMode(mNavBarTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT);
730     }
731 
732     // Returns true if the status bar mode has changed.
updateStatusBarMode(int barMode)733     private boolean updateStatusBarMode(int barMode) {
734         if (mStatusBarMode != barMode) {
735             mStatusBarMode = barMode;
736             mAutoHideController.touchAutoHide();
737             return true;
738         }
739         return false;
740     }
741 
742     // Returns true if the nav bar mode has changed.
updateNavBarMode(int barMode)743     private boolean updateNavBarMode(int barMode) {
744         if (mSystemBarMode != barMode) {
745             mSystemBarMode = barMode;
746             mAutoHideController.touchAutoHide();
747             return true;
748         }
749         return false;
750     }
751 
752     @Override
onConfigChanged(Configuration newConfig)753     public void onConfigChanged(Configuration newConfig) {
754         Locale oldLocale = mCurrentLocale;
755         mCurrentLocale = newConfig.getLocales().get(0);
756 
757         boolean isConfigNightMode = newConfig.isNightModeActive();
758         if (isConfigNightMode == mIsUiModeNight
759                 && (mCurrentLocale != null && mCurrentLocale.equals(oldLocale)
760                 || mCurrentLocale == oldLocale)) {
761             return;
762         }
763 
764         // Refresh UI on Night mode or system language changes.
765         if (isConfigNightMode != mIsUiModeNight) {
766             mIsUiModeNight = isConfigNightMode;
767             mUiModeManager.setNightModeActivated(mIsUiModeNight);
768         }
769 
770         // cache the current state
771         // The focused view will be destroyed during re-layout, causing the framework to adjust
772         // the focus unexpectedly. To avoid that, move focus to a view that won't be
773         // destroyed during re-layout and has no focus highlight (the FocusParkingView), then
774         // move focus back to the previously focused view after re-layout.
775         mCarSystemBarController.cacheAndHideFocus();
776         View profilePickerView = null;
777         boolean isProfilePickerOpen = false;
778         if (mTopSystemBarView != null) {
779             profilePickerView = mTopSystemBarView.findViewById(R.id.user_name);
780         }
781         if (profilePickerView != null) isProfilePickerOpen = profilePickerView.isSelected();
782         if (isProfilePickerOpen) {
783             profilePickerView.callOnClick();
784         }
785 
786         resetSystemBarContent(/* isProvisionedStateChange= */ false);
787 
788         // retrieve the previous state
789         if (isProfilePickerOpen) {
790             if (mTopSystemBarView != null) {
791                 profilePickerView = mTopSystemBarView.findViewById(R.id.user_name);
792             }
793             if (profilePickerView != null) profilePickerView.callOnClick();
794         }
795 
796         mCarSystemBarController.restoreFocus();
797     }
798 
799     @VisibleForTesting
restartSystemBars()800     void restartSystemBars() {
801         mCarSystemBarRestartTracker.notifyPendingRestart(/* recreateWindows= */ true,
802                 /* provisionedStateChanged= */ false);
803 
804         mCarSystemBarController.removeAll();
805         mCarSystemBarController.resetSystemBarConfigs();
806         clearSystemBarWindow(/* removeUnusedWindow= */ true);
807         buildNavBarWindows();
808         buildNavBarContent();
809         attachNavBarWindows();
810 
811         mCarSystemBarRestartTracker.notifyRestartComplete(/* recreateWindows= */ true,
812                 /* provisionedStateChanged= */ false);
813     }
814 
clearSystemBarWindow(boolean removeUnusedWindow)815     private void clearSystemBarWindow(boolean removeUnusedWindow) {
816         if (mTopSystemBarWindow != null) {
817             mTopSystemBarWindow.removeAllViews();
818             mHvacController.unregisterViews(mTopSystemBarView);
819             if (removeUnusedWindow) {
820                 mWindowManager.removeViewImmediate(mTopSystemBarWindow);
821                 mTopSystemBarAttached = false;
822             }
823             mTopSystemBarView = null;
824         }
825 
826         if (mBottomSystemBarWindow != null) {
827             mBottomSystemBarWindow.removeAllViews();
828             mHvacController.unregisterViews(mBottomSystemBarView);
829             if (removeUnusedWindow) {
830                 mWindowManager.removeViewImmediate(mBottomSystemBarWindow);
831                 mBottomSystemBarAttached = false;
832             }
833             mBottomSystemBarView = null;
834         }
835 
836         if (mLeftSystemBarWindow != null) {
837             mLeftSystemBarWindow.removeAllViews();
838             mHvacController.unregisterViews(mLeftSystemBarView);
839             if (removeUnusedWindow) {
840                 mWindowManager.removeViewImmediate(mLeftSystemBarWindow);
841                 mLeftSystemBarAttached = false;
842             }
843             mLeftSystemBarView = null;
844         }
845 
846         if (mRightSystemBarWindow != null) {
847             mRightSystemBarWindow.removeAllViews();
848             mHvacController.unregisterViews(mRightSystemBarView);
849             if (removeUnusedWindow) {
850                 mWindowManager.removeViewImmediate(mRightSystemBarWindow);
851                 mRightSystemBarAttached = false;
852             }
853             mRightSystemBarView = null;
854         }
855     }
856 
857     @VisibleForTesting
setUiModeManager(UiModeManager uiModeManager)858     void setUiModeManager(UiModeManager uiModeManager) {
859         mUiModeManager = uiModeManager;
860     }
861 
862     @Override
onKeyboardVisibilityChanged(boolean show)863     public void onKeyboardVisibilityChanged(boolean show) {
864         updateKeyboardVisibility(show);
865     }
866 }
867