1 /* 2 * Copyright (C) 2015 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.statusbar.car; 18 19 import android.content.Context; 20 import android.graphics.Rect; 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.Dependency; 27 import com.android.systemui.R; 28 import com.android.systemui.statusbar.phone.StatusBarIconController; 29 30 /** 31 * A custom navigation bar for the automotive use case. 32 * <p> 33 * The navigation bar in the automotive use case is more like a list of shortcuts, rendered 34 * in a linear layout. 35 */ 36 class CarNavigationBarView extends LinearLayout { 37 private View mNavButtons; 38 private CarNavigationButton mNotificationsButton; 39 private CarStatusBar mCarStatusBar; 40 private Context mContext; 41 private View mLockScreenButtons; 42 // used to wire in open/close gestures for notifications 43 private OnTouchListener mStatusBarWindowTouchListener; 44 45 CarNavigationBarView(Context context, AttributeSet attrs)46 public CarNavigationBarView(Context context, AttributeSet attrs) { 47 super(context, attrs); 48 mContext = context; 49 } 50 51 @Override onFinishInflate()52 public void onFinishInflate() { 53 mNavButtons = findViewById(R.id.nav_buttons); 54 mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons); 55 56 mNotificationsButton = findViewById(R.id.notifications); 57 if (mNotificationsButton != null) { 58 mNotificationsButton.setOnClickListener(this::onNotificationsClick); 59 } 60 View mStatusIcons = findViewById(R.id.statusIcons); 61 if (mStatusIcons != null) { 62 // Attach the controllers for Status icons such as wifi and bluetooth if the standard 63 // container is in the view. 64 StatusBarIconController.DarkIconManager mDarkIconManager = 65 new StatusBarIconController.DarkIconManager( 66 mStatusIcons.findViewById(R.id.statusIcons)); 67 mDarkIconManager.setShouldLog(true); 68 Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); 69 } 70 // needs to be clickable so that it will receive ACTION_MOVE events 71 setClickable(true); 72 } 73 74 // Used to forward touch events even if the touch was initiated from a child component 75 @Override onInterceptTouchEvent(MotionEvent ev)76 public boolean onInterceptTouchEvent(MotionEvent ev) { 77 if (mStatusBarWindowTouchListener != null) { 78 boolean shouldConsumeEvent = shouldConsumeNotificationButtonEvent(ev); 79 // Forward touch events to the status bar window so it can drag 80 // windows if required (Notification shade) 81 mStatusBarWindowTouchListener.onTouch(this, ev); 82 // return true if child views should not receive this event. 83 if (shouldConsumeEvent) { 84 return true; 85 } 86 } 87 return super.onInterceptTouchEvent(ev); 88 } 89 90 /** 91 * If the motion event is over top of the notification button while the notification 92 * panel is open, we need the statusbar touch listeners handle the event instead of the button. 93 * Since the statusbar listener will trigger a close of the notification panel before the 94 * any button click events are fired this will prevent reopening the panel. 95 * 96 * Note: we can't use requestDisallowInterceptTouchEvent because the gesture detector will 97 * always receive the ACTION_DOWN and thus think a longpress happened if no other events are 98 * received 99 * 100 * @return true if the notification button should not receive the event 101 */ shouldConsumeNotificationButtonEvent(MotionEvent ev)102 private boolean shouldConsumeNotificationButtonEvent(MotionEvent ev) { 103 if (mNotificationsButton == null || !mCarStatusBar.isNotificationPanelOpen()) { 104 return false; 105 } 106 Rect notificationButtonLocation = new Rect(); 107 mNotificationsButton.getHitRect(notificationButtonLocation); 108 return notificationButtonLocation.contains((int) ev.getX(), (int) ev.getY()); 109 } 110 111 setStatusBar(CarStatusBar carStatusBar)112 void setStatusBar(CarStatusBar carStatusBar) { 113 mCarStatusBar = carStatusBar; 114 } 115 116 /** 117 * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent 118 * 119 * @param statusBarWindowTouchListener The listener to call from touch and intercept touch 120 */ setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener)121 void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) { 122 mStatusBarWindowTouchListener = statusBarWindowTouchListener; 123 } 124 125 @Override onTouchEvent(MotionEvent event)126 public boolean onTouchEvent(MotionEvent event) { 127 if (mStatusBarWindowTouchListener != null) { 128 mStatusBarWindowTouchListener.onTouch(this, event); 129 } 130 return super.onTouchEvent(event); 131 } 132 onNotificationsClick(View v)133 protected void onNotificationsClick(View v) { 134 mCarStatusBar.togglePanel(); 135 } 136 137 /** 138 * If there are buttons declared in the layout they will be shown and the normal 139 * Nav buttons will be hidden. 140 */ showKeyguardButtons()141 public void showKeyguardButtons() { 142 if (mLockScreenButtons == null) { 143 return; 144 } 145 mLockScreenButtons.setVisibility(View.VISIBLE); 146 mNavButtons.setVisibility(View.GONE); 147 } 148 149 /** 150 * If there are buttons declared in the layout they will be hidden and the normal 151 * Nav buttons will be shown. 152 */ hideKeyguardButtons()153 public void hideKeyguardButtons() { 154 if (mLockScreenButtons == null) return; 155 156 mNavButtons.setVisibility(View.VISIBLE); 157 mLockScreenButtons.setVisibility(View.GONE); 158 } 159 160 /** 161 * Toggles the notification unseen indicator on/off. 162 * 163 * @param hasUnseen true if the unseen notification count is great than 0. 164 */ toggleNotificationUnseenIndicator(Boolean hasUnseen)165 void toggleNotificationUnseenIndicator(Boolean hasUnseen) { 166 if (mNotificationsButton == null) return; 167 168 mNotificationsButton.setUnseen(hasUnseen); 169 } 170 } 171