• 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.IntDef;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.util.AttributeSet;
23 import android.util.Log;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.LinearLayout;
28 
29 import com.android.systemui.R;
30 import com.android.systemui.car.hvac.HvacPanelOverlayViewController;
31 import com.android.systemui.car.statusicon.ui.QuickControlsEntryPointsController;
32 import com.android.systemui.car.statusicon.ui.ReadOnlyIconsController;
33 import com.android.systemui.car.systembar.CarSystemBarController.HvacPanelController;
34 import com.android.systemui.car.systembar.CarSystemBarController.NotificationsShadeController;
35 import com.android.systemui.settings.UserTracker;
36 
37 import java.lang.annotation.ElementType;
38 import java.lang.annotation.Target;
39 import java.util.Set;
40 
41 /**
42  * A custom system bar for the automotive use case.
43  * <p>
44  * The system bar in the automotive use case is more like a list of shortcuts, rendered
45  * in a linear layout.
46  */
47 public class CarSystemBarView extends LinearLayout {
48 
49     @IntDef(value = {BUTTON_TYPE_NAVIGATION, BUTTON_TYPE_KEYGUARD, BUTTON_TYPE_OCCLUSION})
50     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
51     private @interface ButtonsType {
52     }
53 
54     private static final String TAG = CarSystemBarView.class.getSimpleName();
55     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
56 
57     public static final int BUTTON_TYPE_NAVIGATION = 0;
58     public static final int BUTTON_TYPE_KEYGUARD = 1;
59     public static final int BUTTON_TYPE_OCCLUSION = 2;
60 
61     private final boolean mConsumeTouchWhenPanelOpen;
62     private final boolean mButtonsDraggable;
63 
64     private CarSystemBarButton mHomeButton;
65     private CarSystemBarButton mPassengerHomeButton;
66     private View mNavButtons;
67     private CarSystemBarButton mNotificationsButton;
68     private HvacButton mHvacButton;
69     private NotificationsShadeController mNotificationsShadeController;
70     private HvacPanelController mHvacPanelController;
71     private View mLockScreenButtons;
72     private View mOcclusionButtons;
73     private ViewGroup mQcEntryPointsContainer;
74     private ViewGroup mReadOnlyIconsContainer;
75     // used to wire in open/close gestures for overlay panels
76     private Set<OnTouchListener> mStatusBarWindowTouchListeners;
77     private HvacPanelOverlayViewController mHvacPanelOverlayViewController;
78     private CarSystemBarButton mControlCenterButton;
79 
CarSystemBarView(Context context, AttributeSet attrs)80     public CarSystemBarView(Context context, AttributeSet attrs) {
81         super(context, attrs);
82         mConsumeTouchWhenPanelOpen = getResources().getBoolean(
83                 R.bool.config_consumeSystemBarTouchWhenNotificationPanelOpen);
84         mButtonsDraggable = getResources().getBoolean(R.bool.config_systemBarButtonsDraggable);
85     }
86 
87     @Override
onFinishInflate()88     public void onFinishInflate() {
89         mHomeButton = findViewById(R.id.home);
90         mPassengerHomeButton = findViewById(R.id.passenger_home);
91         mNavButtons = findViewById(R.id.nav_buttons);
92         mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons);
93         mOcclusionButtons = findViewById(R.id.occlusion_buttons);
94         mNotificationsButton = findViewById(R.id.notifications);
95         mHvacButton = findViewById(R.id.hvac);
96         mQcEntryPointsContainer = findViewById(R.id.qc_entry_points_container);
97         mReadOnlyIconsContainer = findViewById(R.id.read_only_icons_container);
98         mControlCenterButton = findViewById(R.id.control_center_nav);
99         if (mNotificationsButton != null) {
100             mNotificationsButton.setOnClickListener(this::onNotificationsClick);
101         }
102         if (mHvacButton != null) {
103             mHvacButton.setOnClickListener(this::onHvacClick);
104         }
105         // Needs to be clickable so that it will receive ACTION_MOVE events.
106         setClickable(true);
107         // Needs to not be focusable so rotary won't highlight the entire nav bar.
108         setFocusable(false);
109     }
110 
updateHomeButtonVisibility(boolean isPassenger)111     void updateHomeButtonVisibility(boolean isPassenger) {
112         if (!isPassenger) {
113             return;
114         }
115         if (mPassengerHomeButton != null) {
116             if (mHomeButton != null) {
117                 mHomeButton.setVisibility(GONE);
118             }
119             mPassengerHomeButton.setVisibility(VISIBLE);
120         }
121     }
122 
setupHvacButton()123     void setupHvacButton() {
124         if (mHvacButton != null) {
125             mHvacButton.setOnClickListener(this::onHvacClick);
126         }
127     }
128 
setupQuickControlsEntryPoints( QuickControlsEntryPointsController quickControlsEntryPointsController, boolean isSetUp)129     void setupQuickControlsEntryPoints(
130             QuickControlsEntryPointsController quickControlsEntryPointsController,
131             boolean isSetUp) {
132         if (mQcEntryPointsContainer != null) {
133             quickControlsEntryPointsController.addIconViews(mQcEntryPointsContainer, isSetUp);
134         }
135     }
136 
setupReadOnlyIcons(ReadOnlyIconsController readOnlyIconsController)137     void setupReadOnlyIcons(ReadOnlyIconsController readOnlyIconsController) {
138         if (mReadOnlyIconsContainer != null) {
139             readOnlyIconsController.addIconViews(mReadOnlyIconsContainer,
140                     /* shouldAttachPanel= */false);
141         }
142     }
143 
setupSystemBarButtons(UserTracker userTracker)144     void setupSystemBarButtons(UserTracker userTracker) {
145         setupSystemBarButtons(this, userTracker);
146     }
147 
setupSystemBarButtons(View v, UserTracker userTracker)148     private void setupSystemBarButtons(View v, UserTracker userTracker) {
149         if (v instanceof CarSystemBarButton) {
150             ((CarSystemBarButton) v).setUserTracker(userTracker);
151         } else if (v instanceof ViewGroup) {
152             ViewGroup viewGroup = (ViewGroup) v;
153             for (int i = 0; i < viewGroup.getChildCount(); i++) {
154                 setupSystemBarButtons(viewGroup.getChildAt(i), userTracker);
155             }
156         }
157     }
158 
updateControlCenterButtonVisibility(boolean isMumd)159     void updateControlCenterButtonVisibility(boolean isMumd) {
160         if (mControlCenterButton != null) {
161             mControlCenterButton.setVisibility(isMumd ? VISIBLE : GONE);
162         }
163     }
164 
165     // Used to forward touch events even if the touch was initiated from a child component
166     @Override
onInterceptTouchEvent(MotionEvent ev)167     public boolean onInterceptTouchEvent(MotionEvent ev) {
168         if (mStatusBarWindowTouchListeners != null && !mStatusBarWindowTouchListeners.isEmpty()) {
169             if (!mButtonsDraggable) {
170                 return false;
171             }
172             boolean shouldConsumeEvent = mNotificationsShadeController == null ? false
173                     : mNotificationsShadeController.isNotificationPanelOpen();
174 
175             // Forward touch events to the status bar window so it can drag
176             // windows if required (ex. Notification shade)
177             triggerAllTouchListeners(this, ev);
178 
179             if (mConsumeTouchWhenPanelOpen && shouldConsumeEvent) {
180                 return true;
181             }
182         }
183         return super.onInterceptTouchEvent(ev);
184     }
185 
186     /** Sets the notifications panel controller. */
setNotificationsPanelController(NotificationsShadeController controller)187     public void setNotificationsPanelController(NotificationsShadeController controller) {
188         mNotificationsShadeController = controller;
189     }
190 
191     /** Sets the HVAC panel controller. */
setHvacPanelController(HvacPanelController controller)192     public void setHvacPanelController(HvacPanelController controller) {
193         mHvacPanelController = controller;
194     }
195 
196     /** Gets the notifications panel controller. */
getNotificationsPanelController()197     public NotificationsShadeController getNotificationsPanelController() {
198         return mNotificationsShadeController;
199     }
200 
201     /** Gets the HVAC panel controller. */
getHvacPanelController()202     public HvacPanelController getHvacPanelController() {
203         return mHvacPanelController;
204     }
205 
206     /**
207      * Sets the touch listeners that will be called from onInterceptTouchEvent and onTouchEvent
208      *
209      * @param statusBarWindowTouchListeners List of listeners to call from touch and intercept touch
210      */
setStatusBarWindowTouchListeners( Set<OnTouchListener> statusBarWindowTouchListeners)211     public void setStatusBarWindowTouchListeners(
212             Set<OnTouchListener> statusBarWindowTouchListeners) {
213         mStatusBarWindowTouchListeners = statusBarWindowTouchListeners;
214     }
215 
216     /** Gets the touch listeners that will be called from onInterceptTouchEvent and onTouchEvent. */
getStatusBarWindowTouchListeners()217     public Set<OnTouchListener> getStatusBarWindowTouchListeners() {
218         return mStatusBarWindowTouchListeners;
219     }
220 
221     @Override
onTouchEvent(MotionEvent event)222     public boolean onTouchEvent(MotionEvent event) {
223         triggerAllTouchListeners(this, event);
224         return super.onTouchEvent(event);
225     }
226 
onNotificationsClick(View v)227     protected void onNotificationsClick(View v) {
228         if (mNotificationsButton != null
229                 && mNotificationsButton.getDisabled()) {
230             mNotificationsButton.runOnClickWhileDisabled();
231             return;
232         }
233         if (mNotificationsShadeController != null) {
234             // If the notification shade is about to open, close the hvac panel
235             if (!mNotificationsShadeController.isNotificationPanelOpen()
236                     && mHvacPanelController != null
237                     && mHvacPanelController.isHvacPanelOpen()) {
238                 mHvacPanelController.togglePanel();
239             }
240             mNotificationsShadeController.togglePanel();
241         }
242     }
243 
onHvacClick(View v)244     protected void onHvacClick(View v) {
245         if (mHvacPanelController != null) {
246             // If the hvac panel is about to open, close the notification shade
247             if (!mHvacPanelController.isHvacPanelOpen()
248                     && mNotificationsShadeController != null
249                     && mNotificationsShadeController.isNotificationPanelOpen()) {
250                 mNotificationsShadeController.togglePanel();
251             }
252             mHvacPanelController.togglePanel();
253         }
254     }
255 
256     /**
257      * Shows buttons of the specified {@link ButtonsType}.
258      *
259      * NOTE: Only one type of buttons can be shown at a time, so showing buttons of one type will
260      * hide all buttons of other types.
261      *
262      * @param buttonsType
263      */
showButtonsOfType(@uttonsType int buttonsType)264     public void showButtonsOfType(@ButtonsType int buttonsType) {
265         switch(buttonsType) {
266             case BUTTON_TYPE_NAVIGATION:
267                 setNavigationButtonsVisibility(View.VISIBLE);
268                 setKeyguardButtonsVisibility(View.GONE);
269                 setOcclusionButtonsVisibility(View.GONE);
270                 break;
271             case BUTTON_TYPE_KEYGUARD:
272                 setNavigationButtonsVisibility(View.GONE);
273                 setKeyguardButtonsVisibility(View.VISIBLE);
274                 setOcclusionButtonsVisibility(View.GONE);
275                 break;
276             case BUTTON_TYPE_OCCLUSION:
277                 setNavigationButtonsVisibility(View.GONE);
278                 setKeyguardButtonsVisibility(View.GONE);
279                 setOcclusionButtonsVisibility(View.VISIBLE);
280                 break;
281         }
282     }
283 
284     /**
285      * Sets the system bar view's disabled state and runnable when disabled.
286      */
setDisabledSystemBarButton(int viewId, boolean disabled, Runnable runnable, @Nullable String buttonName)287     public void setDisabledSystemBarButton(int viewId, boolean disabled, Runnable runnable,
288                 @Nullable String buttonName) {
289         CarSystemBarButton button = findViewById(viewId);
290         if (button != null) {
291             if (DEBUG) {
292                 Log.d(TAG, "setDisabledSystemBarButton for: " + buttonName + " to: " + disabled);
293             }
294             button.setDisabled(disabled, runnable);
295         }
296     }
297 
298     /**
299      * Sets the system bar specific View container's visibility. ViewName is used just for
300      * debugging.
301      */
setVisibilityByViewId(int viewId, @Nullable String viewName, @View.Visibility int visibility)302     public void setVisibilityByViewId(int viewId, @Nullable String viewName,
303                 @View.Visibility int visibility) {
304         View v = findViewById(viewId);
305         if (v != null) {
306             if (DEBUG) Log.d(TAG, "setVisibilityByViewId for: " + viewName + " to: " + visibility);
307             v.setVisibility(visibility);
308         }
309     }
310 
311     /**
312      * Sets the HvacPanelOverlayViewController and adds HVAC button listeners
313      */
registerHvacPanelOverlayViewController(HvacPanelOverlayViewController controller)314     public void registerHvacPanelOverlayViewController(HvacPanelOverlayViewController controller) {
315         mHvacPanelOverlayViewController = controller;
316         if (mHvacPanelOverlayViewController != null && mHvacButton != null) {
317             mHvacPanelOverlayViewController.registerViewStateListener(mHvacButton);
318         }
319     }
320 
setNavigationButtonsVisibility(@iew.Visibility int visibility)321     private void setNavigationButtonsVisibility(@View.Visibility int visibility) {
322         if (mNavButtons != null) {
323             mNavButtons.setVisibility(visibility);
324         }
325     }
326 
setKeyguardButtonsVisibility(@iew.Visibility int visibility)327     private void setKeyguardButtonsVisibility(@View.Visibility int visibility) {
328         if (mLockScreenButtons != null) {
329             mLockScreenButtons.setVisibility(visibility);
330         }
331     }
332 
setOcclusionButtonsVisibility(@iew.Visibility int visibility)333     private void setOcclusionButtonsVisibility(@View.Visibility int visibility) {
334         if (mOcclusionButtons != null) {
335             mOcclusionButtons.setVisibility(visibility);
336         }
337     }
338 
triggerAllTouchListeners(View view, MotionEvent event)339     private void triggerAllTouchListeners(View view, MotionEvent event) {
340         if (mStatusBarWindowTouchListeners == null) {
341             return;
342         }
343         for (OnTouchListener listener : mStatusBarWindowTouchListeners) {
344             listener.onTouch(view, event);
345         }
346     }
347 
348     /**
349      * Toggles the notification unseen indicator on/off.
350      *
351      * @param hasUnseen true if the unseen notification count is great than 0.
352      */
toggleNotificationUnseenIndicator(Boolean hasUnseen)353     public void toggleNotificationUnseenIndicator(Boolean hasUnseen) {
354         if (mNotificationsButton == null) return;
355 
356         mNotificationsButton.setUnseen(hasUnseen);
357     }
358 }
359