blob: 25191f6a9617d52bdfafe3fa8ed281610045198b [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
Priyank Singhb0241582019-04-19 18:46:26 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Matthew Ng94380652019-04-08 13:43:07 -070022import android.annotation.Nullable;
Brad Stenning3b0d7642019-03-28 11:04:30 -070023import android.app.ActivityManager;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070024import android.app.ActivityTaskManager;
Brad Stenningd3ede642019-04-23 21:19:27 -070025import android.car.Car;
jovanakedba98c2018-09-14 15:46:24 -070026import android.car.drivingstate.CarDrivingStateEvent;
Brad Stenningd3ede642019-04-23 21:19:27 -070027import android.car.drivingstate.CarUxRestrictionsManager;
Jonathan Koo007ff072019-05-10 09:24:08 -070028import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
Erin Yan8182bfd2019-08-14 12:03:12 -070029import android.car.trust.CarTrustAgentEnrollmentManager;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070030import android.content.Context;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080031import android.graphics.PixelFormat;
Priyank Singhb0241582019-04-19 18:46:26 -070032import android.graphics.Rect;
Anthony Chene658cc22017-04-27 11:17:35 -070033import android.graphics.drawable.Drawable;
Heemin Seog28097c42019-08-15 13:18:41 -070034import android.inputmethodservice.InputMethodService;
35import android.os.IBinder;
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080036import android.util.Log;
Heemin Seog28097c42019-08-15 13:18:41 -070037import android.view.Display;
Brad Stenning3b0d7642019-03-28 11:04:30 -070038import android.view.GestureDetector;
Brad Stenning078235b2017-12-18 08:25:10 -080039import android.view.Gravity;
Brad Stenning3b0d7642019-03-28 11:04:30 -070040import android.view.MotionEvent;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080041import android.view.View;
Anthony Chen0801a5c2017-03-22 09:54:37 -070042import android.view.ViewGroup;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080043import android.view.ViewGroup.LayoutParams;
Priyank Singhb9ab0522019-05-03 15:36:25 -070044import android.view.ViewTreeObserver;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080045import android.view.WindowManager;
Anthony Chene658cc22017-04-27 11:17:35 -070046
Brad Stenning3b0d7642019-03-28 11:04:30 -070047import androidx.annotation.NonNull;
48import androidx.recyclerview.widget.RecyclerView;
49
Brad Stenning2ccc2a42019-04-15 10:17:01 -070050import com.android.car.notification.CarHeadsUpNotificationManager;
Brad Stenning3b0d7642019-03-28 11:04:30 -070051import com.android.car.notification.CarNotificationListener;
52import com.android.car.notification.CarNotificationView;
53import com.android.car.notification.CarUxRestrictionManagerWrapper;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070054import com.android.car.notification.HeadsUpEntry;
Brad Stenning3b0d7642019-03-28 11:04:30 -070055import com.android.car.notification.NotificationClickHandlerFactory;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070056import com.android.car.notification.NotificationDataManager;
Brad Stenning3b0d7642019-03-28 11:04:30 -070057import com.android.car.notification.NotificationViewController;
58import com.android.car.notification.PreprocessingManager;
Matthew Ng94380652019-04-08 13:43:07 -070059import com.android.internal.statusbar.RegisterStatusBarResult;
Winson Chung2db35572017-10-09 15:08:30 -070060import com.android.keyguard.KeyguardUpdateMonitor;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070061import com.android.systemui.BatteryMeterView;
Jason Monk27d01a622018-12-10 15:57:09 -050062import com.android.systemui.CarSystemUIFactory;
Jason Monk9c7844c2017-01-18 15:21:53 -050063import com.android.systemui.Dependency;
Winson Chung2db35572017-10-09 15:08:30 -070064import com.android.systemui.Prefs;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080065import com.android.systemui.R;
Jason Monk27d01a622018-12-10 15:57:09 -050066import com.android.systemui.SystemUIFactory;
Winson Chung2db35572017-10-09 15:08:30 -070067import com.android.systemui.classifier.FalsingLog;
Jason Monkaa573e92017-01-27 17:00:29 -050068import com.android.systemui.fragments.FragmentHostManager;
jovanak622def22019-06-11 11:51:54 -070069import com.android.systemui.keyguard.ScreenLifecycle;
Dave Mankoff781ef7e2019-06-28 16:33:25 -040070import com.android.systemui.plugins.FalsingManager;
Brad Stenning8d1a51c2018-11-20 17:34:16 -080071import com.android.systemui.plugins.qs.QS;
72import com.android.systemui.qs.car.CarQSFragment;
Winson Chungb05b3982017-11-01 18:02:43 -070073import com.android.systemui.shared.system.ActivityManagerWrapper;
Winson Chung67f5c8b2018-09-24 12:09:19 -070074import com.android.systemui.shared.system.TaskStackChangeListener;
Priyank Singhb0241582019-04-19 18:46:26 -070075import com.android.systemui.statusbar.FlingAnimationUtils;
Rakesh Iyer2790a372016-01-22 15:33:39 -080076import com.android.systemui.statusbar.StatusBarState;
Brad Stenning38b46f82018-03-27 13:57:29 -070077import com.android.systemui.statusbar.car.hvac.HvacController;
Brad Stenningf084d882018-08-08 13:34:21 -070078import com.android.systemui.statusbar.car.hvac.TemperatureView;
Jason Monkaa573e92017-01-27 17:00:29 -050079import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050080import com.android.systemui.statusbar.phone.StatusBar;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070081import com.android.systemui.statusbar.policy.BatteryController;
Brad Stenning224b5b32018-03-28 21:26:57 -070082import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Jason Monk9c7844c2017-01-18 15:21:53 -050083import com.android.systemui.statusbar.policy.UserSwitcherController;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080084
Qiming Shib9dfb922017-04-13 16:23:18 +080085import java.io.FileDescriptor;
86import java.io.PrintWriter;
87import java.util.Map;
davidln54739152018-07-02 13:25:44 -070088
Rakesh Iyer1186faa2015-12-07 16:48:46 -080089/**
90 * A status bar (and navigation bar) tailored for the automotive use case.
91 */
Heemin Seog28097c42019-08-15 13:18:41 -070092public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080093 private static final String TAG = "CarStatusBar";
Priyank Singhb0241582019-04-19 18:46:26 -070094 // used to calculate how fast to open or close the window
95 private static final float DEFAULT_FLING_VELOCITY = 0;
96 // max time a fling animation takes
97 private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
98 // acceleration rate for the fling animation
99 private static final float FLING_SPEED_UP_FACTOR = 0.6f;
100
101 private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
102 private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
Rakesh Iyer74ebabe2016-03-11 10:10:43 -0800103
Victor Chan1c6d0582016-01-09 16:26:37 -0800104 private TaskStackListenerImpl mTaskStackListener;
Victor Chan1c6d0582016-01-09 16:26:37 -0800105
Rakesh Iyer2790a372016-01-22 15:33:39 -0800106 private FullscreenUserSwitcher mFullscreenUserSwitcher;
Victor Chan1c6d0582016-01-09 16:26:37 -0800107
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700108 private CarBatteryController mCarBatteryController;
109 private BatteryMeterView mBatteryMeterView;
Anthony Chene658cc22017-04-27 11:17:35 -0700110 private Drawable mNotificationPanelBackground;
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700111
Scott Randolphf4085822017-06-27 15:23:01 -0700112 private ViewGroup mNavigationBarWindow;
Brad Stenning078235b2017-12-18 08:25:10 -0800113 private ViewGroup mLeftNavigationBarWindow;
114 private ViewGroup mRightNavigationBarWindow;
Jason Monk49fa0162017-01-11 09:21:56 -0500115 private CarNavigationBarView mNavigationBarView;
Brad Stenning078235b2017-12-18 08:25:10 -0800116 private CarNavigationBarView mLeftNavigationBarView;
117 private CarNavigationBarView mRightNavigationBarView;
Anthony Chen1c59e9f2016-04-11 11:05:48 -0700118
Qiming Shib9dfb922017-04-13 16:23:18 +0800119 private final Object mQueueLock = new Object();
Brad Stenning078235b2017-12-18 08:25:10 -0800120 private boolean mShowLeft;
121 private boolean mShowRight;
122 private boolean mShowBottom;
123 private CarFacetButtonController mCarFacetButtonController;
Brad Stenning224b5b32018-03-28 21:26:57 -0700124 private ActivityManagerWrapper mActivityManagerWrapper;
125 private DeviceProvisionedController mDeviceProvisionedController;
126 private boolean mDeviceIsProvisioned = true;
Brad Stenningf084d882018-08-08 13:34:21 -0700127 private HvacController mHvacController;
jovanakedba98c2018-09-14 15:46:24 -0700128 private DrivingStateHelper mDrivingStateHelper;
Jonathan Koo007ff072019-05-10 09:24:08 -0700129 private PowerManagerHelper mPowerManagerHelper;
130 private FlingAnimationUtils mFlingAnimationUtils;
jovanakedba98c2018-09-14 15:46:24 -0700131 private SwitchToGuestTimer mSwitchToGuestTimer;
Jonathan Koo007ff072019-05-10 09:24:08 -0700132 private NotificationDataManager mNotificationDataManager;
133 private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
jovanak622def22019-06-11 11:51:54 -0700134 private ScreenLifecycle mScreenLifecycle;
Brad Stenning078235b2017-12-18 08:25:10 -0800135
Brad Stenning3b0d7642019-03-28 11:04:30 -0700136 // The container for the notifications.
137 private CarNotificationView mNotificationView;
138 private RecyclerView mNotificationList;
Priyank Singh647f9e62019-05-16 15:19:53 -0700139 // The handler bar view at the bottom of notification shade.
140 private View mHandleBar;
Adora Zhangdaea9112019-04-11 17:47:40 -0700141 // The controller for the notification view.
142 private NotificationViewController mNotificationViewController;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700143 // The state of if the notification list is currently showing the bottom.
144 private boolean mNotificationListAtBottom;
145 // Was the notification list at the bottom when the user first touched the screen
146 private boolean mNotificationListAtBottomAtTimeOfTouch;
147 // To be attached to the navigation bars such that they can close the notification panel if
148 // it's open.
149 private View.OnTouchListener mNavBarNotificationTouchListener;
150
Priyank Singhb0241582019-04-19 18:46:26 -0700151 // Percentage from top of the screen after which the notification shade will open. This value
152 // will be used while opening the notification shade.
153 private int mSettleOpenPercentage;
154 // Percentage from top of the screen below which the notification shade will close. This
155 // value will be used while closing the notification shade.
156 private int mSettleClosePercentage;
157 // Percentage of notification shade open from top of the screen.
158 private int mPercentageFromBottom;
159 // If notification shade is animation to close or to open.
160 private boolean mIsNotificationAnimating;
161
162 // Tracks when the notification shade is being scrolled. This refers to the glass pane being
163 // scrolled not the recycler view.
164 private boolean mIsTracking;
165 private float mFirstTouchDownOnGlassPane;
166
167 // If the notification card inside the recycler view is being swiped.
168 private boolean mIsNotificationCardSwiping;
169 // If notification shade is being swiped vertically to close.
170 private boolean mIsSwipingVerticallyToClose;
Priyank Singh8bd920f2019-06-04 15:21:46 -0700171 // Whether heads-up notifications should be shown when shade is open.
172 private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
Heemin Seog28097c42019-08-15 13:18:41 -0700173 // If the nav bar should be hidden when the soft keyboard is visible.
174 private boolean mHideNavBarForKeyboard;
175 private boolean mBottomNavBarVisible;
Priyank Singhb0241582019-04-19 18:46:26 -0700176
Jonathan Koo007ff072019-05-10 09:24:08 -0700177 private final CarPowerStateListener mCarPowerStateListener =
178 (int state) -> {
179 // When the car powers on, clear all notifications and mute/unread states.
180 Log.d(TAG, "New car power state: " + state);
181 if (state == CarPowerStateListener.ON) {
182 if (mNotificationClickHandlerFactory != null) {
183 mNotificationClickHandlerFactory.clearAllNotifications();
184 }
185 if (mNotificationDataManager != null) {
186 mNotificationDataManager.clearAll();
187 }
188 }
189 };
190
Victor Chan1c6d0582016-01-09 16:26:37 -0800191 @Override
192 public void start() {
Brad Stenningeb6051c2019-02-27 13:46:11 -0800193 // get the provisioned state before calling the parent class since it's that flow that
194 // builds the nav bar
195 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
196 mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
Heemin Seog28097c42019-08-15 13:18:41 -0700197
198 // Keyboard related setup, before nav bars are created.
199 mHideNavBarForKeyboard = mContext.getResources().getBoolean(
200 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
201 mBottomNavBarVisible = false;
202
jovanak504e7532019-08-19 16:32:50 -0700203 // Need to initialize screen lifecycle before calling super.start - before switcher is
204 // created.
205 mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
206 mScreenLifecycle.addObserver(mScreenObserver);
207
Victor Chan1c6d0582016-01-09 16:26:37 -0800208 super.start();
Jaewan Kim938a50b2016-03-14 17:35:43 +0900209 mTaskStackListener = new TaskStackListenerImpl();
Brad Stenning224b5b32018-03-28 21:26:57 -0700210 mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
211 mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700212
Jason Monke59dc402018-08-16 12:05:01 -0400213 mNotificationPanel.setScrollingEnabled(true);
Priyank Singhb0241582019-04-19 18:46:26 -0700214 mSettleOpenPercentage = mContext.getResources().getInteger(
215 R.integer.notification_settle_open_percentage);
216 mSettleClosePercentage = mContext.getResources().getInteger(
217 R.integer.notification_settle_close_percentage);
Jonathan Koo007ff072019-05-10 09:24:08 -0700218 mFlingAnimationUtils = new FlingAnimationUtils(mContext,
Priyank Singhb0241582019-04-19 18:46:26 -0700219 FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800220
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800221 createBatteryController();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700222 mCarBatteryController.startListening();
Brad Stenning38b46f82018-03-27 13:57:29 -0700223
Brad Stenningf084d882018-08-08 13:34:21 -0700224 mHvacController.connectToCarService();
davidln54739152018-07-02 13:25:44 -0700225
Jason Monk27d01a622018-12-10 15:57:09 -0500226 CarSystemUIFactory factory = SystemUIFactory.getInstance();
Brad Stenning224b5b32018-03-28 21:26:57 -0700227 if (!mDeviceIsProvisioned) {
228 mDeviceProvisionedController.addCallback(
229 new DeviceProvisionedController.DeviceProvisionedListener() {
230 @Override
231 public void onDeviceProvisionedChanged() {
Brad Stenninga2d87d82019-01-24 12:33:10 -0800232 mHandler.post(() -> {
233 // on initial boot we are getting a call even though the value
234 // is the same so we are confirming the reset is needed
235 boolean deviceProvisioned =
236 mDeviceProvisionedController.isDeviceProvisioned();
237 if (mDeviceIsProvisioned != deviceProvisioned) {
238 mDeviceIsProvisioned = deviceProvisioned;
239 restartNavBars();
240 }
241 });
Brad Stenning224b5b32018-03-28 21:26:57 -0700242 }
243 });
244 }
jovanakedba98c2018-09-14 15:46:24 -0700245
246 // Register a listener for driving state changes.
247 mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
248 mDrivingStateHelper.connectToCarService();
249
Jonathan Koo007ff072019-05-10 09:24:08 -0700250 mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
251 mPowerManagerHelper.connectToCarService();
252
jovanakedba98c2018-09-14 15:46:24 -0700253 mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700254 }
255
Brad Stenning224b5b32018-03-28 21:26:57 -0700256 /**
257 * Remove all content from navbars and rebuild them. Used to allow for different nav bars
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800258 * before and after the device is provisioned. . Also for change of density and font size.
Brad Stenning224b5b32018-03-28 21:26:57 -0700259 */
260 private void restartNavBars() {
Brad Stenningf084d882018-08-08 13:34:21 -0700261 // remove and reattach all hvac components such that we don't keep a reference to unused
262 // ui elements
263 mHvacController.removeAllComponents();
264 addTemperatureViewToController(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700265 mCarFacetButtonController.removeAll();
Brad Stenning224b5b32018-03-28 21:26:57 -0700266 if (mNavigationBarWindow != null) {
267 mNavigationBarWindow.removeAllViews();
268 mNavigationBarView = null;
269 }
270
271 if (mLeftNavigationBarWindow != null) {
272 mLeftNavigationBarWindow.removeAllViews();
273 mLeftNavigationBarView = null;
274 }
275
276 if (mRightNavigationBarWindow != null) {
277 mRightNavigationBarWindow.removeAllViews();
278 mRightNavigationBarView = null;
279 }
Brad Stenningf084d882018-08-08 13:34:21 -0700280
Brad Stenning224b5b32018-03-28 21:26:57 -0700281 buildNavBarContent();
Brad Stenning75221642019-03-13 07:33:24 -0700282 // If the UI was rebuilt (day/night change) while the keyguard was up we need to
283 // correctly respect that state.
284 if (mIsKeyguard) {
285 updateNavBarForKeyguardContent();
286 }
Brad Stenning4534f442019-04-01 21:43:32 -0700287 // CarFacetButtonController was reset therefore we need to re-add the status bar elements
288 // to the controller.
289 mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700290 }
291
Brad Stenningf084d882018-08-08 13:34:21 -0700292 private void addTemperatureViewToController(View v) {
293 if (v instanceof TemperatureView) {
Brad Stenningf084d882018-08-08 13:34:21 -0700294 mHvacController.addHvacTextView((TemperatureView) v);
295 } else if (v instanceof ViewGroup) {
296 ViewGroup viewGroup = (ViewGroup) v;
297 for (int i = 0; i < viewGroup.getChildCount(); i++) {
298 addTemperatureViewToController(viewGroup.getChildAt(i));
299 }
300 }
301 }
302
Brad Stenning7e411812018-04-13 22:52:39 -0700303 /**
304 * Allows for showing or hiding just the navigation bars. This is indented to be used when
305 * the full screen user selector is shown.
306 */
davidln54739152018-07-02 13:25:44 -0700307 void setNavBarVisibility(@View.Visibility int visibility) {
Brad Stenning7e411812018-04-13 22:52:39 -0700308 if (mNavigationBarWindow != null) {
309 mNavigationBarWindow.setVisibility(visibility);
310 }
311 if (mLeftNavigationBarWindow != null) {
312 mLeftNavigationBarWindow.setVisibility(visibility);
313 }
314 if (mRightNavigationBarWindow != null) {
315 mRightNavigationBarWindow.setVisibility(visibility);
316 }
317 }
318
319
320 @Override
321 public boolean hideKeyguard() {
322 boolean result = super.hideKeyguard();
323 if (mNavigationBarView != null) {
324 mNavigationBarView.hideKeyguardButtons();
325 }
326 if (mLeftNavigationBarView != null) {
327 mLeftNavigationBarView.hideKeyguardButtons();
328 }
329 if (mRightNavigationBarView != null) {
330 mRightNavigationBarView.hideKeyguardButtons();
331 }
332 return result;
333 }
334
Brad Stenning7e411812018-04-13 22:52:39 -0700335 @Override
336 public void showKeyguard() {
337 super.showKeyguard();
Brad Stenning75221642019-03-13 07:33:24 -0700338 updateNavBarForKeyguardContent();
339 }
340
341 /**
342 * Switch to the keyguard applicable content contained in the nav bars
343 */
344 private void updateNavBarForKeyguardContent() {
Brad Stenning7e411812018-04-13 22:52:39 -0700345 if (mNavigationBarView != null) {
346 mNavigationBarView.showKeyguardButtons();
347 }
348 if (mLeftNavigationBarView != null) {
349 mLeftNavigationBarView.showKeyguardButtons();
350 }
351 if (mRightNavigationBarView != null) {
352 mRightNavigationBarView.showKeyguardButtons();
353 }
354 }
Brad Stenning224b5b32018-03-28 21:26:57 -0700355
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700356 @Override
Matthew Ng94380652019-04-08 13:43:07 -0700357 protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
358 super.makeStatusBarView(result);
Jason Monk27d01a622018-12-10 15:57:09 -0500359 mHvacController = new HvacController(mContext);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700360
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800361 CarSystemUIFactory factory = SystemUIFactory.getInstance();
362 mCarFacetButtonController = factory.getCarDependencyComponent()
davidln1ceedb52019-05-30 14:25:01 -0700363 .getCarFacetButtonController();
Anthony Chene658cc22017-04-27 11:17:35 -0700364 mNotificationPanelBackground = getDefaultWallpaper();
365 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
366
Jason Monkaa573e92017-01-27 17:00:29 -0500367 FragmentHostManager manager = FragmentHostManager.get(mStatusBarWindow);
368 manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
Anthony Chene658cc22017-04-27 11:17:35 -0700369 mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700370
Jason Monkaa573e92017-01-27 17:00:29 -0500371 // By default, the BatteryMeterView should not be visible. It will be toggled
372 // when a device has connected by bluetooth.
373 mBatteryMeterView.setVisibility(View.GONE);
Jason Monkaa573e92017-01-27 17:00:29 -0500374 });
Brad Stenning3b0d7642019-03-28 11:04:30 -0700375
376 connectNotificationsUI();
377 }
378
379 /**
380 * Attach the notification listeners and controllers to the UI as well as build all the
381 * touch listeners needed for opening and closing the notification panel
382 */
383 private void connectNotificationsUI() {
384 // Attached to the status bar to detect pull down of the notification shade.
385 GestureDetector openGestureDetector = new GestureDetector(mContext,
386 new OpenNotificationGestureListener() {
387 @Override
388 protected void openNotification() {
389 animateExpandNotificationsPanel();
390 }
391 });
392 // Attached to the notification ui to detect close request of the notification shade.
393 GestureDetector closeGestureDetector = new GestureDetector(mContext,
394 new CloseNotificationGestureListener() {
395 @Override
396 protected void close() {
Brad Stenning44d39072019-04-20 11:53:17 -0700397 if (mPanelExpanded) {
398 animateCollapsePanels();
399 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700400 }
401 });
402 // Attached to the NavBars to close the notification shade
403 GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
404 new NavBarCloseNotificationGestureListener() {
405 @Override
406 protected void close() {
Brad Stenning44d39072019-04-20 11:53:17 -0700407 if (mPanelExpanded) {
408 animateCollapsePanels();
409 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700410 }
411 });
Priyank Singha47b6f72019-05-28 13:02:47 -0700412
413 // Attached to the Handle bar to close the notification shade
414 GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
415 new HandleBarCloseNotificationGestureListener());
416
Brad Stenning3b0d7642019-03-28 11:04:30 -0700417 mNavBarNotificationTouchListener =
Priyank Singhb0241582019-04-19 18:46:26 -0700418 (v, event) -> {
419 boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
420 if (consumed) {
421 return true;
422 }
Priyank Singha47b6f72019-05-28 13:02:47 -0700423 maybeCompleteAnimation(event);
Priyank Singhb0241582019-04-19 18:46:26 -0700424 return true;
425 };
Brad Stenning3b0d7642019-03-28 11:04:30 -0700426
Brad Stenningc622f1d2019-01-29 11:24:11 -0800427 // The following are the ui elements that the user would call the status bar.
428 // This will set the status bar so it they can make call backs.
429 CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
430 topBar.setStatusBar(this);
Priyank Singhb0241582019-04-19 18:46:26 -0700431 topBar.setStatusBarWindowTouchListener((v1, event1) -> {
432
433 boolean consumed = openGestureDetector.onTouchEvent(event1);
434 if (consumed) {
435 return true;
436 }
Priyank Singha47b6f72019-05-28 13:02:47 -0700437 maybeCompleteAnimation(event1);
Priyank Singhb0241582019-04-19 18:46:26 -0700438 return true;
439 }
440 );
Brad Stenning3b0d7642019-03-28 11:04:30 -0700441
Jonathan Koo007ff072019-05-10 09:24:08 -0700442 mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(
Brad Stenning3b0d7642019-03-28 11:04:30 -0700443 mBarService,
444 launchResult -> {
445 if (launchResult == ActivityManager.START_TASK_TO_FRONT
446 || launchResult == ActivityManager.START_SUCCESS) {
447 animateCollapsePanels();
448 }
449 });
Brad Stenningd3ede642019-04-23 21:19:27 -0700450 Car car = Car.createCar(mContext);
451 CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
452 car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700453 CarNotificationListener carNotificationListener = new CarNotificationListener();
454 CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
455 new CarUxRestrictionManagerWrapper();
Brad Stenningd3ede642019-04-23 21:19:27 -0700456 carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700457
Jonathan Koo007ff072019-05-10 09:24:08 -0700458 mNotificationDataManager = new NotificationDataManager();
459 mNotificationDataManager.setOnUnseenCountUpdateListener(
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700460 () -> {
Jonathan Koo307bda92019-05-14 16:53:48 -0700461 if (mNavigationBarView != null && mNotificationDataManager != null) {
462 Boolean hasUnseen =
463 mNotificationDataManager.getUnseenNotificationCount() > 0;
464 if (mNavigationBarView != null) {
465 mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
466 }
467
468 if (mLeftNavigationBarView != null) {
469 mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
470 }
471
472 if (mRightNavigationBarView != null) {
473 mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
474 }
475 }
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700476 });
477
Priyank Singh8bd920f2019-06-04 15:21:46 -0700478 mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
479 R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
Brad Stenning2ccc2a42019-04-15 10:17:01 -0700480 CarHeadsUpNotificationManager carHeadsUpNotificationManager =
Jonathan Koo007ff072019-05-10 09:24:08 -0700481 new CarSystemUIHeadsUpNotificationManager(mContext,
482 mNotificationClickHandlerFactory, mNotificationDataManager);
483 mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
Brad Stenning2ccc2a42019-04-15 10:17:01 -0700484
Brad Stenning3b0d7642019-03-28 11:04:30 -0700485 carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
Jonathan Koo007ff072019-05-10 09:24:08 -0700486 carHeadsUpNotificationManager, mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700487
488 mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
489 View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
Priyank Singh647f9e62019-05-16 15:19:53 -0700490 mHandleBar = mStatusBarWindow.findViewById(R.id.handle_bar);
Jonathan Koo007ff072019-05-10 09:24:08 -0700491 mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
492 mNotificationView.setNotificationDataManager(mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700493
494 // The glass pane is used to view touch events before passed to the notification list.
495 // This allows us to initialize gesture listeners and detect when to close the notifications
496 glassPane.setOnTouchListener((v, event) -> {
497 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
498 mNotificationListAtBottomAtTimeOfTouch = false;
499 }
500 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
Priyank Singhb0241582019-04-19 18:46:26 -0700501 mFirstTouchDownOnGlassPane = event.getRawX();
Brad Stenning3b0d7642019-03-28 11:04:30 -0700502 mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
Priyank Singhb0241582019-04-19 18:46:26 -0700503 // Reset the tracker when there is a touch down on the glass pane.
504 mIsTracking = false;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700505 // Pass the down event to gesture detector so that it knows where the touch event
506 // started.
507 closeGestureDetector.onTouchEvent(event);
508 }
509 return false;
510 });
Priyank Singha47b6f72019-05-28 13:02:47 -0700511
512 mHandleBar.setOnTouchListener((v, event) -> {
513 handleBarCloseNotificationGestureDetector.onTouchEvent(event);
514 maybeCompleteAnimation(event);
515 return true;
516 });
517
Brad Stenning3b0d7642019-03-28 11:04:30 -0700518 mNotificationList = mNotificationView.findViewById(R.id.notifications);
519 mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
520 @Override
521 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
522 super.onScrolled(recyclerView, dx, dy);
523 if (!mNotificationList.canScrollVertically(1)) {
524 mNotificationListAtBottom = true;
525 return;
526 }
527 mNotificationListAtBottom = false;
Priyank Singhb0241582019-04-19 18:46:26 -0700528 mIsSwipingVerticallyToClose = false;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700529 mNotificationListAtBottomAtTimeOfTouch = false;
530 }
531 });
532 mNotificationList.setOnTouchListener(new View.OnTouchListener() {
533 @Override
534 public boolean onTouch(View v, MotionEvent event) {
Priyank Singhb0241582019-04-19 18:46:26 -0700535 mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
536 > SWIPE_MAX_OFF_PATH;
537 if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
538 // We need to save the state here as if notification card is swiping we will
539 // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
540 // closing the notification shade while the notification card is being swiped.
541 mIsSwipingVerticallyToClose = true;
542 }
543
544 // If the card is swiping we should not allow the notification shade to close.
545 // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
546 // for us. We are also checking for mIsTracking because while swiping the
547 // notification shade to close if the user goes a bit horizontal while swiping
548 // upwards then also this should close.
549 if (mIsNotificationCardSwiping && !mIsTracking) {
550 mNotificationListAtBottomAtTimeOfTouch = false;
551 }
552
Priyank Singhbae09ae2019-04-25 18:17:02 -0700553 boolean handled = closeGestureDetector.onTouchEvent(event);
Priyank Singhb0241582019-04-19 18:46:26 -0700554 boolean isTracking = mIsTracking;
Priyank Singh647f9e62019-05-16 15:19:53 -0700555 Rect rect = mNotificationView.getClipBounds();
Priyank Singhf3912862019-04-22 10:41:58 -0700556 float clippedHeight = 0;
557 if (rect != null) {
558 clippedHeight = rect.bottom;
559 }
Priyank Singhb0241582019-04-19 18:46:26 -0700560 if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
561 && mIsSwipingVerticallyToClose) {
562 if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
563 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
564 } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
565 // this can be caused when user is at the end of the list and trying to
566 // fling to top of the list by scrolling down.
567 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
568 }
569 }
570
Brad Stenning3b0d7642019-03-28 11:04:30 -0700571 // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
572 // the event has been passed to the closeGestureDetector above, such that the
573 // closeGestureDetector sees the up event before the state has changed.
574 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
575 mNotificationListAtBottomAtTimeOfTouch = false;
576 }
Priyank Singhb0241582019-04-19 18:46:26 -0700577 return handled || isTracking;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700578 }
579 });
580
Adora Zhangdaea9112019-04-11 17:47:40 -0700581 mNotificationViewController = new NotificationViewController(
Brad Stenning3b0d7642019-03-28 11:04:30 -0700582 mNotificationView,
583 PreprocessingManager.getInstance(mContext),
584 carNotificationListener,
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700585 carUxRestrictionManagerWrapper,
Jonathan Koo007ff072019-05-10 09:24:08 -0700586 mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700587 mNotificationViewController.enable();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700588 }
589
Brad Stenning44d39072019-04-20 11:53:17 -0700590 /**
591 * @return true if the notification panel is currently visible
592 */
593 boolean isNotificationPanelOpen() {
594 return mPanelExpanded;
595 }
596
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800597 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -0700598 public void animateExpandNotificationsPanel() {
599 if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
600 return;
601 }
602 // scroll to top
603 mNotificationList.scrollToPosition(0);
604 mStatusBarWindowController.setPanelVisible(true);
605 mNotificationView.setVisibility(View.VISIBLE);
Priyank Singhb0241582019-04-19 18:46:26 -0700606 animateNotificationPanel(mOpeningVelocity, false);
607
Brad Stenning3b0d7642019-03-28 11:04:30 -0700608 setPanelExpanded(true);
609 }
610
611 @Override
612 public void animateCollapsePanels(int flags, boolean force, boolean delayed,
613 float speedUpFactor) {
614 super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
615 if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
616 return;
617 }
618 mStatusBarWindowController.setStatusBarFocusable(false);
619 mStatusBarWindow.cancelExpandHelper();
620 mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
Priyank Singhb0241582019-04-19 18:46:26 -0700621
622 animateNotificationPanel(mClosingVelocity, true);
623
624 if (!mIsTracking) {
625 mStatusBarWindowController.setPanelVisible(false);
626 mNotificationView.setVisibility(View.INVISIBLE);
627 }
628
Brad Stenning3b0d7642019-03-28 11:04:30 -0700629 setPanelExpanded(false);
630 }
631
Priyank Singha47b6f72019-05-28 13:02:47 -0700632 private void maybeCompleteAnimation(MotionEvent event) {
633 if (event.getActionMasked() == MotionEvent.ACTION_UP
634 && mNotificationView.getVisibility() == View.VISIBLE) {
635 if (mSettleClosePercentage < mPercentageFromBottom) {
636 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
637 } else {
638 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
639 }
640 }
641 }
642
Priyank Singhb0241582019-04-19 18:46:26 -0700643 /**
644 * Animates the notification shade from one position to other. This is used to either open or
Priyank Singh245bd122019-04-21 15:42:44 -0700645 * close the notification shade completely with a velocity. If the animation is to close the
Priyank Singhb0241582019-04-19 18:46:26 -0700646 * notification shade this method also makes the view invisible after animation ends.
647 */
648 private void animateNotificationPanel(float velocity, boolean isClosing) {
Priyank Singhb0241582019-04-19 18:46:26 -0700649 float to = 0;
650 if (!isClosing) {
651 to = mNotificationView.getHeight();
652 }
Priyank Singhb9ab0522019-05-03 15:36:25 -0700653
Priyank Singh647f9e62019-05-16 15:19:53 -0700654 Rect rect = mNotificationView.getClipBounds();
Priyank Singhb9ab0522019-05-03 15:36:25 -0700655 if (rect != null) {
656 float from = rect.bottom;
657 animate(from, to, velocity, isClosing);
658 return;
659 }
660
661 // We will only be here if the shade is being opened programmatically.
662 ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
663 notificationTreeObserver.addOnGlobalLayoutListener(
664 new ViewTreeObserver.OnGlobalLayoutListener() {
665 @Override
666 public void onGlobalLayout() {
667 ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
668 obs.removeOnGlobalLayoutListener(this);
669 float to = mNotificationView.getHeight();
670 animate(/* from= */ 0, to, velocity, isClosing);
671 }
672 });
673 }
674
675 private void animate(float from, float to, float velocity, boolean isClosing) {
Priyank Singhb0241582019-04-19 18:46:26 -0700676 if (mIsNotificationAnimating) {
677 return;
678 }
679 mIsNotificationAnimating = true;
680 mIsTracking = true;
681 ValueAnimator animator = ValueAnimator.ofFloat(from, to);
682 animator.addUpdateListener(
683 animation -> {
684 float animatedValue = (Float) animation.getAnimatedValue();
685 setNotificationViewClipBounds((int) animatedValue);
686 });
687 animator.addListener(new AnimatorListenerAdapter() {
688 @Override
689 public void onAnimationEnd(Animator animation) {
690 super.onAnimationEnd(animation);
691 mIsNotificationAnimating = false;
692 mIsTracking = false;
693 mOpeningVelocity = DEFAULT_FLING_VELOCITY;
694 mClosingVelocity = DEFAULT_FLING_VELOCITY;
695 if (isClosing) {
696 mStatusBarWindowController.setPanelVisible(false);
697 mNotificationView.setVisibility(View.INVISIBLE);
Priyank Singh647f9e62019-05-16 15:19:53 -0700698 mNotificationView.setClipBounds(null);
Adora Zhangdaea9112019-04-11 17:47:40 -0700699 mNotificationViewController.setIsInForeground(false);
Priyank Singhb0241582019-04-19 18:46:26 -0700700 // let the status bar know that the panel is closed
701 setPanelExpanded(false);
702 } else {
Adora Zhangdaea9112019-04-11 17:47:40 -0700703 mNotificationViewController.setIsInForeground(true);
Priyank Singhb0241582019-04-19 18:46:26 -0700704 // let the status bar know that the panel is open
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700705 mNotificationView.setVisibleNotificationsAsSeen();
Priyank Singhb0241582019-04-19 18:46:26 -0700706 setPanelExpanded(true);
707 }
708 }
709 });
Jonathan Koo007ff072019-05-10 09:24:08 -0700710 mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
Priyank Singhb0241582019-04-19 18:46:26 -0700711 animator.start();
712 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700713
714 @Override
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800715 protected QS createDefaultQSFragment() {
716 return new CarQSFragment();
717 }
718
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800719 private BatteryController createBatteryController() {
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700720 mCarBatteryController = new CarBatteryController(mContext);
721 mCarBatteryController.addBatteryViewHandler(this);
722 return mCarBatteryController;
Victor Chan1c6d0582016-01-09 16:26:37 -0800723 }
724
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800725 @Override
Matthew Ng94380652019-04-08 13:43:07 -0700726 protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
Brad Stenning078235b2017-12-18 08:25:10 -0800727 mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800728 mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800729 mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
730
Brad Stenning224b5b32018-03-28 21:26:57 -0700731 buildNavBarWindows();
732 buildNavBarContent();
733 attachNavBarWindows();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700734
Heemin Seog28097c42019-08-15 13:18:41 -0700735 // Try setting up the initial state of the nav bar if applicable.
736 if (result != null) {
737 setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
738 result.mImeWindowVis, result.mImeBackDisposition,
739 result.mShowImeSwitcher);
740 }
741
Charles Chen10ca70b2018-11-28 00:03:38 +0800742 // There has been a car customized nav bar on the default display, so just create nav bars
743 // on external displays.
Matthew Ng94380652019-04-08 13:43:07 -0700744 mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
Brad Stenning224b5b32018-03-28 21:26:57 -0700745 }
746
747 private void buildNavBarContent() {
748 if (mShowBottom) {
749 buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
750 R.layout.car_navigation_bar_unprovisioned);
751 }
752
753 if (mShowLeft) {
754 buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
755 R.layout.car_left_navigation_bar_unprovisioned);
756 }
757
Brad Stenning078235b2017-12-18 08:25:10 -0800758 if (mShowRight) {
Brad Stenning224b5b32018-03-28 21:26:57 -0700759 buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
760 R.layout.car_right_navigation_bar_unprovisioned);
761 }
762 }
763
764 private void buildNavBarWindows() {
765 if (mShowBottom) {
davidln54739152018-07-02 13:25:44 -0700766 mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
Brad Stenning224b5b32018-03-28 21:26:57 -0700767 R.layout.navigation_bar_window, null);
768 }
769 if (mShowLeft) {
770 mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
davidln54739152018-07-02 13:25:44 -0700771 R.layout.navigation_bar_window, null);
Brad Stenning224b5b32018-03-28 21:26:57 -0700772 }
773 if (mShowRight) {
774 mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
775 R.layout.navigation_bar_window, null);
Brad Stenning078235b2017-12-18 08:25:10 -0800776 }
777
778 }
779
Heemin Seog28097c42019-08-15 13:18:41 -0700780 /**
781 * We register for soft keyboard visibility events such that we can hide the navigation bar
782 * giving more screen space to the IME. Note: this is optional and controlled by
783 * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
784 */
785 @Override
786 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
787 boolean showImeSwitcher) {
788 if (!mHideNavBarForKeyboard) {
789 return;
Brad Stenning224b5b32018-03-28 21:26:57 -0700790 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700791
Heemin Seog28097c42019-08-15 13:18:41 -0700792 if (mContext.getDisplay().getDisplayId() != displayId) {
793 return;
794 }
795
796 boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
797 if (!isKeyboardVisible) {
798 attachBottomNavBarWindow();
799 } else {
800 detachBottomNavBarWindow();
801 }
802 }
803
804 private void attachNavBarWindows() {
805 attachBottomNavBarWindow();
806
Brad Stenning224b5b32018-03-28 21:26:57 -0700807 if (mShowLeft) {
808 int width = mContext.getResources().getDimensionPixelSize(
809 R.dimen.car_left_navigation_bar_width);
810 WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
811 width, LayoutParams.MATCH_PARENT,
812 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
813 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
814 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
815 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
816 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
817 PixelFormat.TRANSLUCENT);
818 leftlp.setTitle("LeftCarNavigationBar");
819 leftlp.windowAnimations = 0;
820 leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
821 leftlp.gravity = Gravity.LEFT;
822 mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
823 }
824 if (mShowRight) {
825 int width = mContext.getResources().getDimensionPixelSize(
826 R.dimen.car_right_navigation_bar_width);
827 WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
828 width, LayoutParams.MATCH_PARENT,
829 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
830 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
831 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
832 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
833 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
834 PixelFormat.TRANSLUCENT);
835 rightlp.setTitle("RightCarNavigationBar");
836 rightlp.windowAnimations = 0;
837 rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
838 rightlp.gravity = Gravity.RIGHT;
839 mWindowManager.addView(mRightNavigationBarWindow, rightlp);
840 }
Heemin Seog28097c42019-08-15 13:18:41 -0700841 }
Brad Stenning224b5b32018-03-28 21:26:57 -0700842
Heemin Seog28097c42019-08-15 13:18:41 -0700843 /**
844 * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
845 * attaching the bottom nav bar.
846 */
847 protected void attachBottomNavBarWindow() {
848 if (!mShowBottom) {
849 return;
850 }
851
852 if (mBottomNavBarVisible) {
853 return;
854 }
855 mBottomNavBarVisible = true;
856
857 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
858 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
859 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
860 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
861 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
862 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
863 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
864 PixelFormat.TRANSLUCENT);
865 lp.setTitle("CarNavigationBar");
866 lp.windowAnimations = 0;
867 mWindowManager.addView(mNavigationBarWindow, lp);
868 }
869
870 /**
871 * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
872 * detaching the bottom nav bar.
873 */
874 protected void detachBottomNavBarWindow() {
875 if (!mShowBottom) {
876 return;
877 }
878
879 if (!mBottomNavBarVisible) {
880 return;
881 }
882 mBottomNavBarVisible = false;
883 mWindowManager.removeView(mNavigationBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700884 }
885
886 private void buildBottomBar(int layout) {
Anthony Chen0801a5c2017-03-22 09:54:37 -0700887 // SystemUI requires that the navigation bar view have a parent. Since the regular
888 // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
889 // CarNavigationBarView.
Brad Stenning224b5b32018-03-28 21:26:57 -0700890 View.inflate(mContext, layout, mNavigationBarWindow);
Scott Randolphf4085822017-06-27 15:23:01 -0700891 mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
892 if (mNavigationBarView == null) {
893 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
Brad Stenning078235b2017-12-18 08:25:10 -0800894 throw new RuntimeException("Unable to build botom nav bar due to missing layout");
Scott Randolphf4085822017-06-27 15:23:01 -0700895 }
Brad Stenning078235b2017-12-18 08:25:10 -0800896 mNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700897 addTemperatureViewToController(mNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700898 mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800899 }
900
Brad Stenning224b5b32018-03-28 21:26:57 -0700901 private void buildLeft(int layout) {
902 View.inflate(mContext, layout, mLeftNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800903 mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
904 if (mLeftNavigationBarView == null) {
905 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
906 throw new RuntimeException("Unable to build left nav bar due to missing layout");
907 }
908 mLeftNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700909 addTemperatureViewToController(mLeftNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700910 mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800911 }
912
913
Brad Stenning224b5b32018-03-28 21:26:57 -0700914 private void buildRight(int layout) {
915 View.inflate(mContext, layout, mRightNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800916 mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
917 if (mRightNavigationBarView == null) {
918 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
919 throw new RuntimeException("Unable to build right nav bar due to missing layout");
920 }
Brad Stenningf084d882018-08-08 13:34:21 -0700921 mRightNavigationBarView.setStatusBar(this);
922 addTemperatureViewToController(mRightNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700923 mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800924 }
925
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800926 @Override
Qiming Shib9dfb922017-04-13 16:23:18 +0800927 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Priyank Singhb0241582019-04-19 18:46:26 -0700928 //When executing dump() function simultaneously, we need to serialize them
Qiming Shib9dfb922017-04-13 16:23:18 +0800929 //to get mStackScroller's position correctly.
930 synchronized (mQueueLock) {
931 pw.println(" mStackScroller: " + viewInfo(mStackScroller));
932 pw.println(" mStackScroller: " + viewInfo(mStackScroller)
933 + " scroll " + mStackScroller.getScrollX()
934 + "," + mStackScroller.getScrollY());
935 }
936
davidln54739152018-07-02 13:25:44 -0700937 pw.print(" mTaskStackListener=");
938 pw.println(mTaskStackListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800939 pw.print(" mCarFacetButtonController=");
940 pw.println(mCarFacetButtonController);
davidln54739152018-07-02 13:25:44 -0700941 pw.print(" mFullscreenUserSwitcher=");
942 pw.println(mFullscreenUserSwitcher);
Qiming Shib9dfb922017-04-13 16:23:18 +0800943 pw.print(" mCarBatteryController=");
944 pw.println(mCarBatteryController);
945 pw.print(" mBatteryMeterView=");
946 pw.println(mBatteryMeterView);
Qiming Shib9dfb922017-04-13 16:23:18 +0800947 pw.print(" mNavigationBarView=");
948 pw.println(mNavigationBarView);
949
950 if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
951 KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
952 }
953
Dave Mankoff781ef7e2019-06-28 16:33:25 -0400954 Dependency.get(FalsingManager.class).dump(pw);
Qiming Shib9dfb922017-04-13 16:23:18 +0800955 FalsingLog.dump(pw);
956
957 pw.println("SharedPreferences:");
958 for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
davidln54739152018-07-02 13:25:44 -0700959 pw.print(" ");
960 pw.print(entry.getKey());
961 pw.print("=");
962 pw.println(entry.getValue());
Qiming Shib9dfb922017-04-13 16:23:18 +0800963 }
964 }
965
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800966 @Override
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700967 public void showBatteryView() {
968 if (Log.isLoggable(TAG, Log.DEBUG)) {
969 Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
970 }
971
972 if (mBatteryMeterView != null) {
973 mBatteryMeterView.setVisibility(View.VISIBLE);
974 }
975 }
976
977 @Override
978 public void hideBatteryView() {
979 if (Log.isLoggable(TAG, Log.DEBUG)) {
980 Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
981 }
982
983 if (mBatteryMeterView != null) {
984 mBatteryMeterView.setVisibility(View.GONE);
985 }
986 }
987
Victor Chan1c6d0582016-01-09 16:26:37 -0800988 /**
Winson Chung67f5c8b2018-09-24 12:09:19 -0700989 * An implementation of TaskStackChangeListener, that listens for changes in the system
davidln54739152018-07-02 13:25:44 -0700990 * task stack and notifies the navigation bar.
Victor Chan1c6d0582016-01-09 16:26:37 -0800991 */
Winson Chung67f5c8b2018-09-24 12:09:19 -0700992 private class TaskStackListenerImpl extends TaskStackChangeListener {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800993 @Override
Victor Chan1c6d0582016-01-09 16:26:37 -0800994 public void onTaskStackChanged() {
Brad Stenninga5e714c2018-04-12 19:23:39 -0700995 try {
996 mCarFacetButtonController.taskChanged(
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700997 ActivityTaskManager.getService().getAllStackInfos());
Brad Stenninga5e714c2018-04-12 19:23:39 -0700998 } catch (Exception e) {
999 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
1000 }
Victor Chan1c6d0582016-01-09 16:26:37 -08001001 }
davidln1ceedb52019-05-30 14:25:01 -07001002
1003 @Override
1004 public void onTaskDisplayChanged(int taskId, int newDisplayId) {
1005 try {
1006 mCarFacetButtonController.taskChanged(
1007 ActivityTaskManager.getService().getAllStackInfos());
1008 } catch (Exception e) {
1009 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
1010 }
1011 }
Victor Chan1c6d0582016-01-09 16:26:37 -08001012 }
Rakesh Iyer2790a372016-01-22 15:33:39 -08001013
jovanakedba98c2018-09-14 15:46:24 -07001014 private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
1015 // Check if we need to start the timer every time driving state changes.
1016 startSwitchToGuestTimerIfDrivingOnKeyguard();
1017 }
1018
1019 private void startSwitchToGuestTimerIfDrivingOnKeyguard() {
1020 if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) {
1021 // We're driving while keyguard is up.
1022 mSwitchToGuestTimer.start();
1023 } else {
1024 mSwitchToGuestTimer.cancel();
1025 }
1026 }
1027
Rakesh Iyer2790a372016-01-22 15:33:39 -08001028 @Override
1029 protected void createUserSwitcher() {
Jason Monk9c7844c2017-01-18 15:21:53 -05001030 UserSwitcherController userSwitcherController =
1031 Dependency.get(UserSwitcherController.class);
1032 if (userSwitcherController.useFullscreenUserSwitcher()) {
Erin Yan8182bfd2019-08-14 12:03:12 -07001033 Car car = Car.createCar(mContext);
1034 CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
1035 .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
Jason Monk9c7844c2017-01-18 15:21:53 -05001036 mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
Erin Yan8182bfd2019-08-14 12:03:12 -07001037 mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
1038 enrollmentManager, mContext);
Rakesh Iyer2790a372016-01-22 15:33:39 -08001039 } else {
1040 super.createUserSwitcher();
1041 }
1042 }
1043
1044 @Override
jovanak168dbdb2019-02-18 01:00:43 -08001045 public void setLockscreenUser(int newUserId) {
1046 super.setLockscreenUser(newUserId);
1047 // Try to dismiss the keyguard after every user switch.
1048 dismissKeyguardWhenUserSwitcherNotDisplayed();
1049 }
1050
1051 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -04001052 public void onStateChanged(int newState) {
1053 super.onStateChanged(newState);
jovanakedba98c2018-09-14 15:46:24 -07001054
1055 startSwitchToGuestTimerIfDrivingOnKeyguard();
1056
jovanak168dbdb2019-02-18 01:00:43 -08001057 if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
1058 hideUserSwitcher();
jovanak604ad512018-08-14 18:41:27 -07001059 } else {
jovanak168dbdb2019-02-18 01:00:43 -08001060 dismissKeyguardWhenUserSwitcherNotDisplayed();
1061 }
1062 }
1063
1064 /** Makes the full screen user switcher visible, if applicable. */
1065 public void showUserSwitcher() {
1066 if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
1067 mFullscreenUserSwitcher.show(); // Makes the switcher visible.
1068 }
1069 }
1070
1071 private void hideUserSwitcher() {
1072 if (mFullscreenUserSwitcher != null) {
jovanak604ad512018-08-14 18:41:27 -07001073 mFullscreenUserSwitcher.hide();
Aarthi Balachander0a427ef2018-07-13 15:00:58 -07001074 }
1075 }
1076
jovanak622def22019-06-11 11:51:54 -07001077 final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
1078 @Override
1079 public void onScreenTurnedOn() {
1080 dismissKeyguardWhenUserSwitcherNotDisplayed();
1081 }
1082 };
1083
jovanak168dbdb2019-02-18 01:00:43 -08001084 // We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
1085 private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
1086 if (mFullscreenUserSwitcher == null) {
1087 return; // Not using the full screen user switcher.
1088 }
1089
1090 if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER
1091 && !mFullscreenUserSwitcher.isVisible()) {
1092 // Current execution path continues to set state after this, thus we deffer the
1093 // dismissal to the next execution cycle.
1094 postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
Rakesh Iyer2790a372016-01-22 15:33:39 -08001095 }
1096 }
Rakesh Iyer74ebabe2016-03-11 10:10:43 -08001097
jovanak604ad512018-08-14 18:41:27 -07001098 public void postDismissKeyguard() {
1099 mHandler.post(this::dismissKeyguard);
1100 }
1101
1102 /**
1103 * Dismisses the keyguard and shows bouncer if authentication is necessary.
1104 */
1105 public void dismissKeyguard() {
jovanak622def22019-06-11 11:51:54 -07001106 // Don't dismiss keyguard when the screen is off.
1107 if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
1108 return;
1109 }
jovanak604ad512018-08-14 18:41:27 -07001110 executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
Brad Stenning8d1a51c2018-11-20 17:34:16 -08001111 true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
jovanak604ad512018-08-14 18:41:27 -07001112 }
1113
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001114 /**
1115 * Ensures that relevant child views are appropriately recreated when the device's density
1116 * changes.
1117 */
1118 @Override
Jason Monk9cac5ff2017-06-02 11:35:50 -04001119 public void onDensityOrFontScaleChanged() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001120 super.onDensityOrFontScaleChanged();
Guobin Zhangeef0e0c2019-02-01 11:54:24 -08001121 restartNavBars();
Anthony Chene658cc22017-04-27 11:17:35 -07001122 // Need to update the background on density changed in case the change was due to night
1123 // mode.
1124 mNotificationPanelBackground = getDefaultWallpaper();
1125 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
1126 }
1127
1128 /**
1129 * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
1130 */
1131 private Drawable getDefaultWallpaper() {
1132 return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001133 }
Brad Stenning19f236a2018-12-11 14:12:30 -08001134
Priyank Singhb0241582019-04-19 18:46:26 -07001135 private void setNotificationViewClipBounds(int height) {
Priyank Singhea4af132019-05-01 17:57:06 -07001136 if (height > mNotificationView.getHeight()) {
1137 height = mNotificationView.getHeight();
1138 }
Priyank Singhb0241582019-04-19 18:46:26 -07001139 Rect clipBounds = new Rect();
1140 clipBounds.set(0, 0, mNotificationView.getWidth(), height);
Priyank Singh245bd122019-04-21 15:42:44 -07001141 // Sets the clip region on the notification list view.
Priyank Singh647f9e62019-05-16 15:19:53 -07001142 mNotificationView.setClipBounds(clipBounds);
1143 if (mHandleBar != null) {
1144 ViewGroup.MarginLayoutParams lp =
1145 (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
1146 mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
1147 }
Priyank Singh245bd122019-04-21 15:42:44 -07001148 if (mNotificationView.getHeight() > 0) {
1149 // Calculates the alpha value for the background based on how much of the notification
1150 // shade is visible to the user. When the notification shade is completely open then
1151 // alpha value will be 1.
1152 float alpha = (float) height / mNotificationView.getHeight();
Brad Stenning8650f412019-07-16 16:46:28 -07001153 Drawable background = mNotificationView.getBackground().mutate();
Priyank Singh245bd122019-04-21 15:42:44 -07001154
1155 background.setAlpha((int) (alpha * 255));
Brad Stenning8650f412019-07-16 16:46:28 -07001156 mNotificationView.setBackground(background);
Priyank Singh245bd122019-04-21 15:42:44 -07001157 }
Priyank Singhb0241582019-04-19 18:46:26 -07001158 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001159
Priyank Singhb0241582019-04-19 18:46:26 -07001160 private void calculatePercentageFromBottom(float height) {
1161 if (mNotificationView.getHeight() > 0) {
1162 mPercentageFromBottom = (int) Math.abs(
1163 height / mNotificationView.getHeight() * 100);
1164 }
1165 }
1166
Brad Stenning3b0d7642019-03-28 11:04:30 -07001167 private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
1168 private static final int SWIPE_MAX_OFF_PATH = 75;
1169 private static final int SWIPE_THRESHOLD_VELOCITY = 200;
Priyank Singhb0241582019-04-19 18:46:26 -07001170
Priyank Singha47b6f72019-05-28 13:02:47 -07001171 /**
1172 * Only responsible for open hooks. Since once the panel opens it covers all elements
1173 * there is no need to merge with close.
1174 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001175 private abstract class OpenNotificationGestureListener extends
1176 GestureDetector.SimpleOnGestureListener {
1177
1178 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001179 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1180 float distanceY) {
1181
1182 if (mNotificationView.getVisibility() == View.INVISIBLE) {
1183 // when the on-scroll is called for the first time to open.
1184 mNotificationList.scrollToPosition(0);
1185 }
1186 mStatusBarWindowController.setPanelVisible(true);
1187 mNotificationView.setVisibility(View.VISIBLE);
1188
1189 // clips the view for the notification shade when the user scrolls to open.
1190 setNotificationViewClipBounds((int) event2.getRawY());
1191
1192 // Initially the scroll starts with height being zero. This checks protects from divide
1193 // by zero error.
1194 calculatePercentageFromBottom(event2.getRawY());
1195
1196 mIsTracking = true;
1197 return true;
1198 }
1199
1200
1201 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001202 public boolean onFling(MotionEvent event1, MotionEvent event2,
1203 float velocityX, float velocityY) {
Priyank Singhb0241582019-04-19 18:46:26 -07001204 if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
1205 mOpeningVelocity = velocityY;
Brad Stenning3b0d7642019-03-28 11:04:30 -07001206 openNotification();
1207 return true;
1208 }
Priyank Singhb0241582019-04-19 18:46:26 -07001209 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
Brad Stenning3b0d7642019-03-28 11:04:30 -07001210
1211 return false;
1212 }
Priyank Singhb0241582019-04-19 18:46:26 -07001213
Brad Stenning3b0d7642019-03-28 11:04:30 -07001214 protected abstract void openNotification();
Brad Stenning19f236a2018-12-11 14:12:30 -08001215 }
1216
Priyank Singha47b6f72019-05-28 13:02:47 -07001217 /**
1218 * To be installed on the open panel notification panel
1219 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001220 private abstract class CloseNotificationGestureListener extends
1221 GestureDetector.SimpleOnGestureListener {
1222
1223 @Override
Priyank Singhbae09ae2019-04-25 18:17:02 -07001224 public boolean onSingleTapUp(MotionEvent motionEvent) {
Priyank Singhb9ab0522019-05-03 15:36:25 -07001225 if (mPanelExpanded) {
1226 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
1227 }
1228 return true;
Priyank Singhbae09ae2019-04-25 18:17:02 -07001229 }
1230
1231 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001232 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1233 float distanceY) {
Priyank Singhbae09ae2019-04-25 18:17:02 -07001234 // should not clip while scroll to the bottom of the list.
Priyank Singhb0241582019-04-19 18:46:26 -07001235 if (!mNotificationListAtBottomAtTimeOfTouch) {
1236 return false;
1237 }
1238 float actualNotificationHeight =
1239 mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
1240 if (actualNotificationHeight > mNotificationView.getHeight()) {
1241 actualNotificationHeight = mNotificationView.getHeight();
1242 }
1243 if (mNotificationView.getHeight() > 0) {
1244 mPercentageFromBottom = (int) Math.abs(
1245 actualNotificationHeight / mNotificationView.getHeight() * 100);
1246 boolean isUp = distanceY > 0;
1247
1248 // This check is to figure out if onScroll was called while swiping the card at
1249 // bottom of the list. At that time we should not allow notification shade to
1250 // close. We are also checking for the upwards swipe gesture here because it is
1251 // possible if a user is closing the notification shade and while swiping starts
1252 // to open again but does not fling. At that time we should allow the
1253 // notification shade to close fully or else it would stuck in between.
1254 if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
1255 > SWIPE_DOWN_MIN_DISTANCE && isUp) {
1256 setNotificationViewClipBounds((int) actualNotificationHeight);
1257 mIsTracking = true;
1258 } else if (!isUp) {
1259 setNotificationViewClipBounds((int) actualNotificationHeight);
1260 }
1261 }
1262 // if we return true the the items in RV won't be scrollable.
1263 return false;
1264 }
1265
1266
1267 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001268 public boolean onFling(MotionEvent event1, MotionEvent event2,
1269 float velocityX, float velocityY) {
Priyank Singha01f2432019-05-17 16:33:57 -07001270 // should not fling if the touch does not start when view is at the bottom of the list.
1271 if (!mNotificationListAtBottomAtTimeOfTouch) {
Priyank Singhbae09ae2019-04-25 18:17:02 -07001272 return false;
1273 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001274 if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
1275 || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
1276 // swipe was not vertical or was not fast enough
1277 return false;
1278 }
1279 boolean isUp = velocityY < 0;
Priyank Singhb0241582019-04-19 18:46:26 -07001280 if (isUp) {
Brad Stenning3b0d7642019-03-28 11:04:30 -07001281 close();
1282 return true;
Priyank Singhb0241582019-04-19 18:46:26 -07001283 } else {
1284 // we should close the shade
1285 animateNotificationPanel(velocityY, false);
Brad Stenning3b0d7642019-03-28 11:04:30 -07001286 }
1287 return false;
1288 }
Priyank Singhb0241582019-04-19 18:46:26 -07001289
Brad Stenning3b0d7642019-03-28 11:04:30 -07001290 protected abstract void close();
1291 }
1292
Priyank Singha47b6f72019-05-28 13:02:47 -07001293 /**
1294 * To be installed on the nav bars.
1295 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001296 private abstract class NavBarCloseNotificationGestureListener extends
1297 CloseNotificationGestureListener {
1298 @Override
1299 public boolean onSingleTapUp(MotionEvent e) {
Priyank Singhb0241582019-04-19 18:46:26 -07001300 mClosingVelocity = DEFAULT_FLING_VELOCITY;
Priyank Singhb9ab0522019-05-03 15:36:25 -07001301 if (mPanelExpanded) {
1302 close();
1303 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001304 return super.onSingleTapUp(e);
1305 }
1306
1307 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001308 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1309 float distanceY) {
1310 calculatePercentageFromBottom(event2.getRawY());
1311 setNotificationViewClipBounds((int) event2.getRawY());
1312 return true;
1313 }
1314
1315 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001316 public void onLongPress(MotionEvent e) {
Priyank Singhb0241582019-04-19 18:46:26 -07001317 mClosingVelocity = DEFAULT_FLING_VELOCITY;
Brad Stenning3b0d7642019-03-28 11:04:30 -07001318 close();
1319 super.onLongPress(e);
1320 }
Adora Zhang0237b9c2019-03-08 12:27:09 -08001321 }
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001322
1323 /**
Priyank Singha47b6f72019-05-28 13:02:47 -07001324 * To be installed on the handle bar.
1325 */
1326 private class HandleBarCloseNotificationGestureListener extends
1327 GestureDetector.SimpleOnGestureListener {
1328
1329 @Override
1330 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1331 float distanceY) {
1332 calculatePercentageFromBottom(event2.getRawY());
1333 // To prevent the jump in the clip bounds while closing the notification shade using
1334 // the handle bar we should calculate the height using the diff of event1 and event2.
1335 // This will help the notification shade to clip smoothly as the event2 value changes
1336 // as event1 value will be fixed.
1337 int clipHeight =
1338 mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
1339 setNotificationViewClipBounds(clipHeight);
1340 return true;
1341 }
1342 }
1343
1344 /**
1345 * SystemUi version of the notification manager that overrides methods such that the
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001346 * notifications end up in the status bar layouts instead of a standalone window.
1347 */
1348 private class CarSystemUIHeadsUpNotificationManager extends CarHeadsUpNotificationManager {
1349
1350 CarSystemUIHeadsUpNotificationManager(Context context,
1351 NotificationClickHandlerFactory clickHandlerFactory,
1352 NotificationDataManager notificationDataManager) {
1353 super(context, clickHandlerFactory, notificationDataManager);
1354 }
1355
1356 @Override
1357 protected View createHeadsUpPanel() {
1358 // In SystemUi the view is already in the window so just return a reference.
1359 return mStatusBarWindow.findViewById(R.id.notification_headsup);
1360 }
1361
1362 @Override
1363 protected void addHeadsUpPanelToDisplay() {
1364 // Set the panel initial state to invisible
1365 mHeadsUpPanel.setVisibility(View.INVISIBLE);
1366 }
1367
1368 @Override
Priyank Singh8bd920f2019-06-04 15:21:46 -07001369 protected void setInternalInsetsInfo(ViewTreeObserver.InternalInsetsInfo info,
1370 HeadsUpEntry currentNotification, boolean panelExpanded) {
1371 super.setInternalInsetsInfo(info, currentNotification, mPanelExpanded);
1372 }
1373
1374 @Override
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001375 protected void setHeadsUpVisible() {
Priyank Singh7e9344f2019-05-07 14:30:13 -07001376 // if the Notifications panel is showing don't show the Heads up
Priyank Singh8bd920f2019-06-04 15:21:46 -07001377 if (!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) {
Priyank Singh7e9344f2019-05-07 14:30:13 -07001378 return;
1379 }
Priyank Singh8bd920f2019-06-04 15:21:46 -07001380
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001381 super.setHeadsUpVisible();
1382 if (mHeadsUpPanel.getVisibility() == View.VISIBLE) {
1383 mStatusBarWindowController.setHeadsUpShowing(true);
1384 mStatusBarWindowController.setForceStatusBarVisible(true);
1385 }
1386 }
1387
1388 @Override
1389 protected void removeNotificationFromPanel(HeadsUpEntry currentHeadsUpNotification) {
1390 super.removeNotificationFromPanel(currentHeadsUpNotification);
1391 // If the panel ended up empty and hidden we can remove it from SystemUi
1392 if (mHeadsUpPanel.getVisibility() != View.VISIBLE) {
1393 mStatusBarWindowController.setHeadsUpShowing(false);
1394 mStatusBarWindowController.setForceStatusBarVisible(false);
1395 }
1396 }
1397 }
Rakesh Iyer1186faa2015-12-07 16:48:46 -08001398}