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