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 com.android.systemui.car.systembar.SystemBarConfigs.BOTTOM;
20 import static com.android.systemui.car.systembar.SystemBarConfigs.LEFT;
21 import static com.android.systemui.car.systembar.SystemBarConfigs.RIGHT;
22 import static com.android.systemui.car.systembar.SystemBarConfigs.TOP;
23 
24 import android.annotation.LayoutRes;
25 import android.app.ActivityManager;
26 import android.app.StatusBarManager;
27 import android.content.Context;
28 import android.os.Build;
29 import android.util.ArraySet;
30 import android.util.Log;
31 import android.view.Gravity;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.Toast;
35 
36 import androidx.annotation.IdRes;
37 import androidx.annotation.Nullable;
38 
39 import com.android.car.ui.FocusParkingView;
40 import com.android.car.ui.utils.ViewUtils;
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.systemui.R;
43 import com.android.systemui.car.hvac.HvacPanelOverlayViewController;
44 import com.android.systemui.car.notification.NotificationPanelViewController;
45 import com.android.systemui.car.statusbar.UserNameViewController;
46 import com.android.systemui.car.statusicon.StatusIconPanelViewController;
47 import com.android.systemui.car.users.CarSystemUIUserUtil;
48 import com.android.systemui.dagger.SysUISingleton;
49 import com.android.systemui.settings.UserTracker;
50 
51 import dagger.Lazy;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.Set;
56 
57 import javax.inject.Provider;
58 
59 /** A single class which controls the navigation bar views. */
60 @SysUISingleton
61 public class CarSystemBarController {
62     private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG;
63 
64     private static final String TAG = CarSystemBarController.class.getSimpleName();
65 
66     private final Context mContext;
67     private final UserTracker mUserTracker;
68     private final CarSystemBarViewFactory mCarSystemBarViewFactory;
69     private final ButtonSelectionStateController mButtonSelectionStateController;
70     private final ButtonRoleHolderController mButtonRoleHolderController;
71     private final Provider<StatusIconPanelViewController.Builder> mPanelControllerBuilderProvider;
72     private final Lazy<UserNameViewController> mUserNameViewControllerLazy;
73     private final Lazy<MicPrivacyChipViewController> mMicPrivacyChipViewControllerLazy;
74     private final Lazy<CameraPrivacyChipViewController> mCameraPrivacyChipViewControllerLazy;
75 
76     private final SystemBarConfigs mSystemBarConfigs;
77     private boolean mShowTop;
78     private boolean mShowBottom;
79     private boolean mShowLeft;
80     private boolean mShowRight;
81     private final int mPrivacyChipXOffset;
82 
83     @IdRes
84     private int mTopFocusedViewId;
85     @IdRes
86     private int mBottomFocusedViewId;
87     @IdRes
88     private int mLeftFocusedViewId;
89     @IdRes
90     private int mRightFocusedViewId;
91 
92     private final Set<View.OnTouchListener> mTopBarTouchListeners = new ArraySet<>();
93     private final Set<View.OnTouchListener> mBottomBarTouchListeners = new ArraySet<>();
94     private final Set<View.OnTouchListener> mLeftBarTouchListeners = new ArraySet<>();
95     private final Set<View.OnTouchListener> mRightBarTouchListeners = new ArraySet<>();
96 
97     private NotificationsShadeController mNotificationsShadeController;
98     private HvacPanelController mHvacPanelController;
99     private StatusIconPanelViewController mMicPanelController;
100     private StatusIconPanelViewController mCameraPanelController;
101     private StatusIconPanelViewController mProfilePanelController;
102     private HvacPanelOverlayViewController mHvacPanelOverlayViewController;
103     private NotificationPanelViewController mNotificationPanelViewController;
104 
105     private CarSystemBarView mTopView;
106     private CarSystemBarView mBottomView;
107     private CarSystemBarView mLeftView;
108     private CarSystemBarView mRightView;
109 
110     // Saved StatusBarManager.DisableFlags
111     private int mStatusBarState;
112     // Saved StatusBarManager.Disable2Flags
113     private int mStatusBarState2;
114     private int mLockTaskMode;
115 
CarSystemBarController(Context context, UserTracker userTracker, CarSystemBarViewFactory carSystemBarViewFactory, ButtonSelectionStateController buttonSelectionStateController, Lazy<UserNameViewController> userNameViewControllerLazy, Lazy<MicPrivacyChipViewController> micPrivacyChipViewControllerLazy, Lazy<CameraPrivacyChipViewController> cameraPrivacyChipViewControllerLazy, ButtonRoleHolderController buttonRoleHolderController, SystemBarConfigs systemBarConfigs, Provider<StatusIconPanelViewController.Builder> panelControllerBuilderProvider)116     public CarSystemBarController(Context context,
117             UserTracker userTracker,
118             CarSystemBarViewFactory carSystemBarViewFactory,
119             ButtonSelectionStateController buttonSelectionStateController,
120             Lazy<UserNameViewController> userNameViewControllerLazy,
121             Lazy<MicPrivacyChipViewController> micPrivacyChipViewControllerLazy,
122             Lazy<CameraPrivacyChipViewController> cameraPrivacyChipViewControllerLazy,
123             ButtonRoleHolderController buttonRoleHolderController,
124             SystemBarConfigs systemBarConfigs,
125             Provider<StatusIconPanelViewController.Builder> panelControllerBuilderProvider) {
126         mContext = context;
127         mUserTracker = userTracker;
128         mCarSystemBarViewFactory = carSystemBarViewFactory;
129         mButtonSelectionStateController = buttonSelectionStateController;
130         mUserNameViewControllerLazy = userNameViewControllerLazy;
131         mMicPrivacyChipViewControllerLazy = micPrivacyChipViewControllerLazy;
132         mCameraPrivacyChipViewControllerLazy = cameraPrivacyChipViewControllerLazy;
133         mButtonRoleHolderController = buttonRoleHolderController;
134         mPanelControllerBuilderProvider = panelControllerBuilderProvider;
135         mSystemBarConfigs = systemBarConfigs;
136 
137         // Read configuration.
138         readConfigs();
139         mPrivacyChipXOffset = -context.getResources()
140                 .getDimensionPixelOffset(R.dimen.privacy_chip_horizontal_padding);
141     }
142 
readConfigs()143     private void readConfigs() {
144         mShowTop = mSystemBarConfigs.getEnabledStatusBySide(TOP);
145         mShowBottom = mSystemBarConfigs.getEnabledStatusBySide(BOTTOM);
146         mShowLeft = mSystemBarConfigs.getEnabledStatusBySide(LEFT);
147         mShowRight = mSystemBarConfigs.getEnabledStatusBySide(RIGHT);
148     }
149 
150     /**
151      * Hides all system bars.
152      */
hideBars()153     public void hideBars() {
154         setTopWindowVisibility(View.GONE);
155         setBottomWindowVisibility(View.GONE);
156         setLeftWindowVisibility(View.GONE);
157         setRightWindowVisibility(View.GONE);
158     }
159 
160     /**
161      * Shows all system bars.
162      */
showBars()163     public void showBars() {
164         setTopWindowVisibility(View.VISIBLE);
165         setBottomWindowVisibility(View.VISIBLE);
166         setLeftWindowVisibility(View.VISIBLE);
167         setRightWindowVisibility(View.VISIBLE);
168     }
169 
170     /** Clean up */
removeAll()171     public void removeAll() {
172         mButtonSelectionStateController.removeAll();
173         mButtonRoleHolderController.removeAll();
174         mUserNameViewControllerLazy.get().removeAll();
175         mMicPrivacyChipViewControllerLazy.get().removeAll();
176         mCameraPrivacyChipViewControllerLazy.get().removeAll();
177 
178         mMicPanelController = null;
179         mCameraPanelController = null;
180         mProfilePanelController = null;
181     }
182 
183     /** Gets the top window if configured to do so. */
184     @Nullable
getTopWindow()185     public ViewGroup getTopWindow() {
186         return mShowTop ? mCarSystemBarViewFactory.getTopWindow() : null;
187     }
188 
189     /** Gets the bottom window if configured to do so. */
190     @Nullable
getBottomWindow()191     public ViewGroup getBottomWindow() {
192         return mShowBottom ? mCarSystemBarViewFactory.getBottomWindow() : null;
193     }
194 
195     /** Gets the left window if configured to do so. */
196     @Nullable
getLeftWindow()197     public ViewGroup getLeftWindow() {
198         return mShowLeft ? mCarSystemBarViewFactory.getLeftWindow() : null;
199     }
200 
201     /** Gets the right window if configured to do so. */
202     @Nullable
getRightWindow()203     public ViewGroup getRightWindow() {
204         return mShowRight ? mCarSystemBarViewFactory.getRightWindow() : null;
205     }
206 
207     /** Toggles the top nav bar visibility. */
setTopWindowVisibility(@iew.Visibility int visibility)208     public boolean setTopWindowVisibility(@View.Visibility int visibility) {
209         return setWindowVisibility(getTopWindow(), visibility);
210     }
211 
212     /** Toggles the bottom nav bar visibility. */
setBottomWindowVisibility(@iew.Visibility int visibility)213     public boolean setBottomWindowVisibility(@View.Visibility int visibility) {
214         return setWindowVisibility(getBottomWindow(), visibility);
215     }
216 
217     /** Toggles the left nav bar visibility. */
setLeftWindowVisibility(@iew.Visibility int visibility)218     public boolean setLeftWindowVisibility(@View.Visibility int visibility) {
219         return setWindowVisibility(getLeftWindow(), visibility);
220     }
221 
222     /** Toggles the right nav bar visibility. */
setRightWindowVisibility(@iew.Visibility int visibility)223     public boolean setRightWindowVisibility(@View.Visibility int visibility) {
224         return setWindowVisibility(getRightWindow(), visibility);
225     }
226 
setWindowVisibility(ViewGroup window, @View.Visibility int visibility)227     private boolean setWindowVisibility(ViewGroup window, @View.Visibility int visibility) {
228         if (window == null) {
229             return false;
230         }
231 
232         if (window.getVisibility() == visibility) {
233             return false;
234         }
235 
236         window.setVisibility(visibility);
237         return true;
238     }
239 
240     /**
241      * Sets the system bar states - {@code StatusBarManager.DisableFlags},
242      * {@code StatusBarManager.Disable2Flags}, lock task mode. When there is a change in state,
243      * and refreshes the system bars.
244      *
245      * @param state {@code StatusBarManager.DisableFlags}
246      * @param state2 {@code StatusBarManager.Disable2Flags}
247      */
setSystemBarStates(int state, int state2)248     public void setSystemBarStates(int state, int state2) {
249         int diff = (state ^ mStatusBarState) | (state2 ^ mStatusBarState2);
250         int lockTaskMode = getLockTaskModeState();
251         if (diff == 0 && mLockTaskMode == lockTaskMode) {
252             if (DEBUG) {
253                 Log.d(TAG, "setSystemBarStates(): status bar states unchanged: state: "
254                         + state + " state2: " +  state2 + " lockTaskMode: " + mLockTaskMode);
255             }
256             return;
257         }
258         mStatusBarState = state;
259         mStatusBarState2 = state2;
260         mLockTaskMode = lockTaskMode;
261         refreshSystemBar();
262     }
263 
264     @VisibleForTesting
getStatusBarState()265     protected int getStatusBarState() {
266         return mStatusBarState;
267     }
268 
269     @VisibleForTesting
getStatusBarState2()270     protected int getStatusBarState2() {
271         return mStatusBarState2;
272     }
273 
274     @VisibleForTesting
getLockTaskMode()275     protected int getLockTaskMode() {
276         return mLockTaskMode;
277     }
278 
279     /**
280      * Refreshes system bar views and sets the visibility of certain components based on
281      * {@link StatusBarManager} flags and lock task mode.
282      * <ul>
283      * <li>Home button will be disabled when {@code StatusBarManager.DISABLE_HOME} is set.
284      * <li>Phone call button will be disable in lock task mode.
285      * <li>App grid button will be disable when {@code StatusBarManager.DISABLE_HOME} is set.
286      * <li>Notification button will be disable when
287      * {@code StatusBarManager.DISABLE_NOTIFICATION_ICONS} is set.
288      * <li>Quick settings and user switcher will be hidden when in lock task mode or when
289      * {@code StatusBarManager.DISABLE2_QUICK_SETTINGS} is set.
290      * </ul>
291      */
refreshSystemBar()292     public void refreshSystemBar() {
293         boolean homeDisabled = ((mStatusBarState & StatusBarManager.DISABLE_HOME) > 0);
294         boolean notificationDisabled =
295                 ((mStatusBarState & StatusBarManager.DISABLE_NOTIFICATION_ICONS) > 0);
296         boolean locked = (mLockTaskMode == ActivityManager.LOCK_TASK_MODE_LOCKED);
297         boolean qcDisabled =
298                 ((mStatusBarState2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) > 0) || locked;
299         boolean systemIconsDisabled =
300                 ((mStatusBarState2 & StatusBarManager.DISABLE2_SYSTEM_ICONS) > 0) || locked;
301 
302         setDisabledSystemBarButton(R.id.home, homeDisabled, "home");
303         setDisabledSystemBarButton(R.id.passenger_home, homeDisabled, "passenger_home");
304         setDisabledSystemBarButton(R.id.phone_nav, locked, "phone_nav");
305         setDisabledSystemBarButton(R.id.grid_nav, homeDisabled, "grid_nav");
306         setDisabledSystemBarButton(R.id.notifications, notificationDisabled, "notifications");
307 
308         setDisabledSystemBarContainer(R.id.user_name_container, qcDisabled,
309                 "user_name_container");
310 
311         if (DEBUG) {
312             Log.d(TAG, "refreshSystemBar: locked?: " + locked
313                     + " homeDisabled: " + homeDisabled
314                     + " notificationDisabled: " + notificationDisabled
315                     + " qcDisabled: " + qcDisabled
316                     + " systemIconsDisabled: " + systemIconsDisabled);
317         }
318     }
319 
getLockTaskModeState()320     private int getLockTaskModeState() {
321         return mContext.getSystemService(ActivityManager.class).getLockTaskModeState();
322     }
323 
setDisabledSystemBarButton(int viewId, boolean disabled, @Nullable String buttonName)324     private void setDisabledSystemBarButton(int viewId, boolean disabled,
325                 @Nullable String buttonName) {
326         for (CarSystemBarView barView : getAllAvailableSystemBarViews()) {
327             barView.setDisabledSystemBarButton(viewId, disabled,
328                     () -> showAdminSupportDetailsDialog(), buttonName);
329         }
330     }
331 
setDisabledSystemBarContainer(int viewId, boolean disabled, @Nullable String viewName)332     private void setDisabledSystemBarContainer(int viewId, boolean disabled,
333                 @Nullable String viewName) {
334         for (CarSystemBarView barView : getAllAvailableSystemBarViews()) {
335             barView.setVisibilityByViewId(viewId, viewName,
336                     disabled ? View.INVISIBLE : View.VISIBLE);
337         }
338     }
339 
showAdminSupportDetailsDialog()340     private void showAdminSupportDetailsDialog() {
341         // TODO(b/205891123): launch AdminSupportDetailsDialog after moving
342         // AdminSupportDetailsDialog out of CarSettings since CarSettings is not and should not
343         // be allowlisted for lock task mode.
344         Toast.makeText(mContext, "This action is unavailable for your profile",
345                 Toast.LENGTH_LONG).show();
346     }
347 
348     /** Gets the top navigation bar with the appropriate listeners set. */
349     @Nullable
getTopBar(boolean isSetUp)350     public CarSystemBarView getTopBar(boolean isSetUp) {
351         if (!mShowTop) {
352             return null;
353         }
354 
355         mTopView = mCarSystemBarViewFactory.getTopBar(isSetUp);
356         setupBar(mTopView, mTopBarTouchListeners, mNotificationsShadeController,
357                 mHvacPanelController, mHvacPanelOverlayViewController,
358                 mNotificationPanelViewController);
359 
360         if (isSetUp) {
361             // We do not want the privacy chips or the profile picker to be clickable in
362             // unprovisioned mode.
363             mMicPanelController = setupSensorQcPanel(mMicPanelController, R.id.mic_privacy_chip,
364                     R.layout.qc_mic_panel);
365             mCameraPanelController = setupSensorQcPanel(mCameraPanelController,
366                     R.id.camera_privacy_chip, R.layout.qc_camera_panel);
367             setupProfilePanel();
368         }
369 
370         return mTopView;
371     }
372 
373     /** Gets the bottom navigation bar with the appropriate listeners set. */
374     @Nullable
getBottomBar(boolean isSetUp)375     public CarSystemBarView getBottomBar(boolean isSetUp) {
376         if (!mShowBottom) {
377             return null;
378         }
379 
380         mBottomView = mCarSystemBarViewFactory.getBottomBar(isSetUp);
381         setupBar(mBottomView, mBottomBarTouchListeners, mNotificationsShadeController,
382                 mHvacPanelController, mHvacPanelOverlayViewController,
383                 mNotificationPanelViewController);
384 
385         return mBottomView;
386     }
387 
388     /** Gets the left navigation bar with the appropriate listeners set. */
389     @Nullable
getLeftBar(boolean isSetUp)390     public CarSystemBarView getLeftBar(boolean isSetUp) {
391         if (!mShowLeft) {
392             return null;
393         }
394 
395         mLeftView = mCarSystemBarViewFactory.getLeftBar(isSetUp);
396         setupBar(mLeftView, mLeftBarTouchListeners, mNotificationsShadeController,
397                 mHvacPanelController, mHvacPanelOverlayViewController,
398                 mNotificationPanelViewController);
399         return mLeftView;
400     }
401 
402     /** Gets the right navigation bar with the appropriate listeners set. */
403     @Nullable
getRightBar(boolean isSetUp)404     public CarSystemBarView getRightBar(boolean isSetUp) {
405         if (!mShowRight) {
406             return null;
407         }
408 
409         mRightView = mCarSystemBarViewFactory.getRightBar(isSetUp);
410         setupBar(mRightView, mRightBarTouchListeners, mNotificationsShadeController,
411                 mHvacPanelController, mHvacPanelOverlayViewController,
412                 mNotificationPanelViewController);
413         return mRightView;
414     }
415 
setupBar(CarSystemBarView view, Set<View.OnTouchListener> statusBarTouchListeners, NotificationsShadeController notifShadeController, HvacPanelController hvacPanelController, HvacPanelOverlayViewController hvacPanelOverlayViewController, NotificationPanelViewController notificationPanelViewController)416     private void setupBar(CarSystemBarView view, Set<View.OnTouchListener> statusBarTouchListeners,
417             NotificationsShadeController notifShadeController,
418             HvacPanelController hvacPanelController,
419             HvacPanelOverlayViewController hvacPanelOverlayViewController,
420             NotificationPanelViewController notificationPanelViewController) {
421         view.updateHomeButtonVisibility(CarSystemUIUserUtil.isSecondaryMUMDSystemUI());
422         view.setStatusBarWindowTouchListeners(statusBarTouchListeners);
423         view.setNotificationsPanelController(notifShadeController);
424         view.registerNotificationPanelViewController(notificationPanelViewController);
425         view.setHvacPanelController(hvacPanelController);
426         view.registerHvacPanelOverlayViewController(hvacPanelOverlayViewController);
427         view.updateControlCenterButtonVisibility(CarSystemUIUserUtil.isMUMDSystemUI());
428         mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
429         mButtonRoleHolderController.addAllButtonsWithRoleName(view);
430         mUserNameViewControllerLazy.get().addUserNameView(view);
431         mMicPrivacyChipViewControllerLazy.get().addPrivacyChipView(view);
432         mCameraPrivacyChipViewControllerLazy.get().addPrivacyChipView(view);
433     }
434 
setupSensorQcPanel( @ullable StatusIconPanelViewController panelController, int chipId, @LayoutRes int panelLayoutRes)435     private StatusIconPanelViewController setupSensorQcPanel(
436             @Nullable StatusIconPanelViewController panelController, int chipId,
437             @LayoutRes int panelLayoutRes) {
438         if (panelController == null) {
439             View privacyChip = mTopView.findViewById(chipId);
440             if (privacyChip != null) {
441                 panelController = mPanelControllerBuilderProvider.get()
442                         .setXOffset(mPrivacyChipXOffset)
443                         .setGravity(Gravity.TOP | Gravity.END)
444                         .build(privacyChip, panelLayoutRes, R.dimen.car_sensor_qc_panel_width);
445                 panelController.init();
446             }
447         }
448         return panelController;
449     }
450 
setupProfilePanel()451     private void setupProfilePanel() {
452         View profilePickerView = mTopView.findViewById(R.id.user_name);
453         if (mProfilePanelController == null && profilePickerView != null) {
454             boolean profilePanelDisabledWhileDriving = mContext.getResources().getBoolean(
455                     R.bool.config_profile_panel_disabled_while_driving);
456             mProfilePanelController = mPanelControllerBuilderProvider.get()
457                     .setGravity(Gravity.TOP | Gravity.END)
458                     .setDisabledWhileDriving(profilePanelDisabledWhileDriving)
459                     .build(profilePickerView, R.layout.qc_profile_switcher,
460                             R.dimen.car_profile_quick_controls_panel_width);
461             mProfilePanelController.init();
462         }
463     }
464 
465     /** Sets a touch listener for the top navigation bar. */
registerTopBarTouchListener(View.OnTouchListener listener)466     public void registerTopBarTouchListener(View.OnTouchListener listener) {
467         boolean setModified = mTopBarTouchListeners.add(listener);
468         if (setModified && mTopView != null) {
469             mTopView.setStatusBarWindowTouchListeners(mTopBarTouchListeners);
470         }
471     }
472 
473     /** Sets a touch listener for the bottom navigation bar. */
registerBottomBarTouchListener(View.OnTouchListener listener)474     public void registerBottomBarTouchListener(View.OnTouchListener listener) {
475         boolean setModified = mBottomBarTouchListeners.add(listener);
476         if (setModified && mBottomView != null) {
477             mBottomView.setStatusBarWindowTouchListeners(mBottomBarTouchListeners);
478         }
479     }
480 
481     /** Sets a touch listener for the left navigation bar. */
registerLeftBarTouchListener(View.OnTouchListener listener)482     public void registerLeftBarTouchListener(View.OnTouchListener listener) {
483         boolean setModified = mLeftBarTouchListeners.add(listener);
484         if (setModified && mLeftView != null) {
485             mLeftView.setStatusBarWindowTouchListeners(mLeftBarTouchListeners);
486         }
487     }
488 
489     /** Sets a touch listener for the right navigation bar. */
registerRightBarTouchListener(View.OnTouchListener listener)490     public void registerRightBarTouchListener(View.OnTouchListener listener) {
491         boolean setModified = mRightBarTouchListeners.add(listener);
492         if (setModified && mRightView != null) {
493             mRightView.setStatusBarWindowTouchListeners(mRightBarTouchListeners);
494         }
495     }
496 
497     /** Sets a notification controller which toggles the notification panel. */
registerNotificationController( NotificationsShadeController notificationsShadeController)498     public void registerNotificationController(
499             NotificationsShadeController notificationsShadeController) {
500         mNotificationsShadeController = notificationsShadeController;
501         if (mTopView != null) {
502             mTopView.setNotificationsPanelController(mNotificationsShadeController);
503         }
504         if (mBottomView != null) {
505             mBottomView.setNotificationsPanelController(mNotificationsShadeController);
506         }
507         if (mLeftView != null) {
508             mLeftView.setNotificationsPanelController(mNotificationsShadeController);
509         }
510         if (mRightView != null) {
511             mRightView.setNotificationsPanelController(mNotificationsShadeController);
512         }
513     }
514 
515     /** Sets the NotificationPanelViewController for views to listen to the panel's state. */
registerNotificationPanelViewController( NotificationPanelViewController notificationPanelViewController)516     public void registerNotificationPanelViewController(
517             NotificationPanelViewController notificationPanelViewController) {
518         mNotificationPanelViewController = notificationPanelViewController;
519         if (mTopView != null) {
520             mTopView.registerNotificationPanelViewController(mNotificationPanelViewController);
521         }
522         if (mBottomView != null) {
523             mBottomView.registerNotificationPanelViewController(mNotificationPanelViewController);
524         }
525         if (mLeftView != null) {
526             mLeftView.registerNotificationPanelViewController(mNotificationPanelViewController);
527         }
528         if (mRightView != null) {
529             mRightView.registerNotificationPanelViewController(mNotificationPanelViewController);
530         }
531     }
532 
533     /** Sets an HVAC controller which toggles the HVAC panel. */
registerHvacPanelController(HvacPanelController hvacPanelController)534     public void registerHvacPanelController(HvacPanelController hvacPanelController) {
535         mHvacPanelController = hvacPanelController;
536         if (mTopView != null) {
537             mTopView.setHvacPanelController(mHvacPanelController);
538         }
539         if (mBottomView != null) {
540             mBottomView.setHvacPanelController(mHvacPanelController);
541         }
542         if (mLeftView != null) {
543             mLeftView.setHvacPanelController(mHvacPanelController);
544         }
545         if (mRightView != null) {
546             mRightView.setHvacPanelController(mHvacPanelController);
547         }
548     }
549 
550     /** Sets the HVACPanelOverlayViewController for views to listen to the panel's state. */
registerHvacPanelOverlayViewController( HvacPanelOverlayViewController hvacPanelOverlayViewController)551     public void registerHvacPanelOverlayViewController(
552             HvacPanelOverlayViewController hvacPanelOverlayViewController) {
553         mHvacPanelOverlayViewController = hvacPanelOverlayViewController;
554         if (mTopView != null) {
555             mTopView.registerHvacPanelOverlayViewController(mHvacPanelOverlayViewController);
556         }
557         if (mBottomView != null) {
558             mBottomView.registerHvacPanelOverlayViewController(mHvacPanelOverlayViewController);
559         }
560         if (mLeftView != null) {
561             mLeftView.registerHvacPanelOverlayViewController(mHvacPanelOverlayViewController);
562         }
563         if (mRightView != null) {
564             mRightView.registerHvacPanelOverlayViewController(mHvacPanelOverlayViewController);
565         }
566     }
567 
568     /**
569      * Shows all of the navigation buttons on the valid instances of {@link CarSystemBarView}.
570      */
showAllNavigationButtons(boolean isSetUp)571     public void showAllNavigationButtons(boolean isSetUp) {
572         checkAllBars(isSetUp);
573         if (mTopView != null) {
574             mTopView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_NAVIGATION);
575         }
576         if (mBottomView != null) {
577             mBottomView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_NAVIGATION);
578         }
579         if (mLeftView != null) {
580             mLeftView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_NAVIGATION);
581         }
582         if (mRightView != null) {
583             mRightView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_NAVIGATION);
584         }
585     }
586 
587     /**
588      * Shows all of the keyguard specific buttons on the valid instances of
589      * {@link CarSystemBarView}.
590      */
showAllKeyguardButtons(boolean isSetUp)591     public void showAllKeyguardButtons(boolean isSetUp) {
592         checkAllBars(isSetUp);
593         if (mTopView != null) {
594             mTopView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_KEYGUARD);
595         }
596         if (mBottomView != null) {
597             mBottomView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_KEYGUARD);
598         }
599         if (mLeftView != null) {
600             mLeftView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_KEYGUARD);
601         }
602         if (mRightView != null) {
603             mRightView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_KEYGUARD);
604         }
605     }
606 
607     /**
608      * Shows all of the occlusion state buttons on the valid instances of
609      * {@link CarSystemBarView}.
610      */
showAllOcclusionButtons(boolean isSetUp)611     public void showAllOcclusionButtons(boolean isSetUp) {
612         checkAllBars(isSetUp);
613         if (mTopView != null) {
614             mTopView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_OCCLUSION);
615         }
616         if (mBottomView != null) {
617             mBottomView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_OCCLUSION);
618         }
619         if (mLeftView != null) {
620             mLeftView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_OCCLUSION);
621         }
622         if (mRightView != null) {
623             mRightView.showButtonsOfType(CarSystemBarView.BUTTON_TYPE_OCCLUSION);
624         }
625     }
626 
627     /** Toggles whether the notifications icon has an unseen indicator or not. */
toggleAllNotificationsUnseenIndicator(boolean isSetUp, boolean hasUnseen)628     public void toggleAllNotificationsUnseenIndicator(boolean isSetUp, boolean hasUnseen) {
629         checkAllBars(isSetUp);
630         if (mTopView != null) {
631             mTopView.toggleNotificationUnseenIndicator(hasUnseen);
632         }
633         if (mBottomView != null) {
634             mBottomView.toggleNotificationUnseenIndicator(hasUnseen);
635         }
636         if (mLeftView != null) {
637             mLeftView.toggleNotificationUnseenIndicator(hasUnseen);
638         }
639         if (mRightView != null) {
640             mRightView.toggleNotificationUnseenIndicator(hasUnseen);
641         }
642     }
643 
644     /** Interface for controlling the notifications shade. */
645     public interface NotificationsShadeController {
646         /** Toggles the visibility of the notifications shade. */
togglePanel()647         void togglePanel();
648 
649         /** Returns {@code true} if the panel is open. */
isNotificationPanelOpen()650         boolean isNotificationPanelOpen();
651     }
652 
653     /** Interface for controlling the HVAC panel. */
654     public interface HvacPanelController {
655         /** Toggles the visibility of the HVAC shade. */
togglePanel()656         void togglePanel();
657 
658         /** Returns {@code true} if the panel is open. */
isHvacPanelOpen()659         boolean isHvacPanelOpen();
660     }
661 
checkAllBars(boolean isSetUp)662     private void checkAllBars(boolean isSetUp) {
663         mTopView = getTopBar(isSetUp);
664         mBottomView = getBottomBar(isSetUp);
665         mLeftView = getLeftBar(isSetUp);
666         mRightView = getRightBar(isSetUp);
667     }
668 
getAllAvailableSystemBarViews()669     private List<CarSystemBarView> getAllAvailableSystemBarViews() {
670         List<CarSystemBarView> barViews = new ArrayList<>();
671         if (mTopView != null) {
672             barViews.add(mTopView);
673         }
674         if (mBottomView != null) {
675             barViews.add(mBottomView);
676         }
677         if (mLeftView != null) {
678             barViews.add(mLeftView);
679         }
680         if (mRightView != null) {
681             barViews.add(mRightView);
682         }
683         return barViews;
684     }
685 
686     /** Resets the cached Views. */
resetViewCache()687     protected void resetViewCache() {
688         mCarSystemBarViewFactory.resetSystemBarViewCache();
689     }
690 
691     /**
692      * Invalidate SystemBarConfigs and fetch again from Resources.
693      * TODO(): b/260206944, Can remove this after we have a fix for overlaid resources not applied.
694      */
resetSystemBarConfigs()695     protected void resetSystemBarConfigs() {
696         mSystemBarConfigs.resetSystemBarConfigs();
697         mCarSystemBarViewFactory.resetSystemBarWindowCache();
698         readConfigs();
699     }
700 
701     /** Stores the ID of the View that is currently focused and hides the focus. */
cacheAndHideFocus()702     protected void cacheAndHideFocus() {
703         mTopFocusedViewId = cacheAndHideFocus(mTopView);
704         if (mTopFocusedViewId != View.NO_ID) return;
705         mBottomFocusedViewId = cacheAndHideFocus(mBottomView);
706         if (mBottomFocusedViewId != View.NO_ID) return;
707         mLeftFocusedViewId = cacheAndHideFocus(mLeftView);
708         if (mLeftFocusedViewId != View.NO_ID) return;
709         mRightFocusedViewId = cacheAndHideFocus(mRightView);
710     }
711 
712     @VisibleForTesting
cacheAndHideFocus(@ullable View rootView)713     int cacheAndHideFocus(@Nullable View rootView) {
714         if (rootView == null) return View.NO_ID;
715         View focusedView = rootView.findFocus();
716         if (focusedView == null || focusedView instanceof FocusParkingView) return View.NO_ID;
717         int focusedViewId = focusedView.getId();
718         ViewUtils.hideFocus(rootView);
719         return focusedViewId;
720     }
721 
722     /** Requests focus on the View that matches the cached ID. */
restoreFocus()723     protected void restoreFocus() {
724         if (restoreFocus(mTopView, mTopFocusedViewId)) return;
725         if (restoreFocus(mBottomView, mBottomFocusedViewId)) return;
726         if (restoreFocus(mLeftView, mLeftFocusedViewId)) return;
727         restoreFocus(mRightView, mRightFocusedViewId);
728     }
729 
restoreFocus(@ullable View rootView, @IdRes int viewToFocusId)730     private boolean restoreFocus(@Nullable View rootView, @IdRes int viewToFocusId) {
731         if (rootView == null || viewToFocusId == View.NO_ID) return false;
732         View focusedView = rootView.findViewById(viewToFocusId);
733         if (focusedView == null) return false;
734         focusedView.requestFocus();
735         return true;
736     }
737 }
738