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