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.content.Context; 21 import android.util.AttributeSet; 22 import android.view.MotionEvent; 23 import android.view.View; 24 import android.widget.LinearLayout; 25 26 import com.android.systemui.R; 27 import com.android.systemui.car.systembar.CarSystemBarController.NotificationsShadeController; 28 import com.android.systemui.statusbar.FeatureFlags; 29 import com.android.systemui.statusbar.phone.StatusBarIconController; 30 31 import java.lang.annotation.ElementType; 32 import java.lang.annotation.Target; 33 34 /** 35 * A custom system bar for the automotive use case. 36 * <p> 37 * The system bar in the automotive use case is more like a list of shortcuts, rendered 38 * in a linear layout. 39 */ 40 public class CarSystemBarView extends LinearLayout { 41 42 @IntDef(value = {BUTTON_TYPE_NAVIGATION, BUTTON_TYPE_KEYGUARD, BUTTON_TYPE_OCCLUSION}) 43 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 44 private @interface ButtonsType { 45 } 46 47 public static final int BUTTON_TYPE_NAVIGATION = 0; 48 public static final int BUTTON_TYPE_KEYGUARD = 1; 49 public static final int BUTTON_TYPE_OCCLUSION = 2; 50 51 private final boolean mConsumeTouchWhenPanelOpen; 52 private final boolean mButtonsDraggable; 53 54 private View mNavButtons; 55 private CarSystemBarButton mNotificationsButton; 56 private NotificationsShadeController mNotificationsShadeController; 57 private View mLockScreenButtons; 58 private View mOcclusionButtons; 59 // used to wire in open/close gestures for notifications 60 private OnTouchListener mStatusBarWindowTouchListener; 61 CarSystemBarView(Context context, AttributeSet attrs)62 public CarSystemBarView(Context context, AttributeSet attrs) { 63 super(context, attrs); 64 mConsumeTouchWhenPanelOpen = getResources().getBoolean( 65 R.bool.config_consumeSystemBarTouchWhenNotificationPanelOpen); 66 mButtonsDraggable = getResources().getBoolean(R.bool.config_systemBarButtonsDraggable); 67 } 68 69 @Override onFinishInflate()70 public void onFinishInflate() { 71 mNavButtons = findViewById(R.id.nav_buttons); 72 mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons); 73 mOcclusionButtons = findViewById(R.id.occlusion_buttons); 74 mNotificationsButton = findViewById(R.id.notifications); 75 if (mNotificationsButton != null) { 76 mNotificationsButton.setOnClickListener(this::onNotificationsClick); 77 } 78 // Needs to be clickable so that it will receive ACTION_MOVE events. 79 setClickable(true); 80 // Needs to not be focusable so rotary won't highlight the entire nav bar. 81 setFocusable(false); 82 } 83 setupIconController(FeatureFlags featureFlags, StatusBarIconController iconController)84 void setupIconController(FeatureFlags featureFlags, StatusBarIconController iconController) { 85 View mStatusIcons = findViewById(R.id.statusIcons); 86 if (mStatusIcons != null) { 87 // Attach the controllers for Status icons such as wifi and bluetooth if the standard 88 // container is in the view. 89 StatusBarIconController.DarkIconManager mDarkIconManager = 90 new StatusBarIconController.DarkIconManager( 91 mStatusIcons.findViewById(R.id.statusIcons), featureFlags); 92 mDarkIconManager.setShouldLog(true); 93 iconController.addIconGroup(mDarkIconManager); 94 } 95 } 96 97 // Used to forward touch events even if the touch was initiated from a child component 98 @Override onInterceptTouchEvent(MotionEvent ev)99 public boolean onInterceptTouchEvent(MotionEvent ev) { 100 if (mStatusBarWindowTouchListener != null) { 101 if (!mButtonsDraggable) { 102 return false; 103 } 104 boolean shouldConsumeEvent = mNotificationsShadeController == null ? false 105 : mNotificationsShadeController.isNotificationPanelOpen(); 106 107 // Forward touch events to the status bar window so it can drag 108 // windows if required (Notification shade) 109 mStatusBarWindowTouchListener.onTouch(this, ev); 110 111 if (mConsumeTouchWhenPanelOpen && shouldConsumeEvent) { 112 return true; 113 } 114 } 115 return super.onInterceptTouchEvent(ev); 116 } 117 118 /** Sets the notifications panel controller. */ setNotificationsPanelController(NotificationsShadeController controller)119 public void setNotificationsPanelController(NotificationsShadeController controller) { 120 mNotificationsShadeController = controller; 121 } 122 123 /** Gets the notifications panel controller. */ getNotificationsPanelController()124 public NotificationsShadeController getNotificationsPanelController() { 125 return mNotificationsShadeController; 126 } 127 128 /** 129 * Sets a touch listener that will be called from onInterceptTouchEvent and onTouchEvent 130 * 131 * @param statusBarWindowTouchListener The listener to call from touch and intercept touch 132 */ setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener)133 public void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) { 134 mStatusBarWindowTouchListener = statusBarWindowTouchListener; 135 } 136 137 /** Gets the touch listener that will be called from onInterceptTouchEvent and onTouchEvent. */ getStatusBarWindowTouchListener()138 public OnTouchListener getStatusBarWindowTouchListener() { 139 return mStatusBarWindowTouchListener; 140 } 141 142 @Override onTouchEvent(MotionEvent event)143 public boolean onTouchEvent(MotionEvent event) { 144 if (mStatusBarWindowTouchListener != null) { 145 mStatusBarWindowTouchListener.onTouch(this, event); 146 } 147 return super.onTouchEvent(event); 148 } 149 onNotificationsClick(View v)150 protected void onNotificationsClick(View v) { 151 if (mNotificationsShadeController != null) { 152 mNotificationsShadeController.togglePanel(); 153 } 154 } 155 156 /** 157 * Shows buttons of the specified {@link ButtonsType}. 158 * 159 * NOTE: Only one type of buttons can be shown at a time, so showing buttons of one type will 160 * hide all buttons of other types. 161 * 162 * @param buttonsType 163 */ showButtonsOfType(@uttonsType int buttonsType)164 public void showButtonsOfType(@ButtonsType int buttonsType) { 165 switch(buttonsType) { 166 case BUTTON_TYPE_NAVIGATION: 167 setNavigationButtonsVisibility(View.VISIBLE); 168 setKeyguardButtonsVisibility(View.GONE); 169 setOcclusionButtonsVisibility(View.GONE); 170 break; 171 case BUTTON_TYPE_KEYGUARD: 172 setNavigationButtonsVisibility(View.GONE); 173 setKeyguardButtonsVisibility(View.VISIBLE); 174 setOcclusionButtonsVisibility(View.GONE); 175 break; 176 case BUTTON_TYPE_OCCLUSION: 177 setNavigationButtonsVisibility(View.GONE); 178 setKeyguardButtonsVisibility(View.GONE); 179 setOcclusionButtonsVisibility(View.VISIBLE); 180 break; 181 } 182 } 183 setNavigationButtonsVisibility(int visibility)184 private void setNavigationButtonsVisibility(int visibility) { 185 if (mNavButtons != null) { 186 mNavButtons.setVisibility(visibility); 187 } 188 } 189 setKeyguardButtonsVisibility(int visibility)190 private void setKeyguardButtonsVisibility(int visibility) { 191 if (mLockScreenButtons != null) { 192 mLockScreenButtons.setVisibility(visibility); 193 } 194 } 195 setOcclusionButtonsVisibility(int visibility)196 private void setOcclusionButtonsVisibility(int visibility) { 197 if (mOcclusionButtons != null) { 198 mOcclusionButtons.setVisibility(visibility); 199 } 200 } 201 202 /** 203 * Toggles the notification unseen indicator on/off. 204 * 205 * @param hasUnseen true if the unseen notification count is great than 0. 206 */ toggleNotificationUnseenIndicator(Boolean hasUnseen)207 public void toggleNotificationUnseenIndicator(Boolean hasUnseen) { 208 if (mNotificationsButton == null) return; 209 210 mNotificationsButton.setUnseen(hasUnseen); 211 } 212 } 213