blob: 44e8874f83869ba447f4997339011b0455ac20ee [file] [log] [blame]
Rakesh Iyer1186faa2015-12-07 16:48:46 -08001/*
Brad Stenning8d1a51c2018-11-20 17:34:16 -08002 * Copyright (C) 2018 The Android Open Source Project
Rakesh Iyer1186faa2015-12-07 16:48:46 -08003 *
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
17package com.android.systemui.statusbar.car;
18
Brad Stenning3b0d7642019-03-28 11:04:30 -070019import android.app.ActivityManager;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070020import android.app.ActivityTaskManager;
jovanakedba98c2018-09-14 15:46:24 -070021import android.car.drivingstate.CarDrivingStateEvent;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080022import android.graphics.PixelFormat;
Anthony Chene658cc22017-04-27 11:17:35 -070023import android.graphics.drawable.Drawable;
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080024import android.util.Log;
Brad Stenning3b0d7642019-03-28 11:04:30 -070025import android.view.GestureDetector;
Brad Stenning078235b2017-12-18 08:25:10 -080026import android.view.Gravity;
Brad Stenning3b0d7642019-03-28 11:04:30 -070027import android.view.MotionEvent;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080028import android.view.View;
Anthony Chen0801a5c2017-03-22 09:54:37 -070029import android.view.ViewGroup;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080030import android.view.ViewGroup.LayoutParams;
31import android.view.WindowManager;
Anthony Chene658cc22017-04-27 11:17:35 -070032
Brad Stenning3b0d7642019-03-28 11:04:30 -070033import androidx.annotation.NonNull;
34import androidx.recyclerview.widget.RecyclerView;
35
36import com.android.car.notification.CarNotificationListener;
37import com.android.car.notification.CarNotificationView;
38import com.android.car.notification.CarUxRestrictionManagerWrapper;
39import com.android.car.notification.NotificationClickHandlerFactory;
40import com.android.car.notification.NotificationViewController;
41import com.android.car.notification.PreprocessingManager;
Winson Chung2db35572017-10-09 15:08:30 -070042import com.android.keyguard.KeyguardUpdateMonitor;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070043import com.android.systemui.BatteryMeterView;
Jason Monk27d01a622018-12-10 15:57:09 -050044import com.android.systemui.CarSystemUIFactory;
Jason Monk9c7844c2017-01-18 15:21:53 -050045import com.android.systemui.Dependency;
Winson Chung2db35572017-10-09 15:08:30 -070046import com.android.systemui.Prefs;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080047import com.android.systemui.R;
Jason Monk27d01a622018-12-10 15:57:09 -050048import com.android.systemui.SystemUIFactory;
Winson Chung2db35572017-10-09 15:08:30 -070049import com.android.systemui.classifier.FalsingLog;
50import com.android.systemui.classifier.FalsingManager;
Jason Monkaa573e92017-01-27 17:00:29 -050051import com.android.systemui.fragments.FragmentHostManager;
Brad Stenning8d1a51c2018-11-20 17:34:16 -080052import com.android.systemui.plugins.qs.QS;
53import com.android.systemui.qs.car.CarQSFragment;
Winson Chungb05b3982017-11-01 18:02:43 -070054import com.android.systemui.shared.system.ActivityManagerWrapper;
Winson Chung67f5c8b2018-09-24 12:09:19 -070055import com.android.systemui.shared.system.TaskStackChangeListener;
Rakesh Iyer2790a372016-01-22 15:33:39 -080056import com.android.systemui.statusbar.StatusBarState;
Brad Stenning38b46f82018-03-27 13:57:29 -070057import com.android.systemui.statusbar.car.hvac.HvacController;
Brad Stenningf084d882018-08-08 13:34:21 -070058import com.android.systemui.statusbar.car.hvac.TemperatureView;
Jason Monkaa573e92017-01-27 17:00:29 -050059import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050060import com.android.systemui.statusbar.phone.StatusBar;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070061import com.android.systemui.statusbar.policy.BatteryController;
Brad Stenning224b5b32018-03-28 21:26:57 -070062import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Jason Monk9c7844c2017-01-18 15:21:53 -050063import com.android.systemui.statusbar.policy.UserSwitcherController;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080064
Qiming Shib9dfb922017-04-13 16:23:18 +080065import java.io.FileDescriptor;
66import java.io.PrintWriter;
67import java.util.Map;
davidln54739152018-07-02 13:25:44 -070068
Rakesh Iyer1186faa2015-12-07 16:48:46 -080069/**
70 * A status bar (and navigation bar) tailored for the automotive use case.
71 */
Jason Monk2a6ea9c2017-01-26 11:14:51 -050072public class CarStatusBar extends StatusBar implements
Anthony Chenda62fdcd52016-04-06 16:15:14 -070073 CarBatteryController.BatteryViewHandler {
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080074 private static final String TAG = "CarStatusBar";
75
Victor Chan1c6d0582016-01-09 16:26:37 -080076 private TaskStackListenerImpl mTaskStackListener;
Victor Chan1c6d0582016-01-09 16:26:37 -080077
Rakesh Iyer2790a372016-01-22 15:33:39 -080078 private FullscreenUserSwitcher mFullscreenUserSwitcher;
Victor Chan1c6d0582016-01-09 16:26:37 -080079
Anthony Chenda62fdcd52016-04-06 16:15:14 -070080 private CarBatteryController mCarBatteryController;
81 private BatteryMeterView mBatteryMeterView;
Anthony Chene658cc22017-04-27 11:17:35 -070082 private Drawable mNotificationPanelBackground;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070083
Scott Randolphf4085822017-06-27 15:23:01 -070084 private ViewGroup mNavigationBarWindow;
Brad Stenning078235b2017-12-18 08:25:10 -080085 private ViewGroup mLeftNavigationBarWindow;
86 private ViewGroup mRightNavigationBarWindow;
Jason Monk49fa0162017-01-11 09:21:56 -050087 private CarNavigationBarView mNavigationBarView;
Brad Stenning078235b2017-12-18 08:25:10 -080088 private CarNavigationBarView mLeftNavigationBarView;
89 private CarNavigationBarView mRightNavigationBarView;
Anthony Chen1c59e9f2016-04-11 11:05:48 -070090
Qiming Shib9dfb922017-04-13 16:23:18 +080091 private final Object mQueueLock = new Object();
Brad Stenning078235b2017-12-18 08:25:10 -080092 private boolean mShowLeft;
93 private boolean mShowRight;
94 private boolean mShowBottom;
95 private CarFacetButtonController mCarFacetButtonController;
Brad Stenning224b5b32018-03-28 21:26:57 -070096 private ActivityManagerWrapper mActivityManagerWrapper;
97 private DeviceProvisionedController mDeviceProvisionedController;
98 private boolean mDeviceIsProvisioned = true;
Brad Stenningf084d882018-08-08 13:34:21 -070099 private HvacController mHvacController;
jovanakedba98c2018-09-14 15:46:24 -0700100 private DrivingStateHelper mDrivingStateHelper;
101 private SwitchToGuestTimer mSwitchToGuestTimer;
Brad Stenning078235b2017-12-18 08:25:10 -0800102
Brad Stenning3b0d7642019-03-28 11:04:30 -0700103 // The container for the notifications.
104 private CarNotificationView mNotificationView;
105 private RecyclerView mNotificationList;
106 // The state of if the notification list is currently showing the bottom.
107 private boolean mNotificationListAtBottom;
108 // Was the notification list at the bottom when the user first touched the screen
109 private boolean mNotificationListAtBottomAtTimeOfTouch;
110 // To be attached to the navigation bars such that they can close the notification panel if
111 // it's open.
112 private View.OnTouchListener mNavBarNotificationTouchListener;
113
Victor Chan1c6d0582016-01-09 16:26:37 -0800114 @Override
115 public void start() {
Brad Stenningeb6051c2019-02-27 13:46:11 -0800116 // get the provisioned state before calling the parent class since it's that flow that
117 // builds the nav bar
118 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
119 mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
Victor Chan1c6d0582016-01-09 16:26:37 -0800120 super.start();
Jaewan Kim938a50b2016-03-14 17:35:43 +0900121 mTaskStackListener = new TaskStackListenerImpl();
Brad Stenning224b5b32018-03-28 21:26:57 -0700122 mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
123 mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700124
Jason Monke59dc402018-08-16 12:05:01 -0400125 mNotificationPanel.setScrollingEnabled(true);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800126
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800127 createBatteryController();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700128 mCarBatteryController.startListening();
Brad Stenning38b46f82018-03-27 13:57:29 -0700129
Brad Stenningf084d882018-08-08 13:34:21 -0700130 mHvacController.connectToCarService();
davidln54739152018-07-02 13:25:44 -0700131
Jason Monk27d01a622018-12-10 15:57:09 -0500132 CarSystemUIFactory factory = SystemUIFactory.getInstance();
Brad Stenning224b5b32018-03-28 21:26:57 -0700133 if (!mDeviceIsProvisioned) {
134 mDeviceProvisionedController.addCallback(
135 new DeviceProvisionedController.DeviceProvisionedListener() {
136 @Override
137 public void onDeviceProvisionedChanged() {
Brad Stenninga2d87d82019-01-24 12:33:10 -0800138 mHandler.post(() -> {
139 // on initial boot we are getting a call even though the value
140 // is the same so we are confirming the reset is needed
141 boolean deviceProvisioned =
142 mDeviceProvisionedController.isDeviceProvisioned();
143 if (mDeviceIsProvisioned != deviceProvisioned) {
144 mDeviceIsProvisioned = deviceProvisioned;
145 restartNavBars();
146 }
147 });
Brad Stenning224b5b32018-03-28 21:26:57 -0700148 }
149 });
150 }
jovanakedba98c2018-09-14 15:46:24 -0700151
152 // Register a listener for driving state changes.
153 mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
154 mDrivingStateHelper.connectToCarService();
155
156 mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700157 }
158
Brad Stenning224b5b32018-03-28 21:26:57 -0700159 /**
160 * Remove all content from navbars and rebuild them. Used to allow for different nav bars
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800161 * before and after the device is provisioned. . Also for change of density and font size.
Brad Stenning224b5b32018-03-28 21:26:57 -0700162 */
163 private void restartNavBars() {
Brad Stenningf084d882018-08-08 13:34:21 -0700164 // remove and reattach all hvac components such that we don't keep a reference to unused
165 // ui elements
166 mHvacController.removeAllComponents();
167 addTemperatureViewToController(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700168 mCarFacetButtonController.removeAll();
Brad Stenning224b5b32018-03-28 21:26:57 -0700169 if (mNavigationBarWindow != null) {
170 mNavigationBarWindow.removeAllViews();
171 mNavigationBarView = null;
172 }
173
174 if (mLeftNavigationBarWindow != null) {
175 mLeftNavigationBarWindow.removeAllViews();
176 mLeftNavigationBarView = null;
177 }
178
179 if (mRightNavigationBarWindow != null) {
180 mRightNavigationBarWindow.removeAllViews();
181 mRightNavigationBarView = null;
182 }
Brad Stenningf084d882018-08-08 13:34:21 -0700183
Brad Stenning224b5b32018-03-28 21:26:57 -0700184 buildNavBarContent();
Brad Stenning75221642019-03-13 07:33:24 -0700185 // If the UI was rebuilt (day/night change) while the keyguard was up we need to
186 // correctly respect that state.
187 if (mIsKeyguard) {
188 updateNavBarForKeyguardContent();
189 }
Brad Stenning4534f442019-04-01 21:43:32 -0700190 // CarFacetButtonController was reset therefore we need to re-add the status bar elements
191 // to the controller.
192 mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700193 }
194
Brad Stenningf084d882018-08-08 13:34:21 -0700195 private void addTemperatureViewToController(View v) {
196 if (v instanceof TemperatureView) {
Brad Stenningf084d882018-08-08 13:34:21 -0700197 mHvacController.addHvacTextView((TemperatureView) v);
198 } else if (v instanceof ViewGroup) {
199 ViewGroup viewGroup = (ViewGroup) v;
200 for (int i = 0; i < viewGroup.getChildCount(); i++) {
201 addTemperatureViewToController(viewGroup.getChildAt(i));
202 }
203 }
204 }
205
Brad Stenning7e411812018-04-13 22:52:39 -0700206 /**
207 * Allows for showing or hiding just the navigation bars. This is indented to be used when
208 * the full screen user selector is shown.
209 */
davidln54739152018-07-02 13:25:44 -0700210 void setNavBarVisibility(@View.Visibility int visibility) {
Brad Stenning7e411812018-04-13 22:52:39 -0700211 if (mNavigationBarWindow != null) {
212 mNavigationBarWindow.setVisibility(visibility);
213 }
214 if (mLeftNavigationBarWindow != null) {
215 mLeftNavigationBarWindow.setVisibility(visibility);
216 }
217 if (mRightNavigationBarWindow != null) {
218 mRightNavigationBarWindow.setVisibility(visibility);
219 }
220 }
221
222
223 @Override
224 public boolean hideKeyguard() {
225 boolean result = super.hideKeyguard();
226 if (mNavigationBarView != null) {
227 mNavigationBarView.hideKeyguardButtons();
228 }
229 if (mLeftNavigationBarView != null) {
230 mLeftNavigationBarView.hideKeyguardButtons();
231 }
232 if (mRightNavigationBarView != null) {
233 mRightNavigationBarView.hideKeyguardButtons();
234 }
235 return result;
236 }
237
238
239 @Override
240 public void showKeyguard() {
241 super.showKeyguard();
Brad Stenning75221642019-03-13 07:33:24 -0700242 updateNavBarForKeyguardContent();
243 }
244
245 /**
246 * Switch to the keyguard applicable content contained in the nav bars
247 */
248 private void updateNavBarForKeyguardContent() {
Brad Stenning7e411812018-04-13 22:52:39 -0700249 if (mNavigationBarView != null) {
250 mNavigationBarView.showKeyguardButtons();
251 }
252 if (mLeftNavigationBarView != null) {
253 mLeftNavigationBarView.showKeyguardButtons();
254 }
255 if (mRightNavigationBarView != null) {
256 mRightNavigationBarView.showKeyguardButtons();
257 }
258 }
Brad Stenning224b5b32018-03-28 21:26:57 -0700259
Brad Stenning224b5b32018-03-28 21:26:57 -0700260
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700261 @Override
Jason Monkaa573e92017-01-27 17:00:29 -0500262 protected void makeStatusBarView() {
263 super.makeStatusBarView();
Jason Monk27d01a622018-12-10 15:57:09 -0500264 mHvacController = new HvacController(mContext);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700265
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800266 CarSystemUIFactory factory = SystemUIFactory.getInstance();
267 mCarFacetButtonController = factory.getCarDependencyComponent()
268 .getCarFacetButtonController();
Anthony Chene658cc22017-04-27 11:17:35 -0700269 mNotificationPanelBackground = getDefaultWallpaper();
270 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
271
Jason Monkaa573e92017-01-27 17:00:29 -0500272 FragmentHostManager manager = FragmentHostManager.get(mStatusBarWindow);
273 manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
Anthony Chene658cc22017-04-27 11:17:35 -0700274 mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700275
Jason Monkaa573e92017-01-27 17:00:29 -0500276 // By default, the BatteryMeterView should not be visible. It will be toggled
277 // when a device has connected by bluetooth.
278 mBatteryMeterView.setVisibility(View.GONE);
Jason Monkaa573e92017-01-27 17:00:29 -0500279 });
Brad Stenning3b0d7642019-03-28 11:04:30 -0700280
281 connectNotificationsUI();
282 }
283
284 /**
285 * Attach the notification listeners and controllers to the UI as well as build all the
286 * touch listeners needed for opening and closing the notification panel
287 */
288 private void connectNotificationsUI() {
289 // Attached to the status bar to detect pull down of the notification shade.
290 GestureDetector openGestureDetector = new GestureDetector(mContext,
291 new OpenNotificationGestureListener() {
292 @Override
293 protected void openNotification() {
294 animateExpandNotificationsPanel();
295 }
296 });
297 // Attached to the notification ui to detect close request of the notification shade.
298 GestureDetector closeGestureDetector = new GestureDetector(mContext,
299 new CloseNotificationGestureListener() {
300 @Override
301 protected void close() {
302 animateCollapsePanels();
303 }
304 });
305 // Attached to the NavBars to close the notification shade
306 GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
307 new NavBarCloseNotificationGestureListener() {
308 @Override
309 protected void close() {
310 animateCollapsePanels();
311 }
312 });
313 mNavBarNotificationTouchListener =
314 (v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
315
Brad Stenningc622f1d2019-01-29 11:24:11 -0800316 // The following are the ui elements that the user would call the status bar.
317 // This will set the status bar so it they can make call backs.
318 CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
319 topBar.setStatusBar(this);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700320 topBar.setStatusBarWindowTouchListener((v1, event1) ->
321 openGestureDetector.onTouchEvent(event1));
322
323 NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
324 mBarService,
325 launchResult -> {
326 if (launchResult == ActivityManager.START_TASK_TO_FRONT
327 || launchResult == ActivityManager.START_SUCCESS) {
328 animateCollapsePanels();
329 }
330 });
331 CarNotificationListener carNotificationListener = new CarNotificationListener();
332 CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
333 new CarUxRestrictionManagerWrapper();
334 carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
335 clickHandlerFactory);
336
337 mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
338 View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
339 mNotificationView.setClickHandlerFactory(clickHandlerFactory);
340
341 // The glass pane is used to view touch events before passed to the notification list.
342 // This allows us to initialize gesture listeners and detect when to close the notifications
343 glassPane.setOnTouchListener((v, event) -> {
344 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
345 mNotificationListAtBottomAtTimeOfTouch = false;
346 }
347 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
348 mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
349 // Pass the down event to gesture detector so that it knows where the touch event
350 // started.
351 closeGestureDetector.onTouchEvent(event);
352 }
353 return false;
354 });
355 mNotificationList = mNotificationView.findViewById(R.id.notifications);
356 mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
357 @Override
358 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
359 super.onScrolled(recyclerView, dx, dy);
360 if (!mNotificationList.canScrollVertically(1)) {
361 mNotificationListAtBottom = true;
362 return;
363 }
364 mNotificationListAtBottom = false;
365 mNotificationListAtBottomAtTimeOfTouch = false;
366 }
367 });
368 mNotificationList.setOnTouchListener(new View.OnTouchListener() {
369 @Override
370 public boolean onTouch(View v, MotionEvent event) {
371 boolean handled = false;
372 if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
373 handled = closeGestureDetector.onTouchEvent(event);
374 }
375 // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
376 // the event has been passed to the closeGestureDetector above, such that the
377 // closeGestureDetector sees the up event before the state has changed.
378 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
379 mNotificationListAtBottomAtTimeOfTouch = false;
380 }
381 return handled;
382 }
383 });
384
385 NotificationViewController mNotificationViewController = new NotificationViewController(
386 mNotificationView,
387 PreprocessingManager.getInstance(mContext),
388 carNotificationListener,
389 carUxRestrictionManagerWrapper);
390 mNotificationViewController.enable();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700391 }
392
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800393 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -0700394 public void animateExpandNotificationsPanel() {
395 if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
396 return;
397 }
398 // scroll to top
399 mNotificationList.scrollToPosition(0);
400 mStatusBarWindowController.setPanelVisible(true);
401 mNotificationView.setVisibility(View.VISIBLE);
402 // let the status bar know that the panel is open
403 setPanelExpanded(true);
404 }
405
406 @Override
407 public void animateCollapsePanels(int flags, boolean force, boolean delayed,
408 float speedUpFactor) {
409 super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
410 if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
411 return;
412 }
413 mStatusBarWindowController.setStatusBarFocusable(false);
414 mStatusBarWindow.cancelExpandHelper();
415 mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
416 mStatusBarWindowController.setPanelVisible(false);
417 mNotificationView.setVisibility(View.INVISIBLE);
418 // let the status bar know that the panel is cloased
419 setPanelExpanded(false);
420 }
421
422
423 @Override
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800424 protected QS createDefaultQSFragment() {
425 return new CarQSFragment();
426 }
427
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800428 private BatteryController createBatteryController() {
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700429 mCarBatteryController = new CarBatteryController(mContext);
430 mCarBatteryController.addBatteryViewHandler(this);
431 return mCarBatteryController;
Victor Chan1c6d0582016-01-09 16:26:37 -0800432 }
433
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800434 @Override
Jason Monk49fa0162017-01-11 09:21:56 -0500435 protected void createNavigationBar() {
Brad Stenning078235b2017-12-18 08:25:10 -0800436 mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800437 mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800438 mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
439
Brad Stenning224b5b32018-03-28 21:26:57 -0700440 buildNavBarWindows();
441 buildNavBarContent();
442 attachNavBarWindows();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700443
Charles Chen10ca70b2018-11-28 00:03:38 +0800444 // There has been a car customized nav bar on the default display, so just create nav bars
445 // on external displays.
446 mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */);
Brad Stenning224b5b32018-03-28 21:26:57 -0700447 }
448
449 private void buildNavBarContent() {
450 if (mShowBottom) {
451 buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
452 R.layout.car_navigation_bar_unprovisioned);
453 }
454
455 if (mShowLeft) {
456 buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
457 R.layout.car_left_navigation_bar_unprovisioned);
458 }
459
Brad Stenning078235b2017-12-18 08:25:10 -0800460 if (mShowRight) {
Brad Stenning224b5b32018-03-28 21:26:57 -0700461 buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
462 R.layout.car_right_navigation_bar_unprovisioned);
463 }
464 }
465
466 private void buildNavBarWindows() {
467 if (mShowBottom) {
davidln54739152018-07-02 13:25:44 -0700468 mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
Brad Stenning224b5b32018-03-28 21:26:57 -0700469 R.layout.navigation_bar_window, null);
470 }
471 if (mShowLeft) {
472 mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
davidln54739152018-07-02 13:25:44 -0700473 R.layout.navigation_bar_window, null);
Brad Stenning224b5b32018-03-28 21:26:57 -0700474 }
475 if (mShowRight) {
476 mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
477 R.layout.navigation_bar_window, null);
Brad Stenning078235b2017-12-18 08:25:10 -0800478 }
479
480 }
481
Brad Stenning224b5b32018-03-28 21:26:57 -0700482 private void attachNavBarWindows() {
Brad Stenning078235b2017-12-18 08:25:10 -0800483
Brad Stenning224b5b32018-03-28 21:26:57 -0700484 if (mShowBottom) {
485 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
486 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
487 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
488 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
489 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
490 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
491 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
492 PixelFormat.TRANSLUCENT);
493 lp.setTitle("CarNavigationBar");
494 lp.windowAnimations = 0;
495 mWindowManager.addView(mNavigationBarWindow, lp);
496 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700497
Brad Stenning224b5b32018-03-28 21:26:57 -0700498 if (mShowLeft) {
499 int width = mContext.getResources().getDimensionPixelSize(
500 R.dimen.car_left_navigation_bar_width);
501 WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
502 width, LayoutParams.MATCH_PARENT,
503 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
504 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
505 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
506 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
507 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
508 PixelFormat.TRANSLUCENT);
509 leftlp.setTitle("LeftCarNavigationBar");
510 leftlp.windowAnimations = 0;
511 leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
512 leftlp.gravity = Gravity.LEFT;
513 mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
514 }
515 if (mShowRight) {
516 int width = mContext.getResources().getDimensionPixelSize(
517 R.dimen.car_right_navigation_bar_width);
518 WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
519 width, LayoutParams.MATCH_PARENT,
520 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
521 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
522 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
523 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
524 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
525 PixelFormat.TRANSLUCENT);
526 rightlp.setTitle("RightCarNavigationBar");
527 rightlp.windowAnimations = 0;
528 rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
529 rightlp.gravity = Gravity.RIGHT;
530 mWindowManager.addView(mRightNavigationBarWindow, rightlp);
531 }
532
533 }
534
535 private void buildBottomBar(int layout) {
Anthony Chen0801a5c2017-03-22 09:54:37 -0700536 // SystemUI requires that the navigation bar view have a parent. Since the regular
537 // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
538 // CarNavigationBarView.
Brad Stenning224b5b32018-03-28 21:26:57 -0700539 View.inflate(mContext, layout, mNavigationBarWindow);
Scott Randolphf4085822017-06-27 15:23:01 -0700540 mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
541 if (mNavigationBarView == null) {
542 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
Brad Stenning078235b2017-12-18 08:25:10 -0800543 throw new RuntimeException("Unable to build botom nav bar due to missing layout");
Scott Randolphf4085822017-06-27 15:23:01 -0700544 }
Brad Stenning078235b2017-12-18 08:25:10 -0800545 mNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700546 addTemperatureViewToController(mNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700547 mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800548 }
549
Brad Stenning224b5b32018-03-28 21:26:57 -0700550 private void buildLeft(int layout) {
551 View.inflate(mContext, layout, mLeftNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800552 mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
553 if (mLeftNavigationBarView == null) {
554 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
555 throw new RuntimeException("Unable to build left nav bar due to missing layout");
556 }
557 mLeftNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700558 addTemperatureViewToController(mLeftNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700559 mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800560 }
561
562
Brad Stenning224b5b32018-03-28 21:26:57 -0700563 private void buildRight(int layout) {
564 View.inflate(mContext, layout, mRightNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800565 mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
566 if (mRightNavigationBarView == null) {
567 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
568 throw new RuntimeException("Unable to build right nav bar due to missing layout");
569 }
Brad Stenningf084d882018-08-08 13:34:21 -0700570 mRightNavigationBarView.setStatusBar(this);
571 addTemperatureViewToController(mRightNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700572 mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800573 }
574
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800575 @Override
Qiming Shib9dfb922017-04-13 16:23:18 +0800576 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
577 //When executing dump() funciton simultaneously, we need to serialize them
578 //to get mStackScroller's position correctly.
579 synchronized (mQueueLock) {
580 pw.println(" mStackScroller: " + viewInfo(mStackScroller));
581 pw.println(" mStackScroller: " + viewInfo(mStackScroller)
582 + " scroll " + mStackScroller.getScrollX()
583 + "," + mStackScroller.getScrollY());
584 }
585
davidln54739152018-07-02 13:25:44 -0700586 pw.print(" mTaskStackListener=");
587 pw.println(mTaskStackListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800588 pw.print(" mCarFacetButtonController=");
589 pw.println(mCarFacetButtonController);
davidln54739152018-07-02 13:25:44 -0700590 pw.print(" mFullscreenUserSwitcher=");
591 pw.println(mFullscreenUserSwitcher);
Qiming Shib9dfb922017-04-13 16:23:18 +0800592 pw.print(" mCarBatteryController=");
593 pw.println(mCarBatteryController);
594 pw.print(" mBatteryMeterView=");
595 pw.println(mBatteryMeterView);
Qiming Shib9dfb922017-04-13 16:23:18 +0800596 pw.print(" mNavigationBarView=");
597 pw.println(mNavigationBarView);
598
599 if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
600 KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
601 }
602
603 FalsingManager.getInstance(mContext).dump(pw);
604 FalsingLog.dump(pw);
605
606 pw.println("SharedPreferences:");
607 for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
davidln54739152018-07-02 13:25:44 -0700608 pw.print(" ");
609 pw.print(entry.getKey());
610 pw.print("=");
611 pw.println(entry.getValue());
Qiming Shib9dfb922017-04-13 16:23:18 +0800612 }
613 }
614
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800615
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800616 @Override
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700617 public void showBatteryView() {
618 if (Log.isLoggable(TAG, Log.DEBUG)) {
619 Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
620 }
621
622 if (mBatteryMeterView != null) {
623 mBatteryMeterView.setVisibility(View.VISIBLE);
624 }
625 }
626
627 @Override
628 public void hideBatteryView() {
629 if (Log.isLoggable(TAG, Log.DEBUG)) {
630 Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
631 }
632
633 if (mBatteryMeterView != null) {
634 mBatteryMeterView.setVisibility(View.GONE);
635 }
636 }
637
Victor Chan1c6d0582016-01-09 16:26:37 -0800638 /**
Winson Chung67f5c8b2018-09-24 12:09:19 -0700639 * An implementation of TaskStackChangeListener, that listens for changes in the system
davidln54739152018-07-02 13:25:44 -0700640 * task stack and notifies the navigation bar.
Victor Chan1c6d0582016-01-09 16:26:37 -0800641 */
Winson Chung67f5c8b2018-09-24 12:09:19 -0700642 private class TaskStackListenerImpl extends TaskStackChangeListener {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800643 @Override
Victor Chan1c6d0582016-01-09 16:26:37 -0800644 public void onTaskStackChanged() {
Brad Stenninga5e714c2018-04-12 19:23:39 -0700645 try {
646 mCarFacetButtonController.taskChanged(
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700647 ActivityTaskManager.getService().getAllStackInfos());
Brad Stenninga5e714c2018-04-12 19:23:39 -0700648 } catch (Exception e) {
649 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
650 }
Victor Chan1c6d0582016-01-09 16:26:37 -0800651 }
Victor Chan1c6d0582016-01-09 16:26:37 -0800652 }
Rakesh Iyer2790a372016-01-22 15:33:39 -0800653
jovanakedba98c2018-09-14 15:46:24 -0700654 private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
655 // Check if we need to start the timer every time driving state changes.
656 startSwitchToGuestTimerIfDrivingOnKeyguard();
657 }
658
659 private void startSwitchToGuestTimerIfDrivingOnKeyguard() {
660 if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) {
661 // We're driving while keyguard is up.
662 mSwitchToGuestTimer.start();
663 } else {
664 mSwitchToGuestTimer.cancel();
665 }
666 }
667
Rakesh Iyer2790a372016-01-22 15:33:39 -0800668 @Override
669 protected void createUserSwitcher() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500670 UserSwitcherController userSwitcherController =
671 Dependency.get(UserSwitcherController.class);
672 if (userSwitcherController.useFullscreenUserSwitcher()) {
673 mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700674 mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
Rakesh Iyer2790a372016-01-22 15:33:39 -0800675 } else {
676 super.createUserSwitcher();
677 }
678 }
679
680 @Override
jovanak168dbdb2019-02-18 01:00:43 -0800681 public void setLockscreenUser(int newUserId) {
682 super.setLockscreenUser(newUserId);
683 // Try to dismiss the keyguard after every user switch.
684 dismissKeyguardWhenUserSwitcherNotDisplayed();
685 }
686
687 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400688 public void onStateChanged(int newState) {
689 super.onStateChanged(newState);
jovanakedba98c2018-09-14 15:46:24 -0700690
691 startSwitchToGuestTimerIfDrivingOnKeyguard();
692
jovanak168dbdb2019-02-18 01:00:43 -0800693 if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
694 hideUserSwitcher();
jovanak604ad512018-08-14 18:41:27 -0700695 } else {
jovanak168dbdb2019-02-18 01:00:43 -0800696 dismissKeyguardWhenUserSwitcherNotDisplayed();
697 }
698 }
699
700 /** Makes the full screen user switcher visible, if applicable. */
701 public void showUserSwitcher() {
702 if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
703 mFullscreenUserSwitcher.show(); // Makes the switcher visible.
704 }
705 }
706
707 private void hideUserSwitcher() {
708 if (mFullscreenUserSwitcher != null) {
jovanak604ad512018-08-14 18:41:27 -0700709 mFullscreenUserSwitcher.hide();
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700710 }
711 }
712
jovanak168dbdb2019-02-18 01:00:43 -0800713 // We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
714 private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
715 if (mFullscreenUserSwitcher == null) {
716 return; // Not using the full screen user switcher.
717 }
718
719 if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER
720 && !mFullscreenUserSwitcher.isVisible()) {
721 // Current execution path continues to set state after this, thus we deffer the
722 // dismissal to the next execution cycle.
723 postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
Rakesh Iyer2790a372016-01-22 15:33:39 -0800724 }
725 }
Rakesh Iyer74ebabe2016-03-11 10:10:43 -0800726
jovanak604ad512018-08-14 18:41:27 -0700727 public void postDismissKeyguard() {
728 mHandler.post(this::dismissKeyguard);
729 }
730
731 /**
732 * Dismisses the keyguard and shows bouncer if authentication is necessary.
733 */
734 public void dismissKeyguard() {
735 executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800736 true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
jovanak604ad512018-08-14 18:41:27 -0700737 }
738
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800739 /**
740 * Ensures that relevant child views are appropriately recreated when the device's density
741 * changes.
742 */
743 @Override
Jason Monk9cac5ff2017-06-02 11:35:50 -0400744 public void onDensityOrFontScaleChanged() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800745 super.onDensityOrFontScaleChanged();
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800746 restartNavBars();
Anthony Chene658cc22017-04-27 11:17:35 -0700747 // Need to update the background on density changed in case the change was due to night
748 // mode.
749 mNotificationPanelBackground = getDefaultWallpaper();
750 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
751 }
752
753 /**
754 * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
755 */
756 private Drawable getDefaultWallpaper() {
757 return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800758 }
Brad Stenning19f236a2018-12-11 14:12:30 -0800759
Priyank Singh63e68602019-03-04 11:57:34 -0800760 /** Returns true if the current user makes it through the setup wizard, false otherwise. */
761 public boolean getIsUserSetup(){
762 return mUserSetup;
763 }
764
Brad Stenning3b0d7642019-03-28 11:04:30 -0700765
766 // TODO: add settle down/up logic
767 private static final int SWIPE_UP_MIN_DISTANCE = 75;
768 private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
769 private static final int SWIPE_MAX_OFF_PATH = 75;
770 private static final int SWIPE_THRESHOLD_VELOCITY = 200;
771 // Only responsible for open hooks. Since once the panel opens it covers all elements
772 // there is no need to merge with close.
773 private abstract class OpenNotificationGestureListener extends
774 GestureDetector.SimpleOnGestureListener {
775
776 @Override
777 public boolean onFling(MotionEvent event1, MotionEvent event2,
778 float velocityX, float velocityY) {
779 if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
780 || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
781 // swipe was not vertical or was not fast enough
782 return false;
783 }
784 boolean isDown = velocityY > 0;
785 float distanceDelta = Math.abs(event1.getY() - event2.getY());
786 if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
787 openNotification();
788 return true;
789 }
790
791 return false;
792 }
793 protected abstract void openNotification();
Brad Stenning19f236a2018-12-11 14:12:30 -0800794 }
795
Brad Stenning3b0d7642019-03-28 11:04:30 -0700796 // to be installed on the open panel notification panel
797 private abstract class CloseNotificationGestureListener extends
798 GestureDetector.SimpleOnGestureListener {
799
800 @Override
801 public boolean onFling(MotionEvent event1, MotionEvent event2,
802 float velocityX, float velocityY) {
803 if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
804 || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
805 // swipe was not vertical or was not fast enough
806 return false;
807 }
808 boolean isUp = velocityY < 0;
809 float distanceDelta = Math.abs(event1.getY() - event2.getY());
810 if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
811 close();
812 return true;
813 }
814 return false;
815 }
816 protected abstract void close();
817 }
818
819 // to be installed on the nav bars
820 private abstract class NavBarCloseNotificationGestureListener extends
821 CloseNotificationGestureListener {
822 @Override
823 public boolean onSingleTapUp(MotionEvent e) {
824 close();
825 return super.onSingleTapUp(e);
826 }
827
828 @Override
829 public void onLongPress(MotionEvent e) {
830 close();
831 super.onLongPress(e);
832 }
Adora Zhang0237b9c2019-03-08 12:27:09 -0800833 }
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800834}