blob: 2874ce60bf16890e1e362019706198e97d2e24df [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;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070029import android.content.Context;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080030import android.graphics.PixelFormat;
Priyank Singhb0241582019-04-19 18:46:26 -070031import android.graphics.Rect;
Anthony Chene658cc22017-04-27 11:17:35 -070032import android.graphics.drawable.Drawable;
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080033import android.util.Log;
Brad Stenning3b0d7642019-03-28 11:04:30 -070034import android.view.GestureDetector;
Brad Stenning078235b2017-12-18 08:25:10 -080035import android.view.Gravity;
Brad Stenning3b0d7642019-03-28 11:04:30 -070036import android.view.MotionEvent;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080037import android.view.View;
Anthony Chen0801a5c2017-03-22 09:54:37 -070038import android.view.ViewGroup;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080039import android.view.ViewGroup.LayoutParams;
Priyank Singhb9ab0522019-05-03 15:36:25 -070040import android.view.ViewTreeObserver;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080041import android.view.WindowManager;
Anthony Chene658cc22017-04-27 11:17:35 -070042
Brad Stenning3b0d7642019-03-28 11:04:30 -070043import androidx.annotation.NonNull;
44import androidx.recyclerview.widget.RecyclerView;
45
Brad Stenning2ccc2a42019-04-15 10:17:01 -070046import com.android.car.notification.CarHeadsUpNotificationManager;
Brad Stenning3b0d7642019-03-28 11:04:30 -070047import com.android.car.notification.CarNotificationListener;
48import com.android.car.notification.CarNotificationView;
49import com.android.car.notification.CarUxRestrictionManagerWrapper;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070050import com.android.car.notification.HeadsUpEntry;
Brad Stenning3b0d7642019-03-28 11:04:30 -070051import com.android.car.notification.NotificationClickHandlerFactory;
Brad Stenning2ccc2a42019-04-15 10:17:01 -070052import com.android.car.notification.NotificationDataManager;
Brad Stenning3b0d7642019-03-28 11:04:30 -070053import com.android.car.notification.NotificationViewController;
54import com.android.car.notification.PreprocessingManager;
Matthew Ng94380652019-04-08 13:43:07 -070055import com.android.internal.statusbar.RegisterStatusBarResult;
Winson Chung2db35572017-10-09 15:08:30 -070056import com.android.keyguard.KeyguardUpdateMonitor;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070057import com.android.systemui.BatteryMeterView;
Jason Monk27d01a622018-12-10 15:57:09 -050058import com.android.systemui.CarSystemUIFactory;
Jason Monk9c7844c2017-01-18 15:21:53 -050059import com.android.systemui.Dependency;
Winson Chung2db35572017-10-09 15:08:30 -070060import com.android.systemui.Prefs;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080061import com.android.systemui.R;
Jason Monk27d01a622018-12-10 15:57:09 -050062import com.android.systemui.SystemUIFactory;
Winson Chung2db35572017-10-09 15:08:30 -070063import com.android.systemui.classifier.FalsingLog;
Dave Mankoffdde5ee62019-05-02 17:36:11 -040064import com.android.systemui.classifier.FalsingManagerFactory;
Jason Monkaa573e92017-01-27 17:00:29 -050065import com.android.systemui.fragments.FragmentHostManager;
jovanak622def22019-06-11 11:51:54 -070066import com.android.systemui.keyguard.ScreenLifecycle;
Brad Stenning8d1a51c2018-11-20 17:34:16 -080067import com.android.systemui.plugins.qs.QS;
68import com.android.systemui.qs.car.CarQSFragment;
Winson Chungb05b3982017-11-01 18:02:43 -070069import com.android.systemui.shared.system.ActivityManagerWrapper;
Winson Chung67f5c8b2018-09-24 12:09:19 -070070import com.android.systemui.shared.system.TaskStackChangeListener;
Priyank Singhb0241582019-04-19 18:46:26 -070071import com.android.systemui.statusbar.FlingAnimationUtils;
Rakesh Iyer2790a372016-01-22 15:33:39 -080072import com.android.systemui.statusbar.StatusBarState;
Brad Stenning38b46f82018-03-27 13:57:29 -070073import com.android.systemui.statusbar.car.hvac.HvacController;
Brad Stenningf084d882018-08-08 13:34:21 -070074import com.android.systemui.statusbar.car.hvac.TemperatureView;
Jason Monkaa573e92017-01-27 17:00:29 -050075import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050076import com.android.systemui.statusbar.phone.StatusBar;
Anthony Chenda62fdcd52016-04-06 16:15:14 -070077import com.android.systemui.statusbar.policy.BatteryController;
Brad Stenning224b5b32018-03-28 21:26:57 -070078import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Jason Monk9c7844c2017-01-18 15:21:53 -050079import com.android.systemui.statusbar.policy.UserSwitcherController;
Rakesh Iyer1186faa2015-12-07 16:48:46 -080080
Qiming Shib9dfb922017-04-13 16:23:18 +080081import java.io.FileDescriptor;
82import java.io.PrintWriter;
83import java.util.Map;
davidln54739152018-07-02 13:25:44 -070084
Rakesh Iyer1186faa2015-12-07 16:48:46 -080085/**
86 * A status bar (and navigation bar) tailored for the automotive use case.
87 */
Jason Monk2a6ea9c2017-01-26 11:14:51 -050088public class CarStatusBar extends StatusBar implements
Anthony Chenda62fdcd52016-04-06 16:15:14 -070089 CarBatteryController.BatteryViewHandler {
Rakesh Iyer74ebabe2016-03-11 10:10:43 -080090 private static final String TAG = "CarStatusBar";
Priyank Singhb0241582019-04-19 18:46:26 -070091 // used to calculate how fast to open or close the window
92 private static final float DEFAULT_FLING_VELOCITY = 0;
93 // max time a fling animation takes
94 private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
95 // acceleration rate for the fling animation
96 private static final float FLING_SPEED_UP_FACTOR = 0.6f;
97
98 private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
99 private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
Rakesh Iyer74ebabe2016-03-11 10:10:43 -0800100
Victor Chan1c6d0582016-01-09 16:26:37 -0800101 private TaskStackListenerImpl mTaskStackListener;
Victor Chan1c6d0582016-01-09 16:26:37 -0800102
Rakesh Iyer2790a372016-01-22 15:33:39 -0800103 private FullscreenUserSwitcher mFullscreenUserSwitcher;
Victor Chan1c6d0582016-01-09 16:26:37 -0800104
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700105 private CarBatteryController mCarBatteryController;
106 private BatteryMeterView mBatteryMeterView;
Anthony Chene658cc22017-04-27 11:17:35 -0700107 private Drawable mNotificationPanelBackground;
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700108
Scott Randolphf4085822017-06-27 15:23:01 -0700109 private ViewGroup mNavigationBarWindow;
Brad Stenning078235b2017-12-18 08:25:10 -0800110 private ViewGroup mLeftNavigationBarWindow;
111 private ViewGroup mRightNavigationBarWindow;
Jason Monk49fa0162017-01-11 09:21:56 -0500112 private CarNavigationBarView mNavigationBarView;
Brad Stenning078235b2017-12-18 08:25:10 -0800113 private CarNavigationBarView mLeftNavigationBarView;
114 private CarNavigationBarView mRightNavigationBarView;
Anthony Chen1c59e9f2016-04-11 11:05:48 -0700115
Qiming Shib9dfb922017-04-13 16:23:18 +0800116 private final Object mQueueLock = new Object();
Brad Stenning078235b2017-12-18 08:25:10 -0800117 private boolean mShowLeft;
118 private boolean mShowRight;
119 private boolean mShowBottom;
120 private CarFacetButtonController mCarFacetButtonController;
Brad Stenning224b5b32018-03-28 21:26:57 -0700121 private ActivityManagerWrapper mActivityManagerWrapper;
122 private DeviceProvisionedController mDeviceProvisionedController;
123 private boolean mDeviceIsProvisioned = true;
Brad Stenningf084d882018-08-08 13:34:21 -0700124 private HvacController mHvacController;
jovanakedba98c2018-09-14 15:46:24 -0700125 private DrivingStateHelper mDrivingStateHelper;
Jonathan Koo007ff072019-05-10 09:24:08 -0700126 private PowerManagerHelper mPowerManagerHelper;
127 private FlingAnimationUtils mFlingAnimationUtils;
jovanakedba98c2018-09-14 15:46:24 -0700128 private SwitchToGuestTimer mSwitchToGuestTimer;
Jonathan Koo007ff072019-05-10 09:24:08 -0700129 private NotificationDataManager mNotificationDataManager;
130 private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
jovanak622def22019-06-11 11:51:54 -0700131 private ScreenLifecycle mScreenLifecycle;
Brad Stenning078235b2017-12-18 08:25:10 -0800132
Brad Stenning3b0d7642019-03-28 11:04:30 -0700133 // The container for the notifications.
134 private CarNotificationView mNotificationView;
135 private RecyclerView mNotificationList;
Priyank Singh647f9e62019-05-16 15:19:53 -0700136 // The handler bar view at the bottom of notification shade.
137 private View mHandleBar;
Adora Zhangdaea9112019-04-11 17:47:40 -0700138 // The controller for the notification view.
139 private NotificationViewController mNotificationViewController;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700140 // The state of if the notification list is currently showing the bottom.
141 private boolean mNotificationListAtBottom;
142 // Was the notification list at the bottom when the user first touched the screen
143 private boolean mNotificationListAtBottomAtTimeOfTouch;
144 // To be attached to the navigation bars such that they can close the notification panel if
145 // it's open.
146 private View.OnTouchListener mNavBarNotificationTouchListener;
147
Priyank Singhb0241582019-04-19 18:46:26 -0700148 // Percentage from top of the screen after which the notification shade will open. This value
149 // will be used while opening the notification shade.
150 private int mSettleOpenPercentage;
151 // Percentage from top of the screen below which the notification shade will close. This
152 // value will be used while closing the notification shade.
153 private int mSettleClosePercentage;
154 // Percentage of notification shade open from top of the screen.
155 private int mPercentageFromBottom;
156 // If notification shade is animation to close or to open.
157 private boolean mIsNotificationAnimating;
158
159 // Tracks when the notification shade is being scrolled. This refers to the glass pane being
160 // scrolled not the recycler view.
161 private boolean mIsTracking;
162 private float mFirstTouchDownOnGlassPane;
163
164 // If the notification card inside the recycler view is being swiped.
165 private boolean mIsNotificationCardSwiping;
166 // If notification shade is being swiped vertically to close.
167 private boolean mIsSwipingVerticallyToClose;
Priyank Singh8bd920f2019-06-04 15:21:46 -0700168 // Whether heads-up notifications should be shown when shade is open.
169 private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
Priyank Singhb0241582019-04-19 18:46:26 -0700170
Jonathan Koo007ff072019-05-10 09:24:08 -0700171 private final CarPowerStateListener mCarPowerStateListener =
172 (int state) -> {
173 // When the car powers on, clear all notifications and mute/unread states.
174 Log.d(TAG, "New car power state: " + state);
175 if (state == CarPowerStateListener.ON) {
176 if (mNotificationClickHandlerFactory != null) {
177 mNotificationClickHandlerFactory.clearAllNotifications();
178 }
179 if (mNotificationDataManager != null) {
180 mNotificationDataManager.clearAll();
181 }
182 }
183 };
184
Victor Chan1c6d0582016-01-09 16:26:37 -0800185 @Override
186 public void start() {
Brad Stenningeb6051c2019-02-27 13:46:11 -0800187 // get the provisioned state before calling the parent class since it's that flow that
188 // builds the nav bar
189 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
190 mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
Victor Chan1c6d0582016-01-09 16:26:37 -0800191 super.start();
Jaewan Kim938a50b2016-03-14 17:35:43 +0900192 mTaskStackListener = new TaskStackListenerImpl();
Brad Stenning224b5b32018-03-28 21:26:57 -0700193 mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
194 mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700195
Jason Monke59dc402018-08-16 12:05:01 -0400196 mNotificationPanel.setScrollingEnabled(true);
Priyank Singhb0241582019-04-19 18:46:26 -0700197 mSettleOpenPercentage = mContext.getResources().getInteger(
198 R.integer.notification_settle_open_percentage);
199 mSettleClosePercentage = mContext.getResources().getInteger(
200 R.integer.notification_settle_close_percentage);
Jonathan Koo007ff072019-05-10 09:24:08 -0700201 mFlingAnimationUtils = new FlingAnimationUtils(mContext,
Priyank Singhb0241582019-04-19 18:46:26 -0700202 FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800203
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800204 createBatteryController();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700205 mCarBatteryController.startListening();
Brad Stenning38b46f82018-03-27 13:57:29 -0700206
Brad Stenningf084d882018-08-08 13:34:21 -0700207 mHvacController.connectToCarService();
davidln54739152018-07-02 13:25:44 -0700208
Jason Monk27d01a622018-12-10 15:57:09 -0500209 CarSystemUIFactory factory = SystemUIFactory.getInstance();
Brad Stenning224b5b32018-03-28 21:26:57 -0700210 if (!mDeviceIsProvisioned) {
211 mDeviceProvisionedController.addCallback(
212 new DeviceProvisionedController.DeviceProvisionedListener() {
213 @Override
214 public void onDeviceProvisionedChanged() {
Brad Stenninga2d87d82019-01-24 12:33:10 -0800215 mHandler.post(() -> {
216 // on initial boot we are getting a call even though the value
217 // is the same so we are confirming the reset is needed
218 boolean deviceProvisioned =
219 mDeviceProvisionedController.isDeviceProvisioned();
220 if (mDeviceIsProvisioned != deviceProvisioned) {
221 mDeviceIsProvisioned = deviceProvisioned;
222 restartNavBars();
223 }
224 });
Brad Stenning224b5b32018-03-28 21:26:57 -0700225 }
226 });
227 }
jovanakedba98c2018-09-14 15:46:24 -0700228
229 // Register a listener for driving state changes.
230 mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
231 mDrivingStateHelper.connectToCarService();
232
Jonathan Koo007ff072019-05-10 09:24:08 -0700233 mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
234 mPowerManagerHelper.connectToCarService();
235
jovanakedba98c2018-09-14 15:46:24 -0700236 mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
jovanak622def22019-06-11 11:51:54 -0700237
238 mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
239 mScreenLifecycle.addObserver(mScreenObserver);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700240 }
241
Brad Stenning224b5b32018-03-28 21:26:57 -0700242 /**
243 * Remove all content from navbars and rebuild them. Used to allow for different nav bars
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800244 * before and after the device is provisioned. . Also for change of density and font size.
Brad Stenning224b5b32018-03-28 21:26:57 -0700245 */
246 private void restartNavBars() {
Brad Stenningf084d882018-08-08 13:34:21 -0700247 // remove and reattach all hvac components such that we don't keep a reference to unused
248 // ui elements
249 mHvacController.removeAllComponents();
250 addTemperatureViewToController(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700251 mCarFacetButtonController.removeAll();
Brad Stenning224b5b32018-03-28 21:26:57 -0700252 if (mNavigationBarWindow != null) {
253 mNavigationBarWindow.removeAllViews();
254 mNavigationBarView = null;
255 }
256
257 if (mLeftNavigationBarWindow != null) {
258 mLeftNavigationBarWindow.removeAllViews();
259 mLeftNavigationBarView = null;
260 }
261
262 if (mRightNavigationBarWindow != null) {
263 mRightNavigationBarWindow.removeAllViews();
264 mRightNavigationBarView = null;
265 }
Brad Stenningf084d882018-08-08 13:34:21 -0700266
Brad Stenning224b5b32018-03-28 21:26:57 -0700267 buildNavBarContent();
Brad Stenning75221642019-03-13 07:33:24 -0700268 // If the UI was rebuilt (day/night change) while the keyguard was up we need to
269 // correctly respect that state.
270 if (mIsKeyguard) {
271 updateNavBarForKeyguardContent();
272 }
Brad Stenning4534f442019-04-01 21:43:32 -0700273 // CarFacetButtonController was reset therefore we need to re-add the status bar elements
274 // to the controller.
275 mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
Brad Stenning224b5b32018-03-28 21:26:57 -0700276 }
277
Brad Stenningf084d882018-08-08 13:34:21 -0700278 private void addTemperatureViewToController(View v) {
279 if (v instanceof TemperatureView) {
Brad Stenningf084d882018-08-08 13:34:21 -0700280 mHvacController.addHvacTextView((TemperatureView) v);
281 } else if (v instanceof ViewGroup) {
282 ViewGroup viewGroup = (ViewGroup) v;
283 for (int i = 0; i < viewGroup.getChildCount(); i++) {
284 addTemperatureViewToController(viewGroup.getChildAt(i));
285 }
286 }
287 }
288
Brad Stenning7e411812018-04-13 22:52:39 -0700289 /**
290 * Allows for showing or hiding just the navigation bars. This is indented to be used when
291 * the full screen user selector is shown.
292 */
davidln54739152018-07-02 13:25:44 -0700293 void setNavBarVisibility(@View.Visibility int visibility) {
Brad Stenning7e411812018-04-13 22:52:39 -0700294 if (mNavigationBarWindow != null) {
295 mNavigationBarWindow.setVisibility(visibility);
296 }
297 if (mLeftNavigationBarWindow != null) {
298 mLeftNavigationBarWindow.setVisibility(visibility);
299 }
300 if (mRightNavigationBarWindow != null) {
301 mRightNavigationBarWindow.setVisibility(visibility);
302 }
303 }
304
305
306 @Override
307 public boolean hideKeyguard() {
308 boolean result = super.hideKeyguard();
309 if (mNavigationBarView != null) {
310 mNavigationBarView.hideKeyguardButtons();
311 }
312 if (mLeftNavigationBarView != null) {
313 mLeftNavigationBarView.hideKeyguardButtons();
314 }
315 if (mRightNavigationBarView != null) {
316 mRightNavigationBarView.hideKeyguardButtons();
317 }
318 return result;
319 }
320
Brad Stenning7e411812018-04-13 22:52:39 -0700321 @Override
322 public void showKeyguard() {
323 super.showKeyguard();
Brad Stenning75221642019-03-13 07:33:24 -0700324 updateNavBarForKeyguardContent();
325 }
326
327 /**
328 * Switch to the keyguard applicable content contained in the nav bars
329 */
330 private void updateNavBarForKeyguardContent() {
Brad Stenning7e411812018-04-13 22:52:39 -0700331 if (mNavigationBarView != null) {
332 mNavigationBarView.showKeyguardButtons();
333 }
334 if (mLeftNavigationBarView != null) {
335 mLeftNavigationBarView.showKeyguardButtons();
336 }
337 if (mRightNavigationBarView != null) {
338 mRightNavigationBarView.showKeyguardButtons();
339 }
340 }
Brad Stenning224b5b32018-03-28 21:26:57 -0700341
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700342 @Override
Matthew Ng94380652019-04-08 13:43:07 -0700343 protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
344 super.makeStatusBarView(result);
Jason Monk27d01a622018-12-10 15:57:09 -0500345 mHvacController = new HvacController(mContext);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700346
Guobin Zhangeef0e0c2019-02-01 11:54:24 -0800347 CarSystemUIFactory factory = SystemUIFactory.getInstance();
348 mCarFacetButtonController = factory.getCarDependencyComponent()
davidlna06b4b22019-05-30 14:25:01 -0700349 .getCarFacetButtonController();
Anthony Chene658cc22017-04-27 11:17:35 -0700350 mNotificationPanelBackground = getDefaultWallpaper();
351 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
352
Jason Monkaa573e92017-01-27 17:00:29 -0500353 FragmentHostManager manager = FragmentHostManager.get(mStatusBarWindow);
354 manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
Anthony Chene658cc22017-04-27 11:17:35 -0700355 mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700356
Jason Monkaa573e92017-01-27 17:00:29 -0500357 // By default, the BatteryMeterView should not be visible. It will be toggled
358 // when a device has connected by bluetooth.
359 mBatteryMeterView.setVisibility(View.GONE);
Jason Monkaa573e92017-01-27 17:00:29 -0500360 });
Brad Stenning3b0d7642019-03-28 11:04:30 -0700361
362 connectNotificationsUI();
363 }
364
365 /**
366 * Attach the notification listeners and controllers to the UI as well as build all the
367 * touch listeners needed for opening and closing the notification panel
368 */
369 private void connectNotificationsUI() {
370 // Attached to the status bar to detect pull down of the notification shade.
371 GestureDetector openGestureDetector = new GestureDetector(mContext,
372 new OpenNotificationGestureListener() {
373 @Override
374 protected void openNotification() {
375 animateExpandNotificationsPanel();
376 }
377 });
378 // Attached to the notification ui to detect close request of the notification shade.
379 GestureDetector closeGestureDetector = new GestureDetector(mContext,
380 new CloseNotificationGestureListener() {
381 @Override
382 protected void close() {
Brad Stenning44d39072019-04-20 11:53:17 -0700383 if (mPanelExpanded) {
384 animateCollapsePanels();
385 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700386 }
387 });
388 // Attached to the NavBars to close the notification shade
389 GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
390 new NavBarCloseNotificationGestureListener() {
391 @Override
392 protected void close() {
Brad Stenning44d39072019-04-20 11:53:17 -0700393 if (mPanelExpanded) {
394 animateCollapsePanels();
395 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700396 }
397 });
Priyank Singha47b6f72019-05-28 13:02:47 -0700398
399 // Attached to the Handle bar to close the notification shade
400 GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
401 new HandleBarCloseNotificationGestureListener());
402
Brad Stenning3b0d7642019-03-28 11:04:30 -0700403 mNavBarNotificationTouchListener =
Priyank Singhb0241582019-04-19 18:46:26 -0700404 (v, event) -> {
405 boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
406 if (consumed) {
407 return true;
408 }
Priyank Singha47b6f72019-05-28 13:02:47 -0700409 maybeCompleteAnimation(event);
Priyank Singhb0241582019-04-19 18:46:26 -0700410 return true;
411 };
Brad Stenning3b0d7642019-03-28 11:04:30 -0700412
Brad Stenningc622f1d2019-01-29 11:24:11 -0800413 // The following are the ui elements that the user would call the status bar.
414 // This will set the status bar so it they can make call backs.
415 CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
416 topBar.setStatusBar(this);
Priyank Singhb0241582019-04-19 18:46:26 -0700417 topBar.setStatusBarWindowTouchListener((v1, event1) -> {
418
419 boolean consumed = openGestureDetector.onTouchEvent(event1);
420 if (consumed) {
421 return true;
422 }
Priyank Singha47b6f72019-05-28 13:02:47 -0700423 maybeCompleteAnimation(event1);
Priyank Singhb0241582019-04-19 18:46:26 -0700424 return true;
425 }
426 );
Brad Stenning3b0d7642019-03-28 11:04:30 -0700427
Jonathan Koo007ff072019-05-10 09:24:08 -0700428 mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(
Brad Stenning3b0d7642019-03-28 11:04:30 -0700429 mBarService,
430 launchResult -> {
431 if (launchResult == ActivityManager.START_TASK_TO_FRONT
432 || launchResult == ActivityManager.START_SUCCESS) {
433 animateCollapsePanels();
434 }
435 });
Brad Stenningd3ede642019-04-23 21:19:27 -0700436 Car car = Car.createCar(mContext);
437 CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
438 car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700439 CarNotificationListener carNotificationListener = new CarNotificationListener();
440 CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
441 new CarUxRestrictionManagerWrapper();
Brad Stenningd3ede642019-04-23 21:19:27 -0700442 carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700443
Jonathan Koo007ff072019-05-10 09:24:08 -0700444 mNotificationDataManager = new NotificationDataManager();
445 mNotificationDataManager.setOnUnseenCountUpdateListener(
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700446 () -> {
Jonathan Koo307bda92019-05-14 16:53:48 -0700447 if (mNavigationBarView != null && mNotificationDataManager != null) {
448 Boolean hasUnseen =
449 mNotificationDataManager.getUnseenNotificationCount() > 0;
450 if (mNavigationBarView != null) {
451 mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
452 }
453
454 if (mLeftNavigationBarView != null) {
455 mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
456 }
457
458 if (mRightNavigationBarView != null) {
459 mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
460 }
461 }
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700462 });
463
Priyank Singh8bd920f2019-06-04 15:21:46 -0700464 mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
465 R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
Brad Stenning2ccc2a42019-04-15 10:17:01 -0700466 CarHeadsUpNotificationManager carHeadsUpNotificationManager =
Jonathan Koo007ff072019-05-10 09:24:08 -0700467 new CarSystemUIHeadsUpNotificationManager(mContext,
468 mNotificationClickHandlerFactory, mNotificationDataManager);
469 mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
Brad Stenning2ccc2a42019-04-15 10:17:01 -0700470
Brad Stenning3b0d7642019-03-28 11:04:30 -0700471 carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
Jonathan Koo007ff072019-05-10 09:24:08 -0700472 carHeadsUpNotificationManager, mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700473
474 mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
475 View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
Priyank Singh647f9e62019-05-16 15:19:53 -0700476 mHandleBar = mStatusBarWindow.findViewById(R.id.handle_bar);
Jonathan Koo007ff072019-05-10 09:24:08 -0700477 mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
478 mNotificationView.setNotificationDataManager(mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700479
480 // The glass pane is used to view touch events before passed to the notification list.
481 // This allows us to initialize gesture listeners and detect when to close the notifications
482 glassPane.setOnTouchListener((v, event) -> {
483 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
484 mNotificationListAtBottomAtTimeOfTouch = false;
485 }
486 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
Priyank Singhb0241582019-04-19 18:46:26 -0700487 mFirstTouchDownOnGlassPane = event.getRawX();
Brad Stenning3b0d7642019-03-28 11:04:30 -0700488 mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
Priyank Singhb0241582019-04-19 18:46:26 -0700489 // Reset the tracker when there is a touch down on the glass pane.
490 mIsTracking = false;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700491 // Pass the down event to gesture detector so that it knows where the touch event
492 // started.
493 closeGestureDetector.onTouchEvent(event);
494 }
495 return false;
496 });
Priyank Singha47b6f72019-05-28 13:02:47 -0700497
498 mHandleBar.setOnTouchListener((v, event) -> {
499 handleBarCloseNotificationGestureDetector.onTouchEvent(event);
500 maybeCompleteAnimation(event);
501 return true;
502 });
503
Brad Stenning3b0d7642019-03-28 11:04:30 -0700504 mNotificationList = mNotificationView.findViewById(R.id.notifications);
505 mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
506 @Override
507 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
508 super.onScrolled(recyclerView, dx, dy);
509 if (!mNotificationList.canScrollVertically(1)) {
510 mNotificationListAtBottom = true;
511 return;
512 }
513 mNotificationListAtBottom = false;
Priyank Singhb0241582019-04-19 18:46:26 -0700514 mIsSwipingVerticallyToClose = false;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700515 mNotificationListAtBottomAtTimeOfTouch = false;
516 }
517 });
518 mNotificationList.setOnTouchListener(new View.OnTouchListener() {
519 @Override
520 public boolean onTouch(View v, MotionEvent event) {
Priyank Singhb0241582019-04-19 18:46:26 -0700521 mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
522 > SWIPE_MAX_OFF_PATH;
523 if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
524 // We need to save the state here as if notification card is swiping we will
525 // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
526 // closing the notification shade while the notification card is being swiped.
527 mIsSwipingVerticallyToClose = true;
528 }
529
530 // If the card is swiping we should not allow the notification shade to close.
531 // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
532 // for us. We are also checking for mIsTracking because while swiping the
533 // notification shade to close if the user goes a bit horizontal while swiping
534 // upwards then also this should close.
535 if (mIsNotificationCardSwiping && !mIsTracking) {
536 mNotificationListAtBottomAtTimeOfTouch = false;
537 }
538
Priyank Singhbae09ae2019-04-25 18:17:02 -0700539 boolean handled = closeGestureDetector.onTouchEvent(event);
Priyank Singhb0241582019-04-19 18:46:26 -0700540 boolean isTracking = mIsTracking;
Priyank Singh647f9e62019-05-16 15:19:53 -0700541 Rect rect = mNotificationView.getClipBounds();
Priyank Singhf3912862019-04-22 10:41:58 -0700542 float clippedHeight = 0;
543 if (rect != null) {
544 clippedHeight = rect.bottom;
545 }
Priyank Singhb0241582019-04-19 18:46:26 -0700546 if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
547 && mIsSwipingVerticallyToClose) {
548 if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
549 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
550 } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
551 // this can be caused when user is at the end of the list and trying to
552 // fling to top of the list by scrolling down.
553 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
554 }
555 }
556
Brad Stenning3b0d7642019-03-28 11:04:30 -0700557 // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
558 // the event has been passed to the closeGestureDetector above, such that the
559 // closeGestureDetector sees the up event before the state has changed.
560 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
561 mNotificationListAtBottomAtTimeOfTouch = false;
562 }
Priyank Singhb0241582019-04-19 18:46:26 -0700563 return handled || isTracking;
Brad Stenning3b0d7642019-03-28 11:04:30 -0700564 }
565 });
566
Adora Zhangdaea9112019-04-11 17:47:40 -0700567 mNotificationViewController = new NotificationViewController(
Brad Stenning3b0d7642019-03-28 11:04:30 -0700568 mNotificationView,
569 PreprocessingManager.getInstance(mContext),
570 carNotificationListener,
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700571 carUxRestrictionManagerWrapper,
Jonathan Koo007ff072019-05-10 09:24:08 -0700572 mNotificationDataManager);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700573 mNotificationViewController.enable();
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700574 }
575
Brad Stenning44d39072019-04-20 11:53:17 -0700576 /**
577 * @return true if the notification panel is currently visible
578 */
579 boolean isNotificationPanelOpen() {
580 return mPanelExpanded;
581 }
582
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800583 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -0700584 public void animateExpandNotificationsPanel() {
585 if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
586 return;
587 }
588 // scroll to top
589 mNotificationList.scrollToPosition(0);
590 mStatusBarWindowController.setPanelVisible(true);
591 mNotificationView.setVisibility(View.VISIBLE);
Priyank Singhb0241582019-04-19 18:46:26 -0700592 animateNotificationPanel(mOpeningVelocity, false);
593
Brad Stenning3b0d7642019-03-28 11:04:30 -0700594 setPanelExpanded(true);
595 }
596
597 @Override
598 public void animateCollapsePanels(int flags, boolean force, boolean delayed,
599 float speedUpFactor) {
600 super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
601 if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
602 return;
603 }
604 mStatusBarWindowController.setStatusBarFocusable(false);
605 mStatusBarWindow.cancelExpandHelper();
606 mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
Priyank Singhb0241582019-04-19 18:46:26 -0700607
608 animateNotificationPanel(mClosingVelocity, true);
609
610 if (!mIsTracking) {
611 mStatusBarWindowController.setPanelVisible(false);
612 mNotificationView.setVisibility(View.INVISIBLE);
613 }
614
Brad Stenning3b0d7642019-03-28 11:04:30 -0700615 setPanelExpanded(false);
616 }
617
Priyank Singha47b6f72019-05-28 13:02:47 -0700618 private void maybeCompleteAnimation(MotionEvent event) {
619 if (event.getActionMasked() == MotionEvent.ACTION_UP
620 && mNotificationView.getVisibility() == View.VISIBLE) {
621 if (mSettleClosePercentage < mPercentageFromBottom) {
622 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
623 } else {
624 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
625 }
626 }
627 }
628
Priyank Singhb0241582019-04-19 18:46:26 -0700629 /**
630 * Animates the notification shade from one position to other. This is used to either open or
Priyank Singh245bd122019-04-21 15:42:44 -0700631 * close the notification shade completely with a velocity. If the animation is to close the
Priyank Singhb0241582019-04-19 18:46:26 -0700632 * notification shade this method also makes the view invisible after animation ends.
633 */
634 private void animateNotificationPanel(float velocity, boolean isClosing) {
Priyank Singhb0241582019-04-19 18:46:26 -0700635 float to = 0;
636 if (!isClosing) {
637 to = mNotificationView.getHeight();
638 }
Priyank Singhb9ab0522019-05-03 15:36:25 -0700639
Priyank Singh647f9e62019-05-16 15:19:53 -0700640 Rect rect = mNotificationView.getClipBounds();
Priyank Singhb9ab0522019-05-03 15:36:25 -0700641 if (rect != null) {
642 float from = rect.bottom;
643 animate(from, to, velocity, isClosing);
644 return;
645 }
646
647 // We will only be here if the shade is being opened programmatically.
648 ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
649 notificationTreeObserver.addOnGlobalLayoutListener(
650 new ViewTreeObserver.OnGlobalLayoutListener() {
651 @Override
652 public void onGlobalLayout() {
653 ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
654 obs.removeOnGlobalLayoutListener(this);
655 float to = mNotificationView.getHeight();
656 animate(/* from= */ 0, to, velocity, isClosing);
657 }
658 });
659 }
660
661 private void animate(float from, float to, float velocity, boolean isClosing) {
Priyank Singhb0241582019-04-19 18:46:26 -0700662 if (mIsNotificationAnimating) {
663 return;
664 }
665 mIsNotificationAnimating = true;
666 mIsTracking = true;
667 ValueAnimator animator = ValueAnimator.ofFloat(from, to);
668 animator.addUpdateListener(
669 animation -> {
670 float animatedValue = (Float) animation.getAnimatedValue();
671 setNotificationViewClipBounds((int) animatedValue);
672 });
673 animator.addListener(new AnimatorListenerAdapter() {
674 @Override
675 public void onAnimationEnd(Animator animation) {
676 super.onAnimationEnd(animation);
677 mIsNotificationAnimating = false;
678 mIsTracking = false;
679 mOpeningVelocity = DEFAULT_FLING_VELOCITY;
680 mClosingVelocity = DEFAULT_FLING_VELOCITY;
681 if (isClosing) {
682 mStatusBarWindowController.setPanelVisible(false);
683 mNotificationView.setVisibility(View.INVISIBLE);
Priyank Singh647f9e62019-05-16 15:19:53 -0700684 mNotificationView.setClipBounds(null);
Adora Zhangdaea9112019-04-11 17:47:40 -0700685 mNotificationViewController.setIsInForeground(false);
Priyank Singhb0241582019-04-19 18:46:26 -0700686 // let the status bar know that the panel is closed
687 setPanelExpanded(false);
688 } else {
Adora Zhangdaea9112019-04-11 17:47:40 -0700689 mNotificationViewController.setIsInForeground(true);
Priyank Singhb0241582019-04-19 18:46:26 -0700690 // let the status bar know that the panel is open
Jonathan Kooc9f71aa2019-05-06 08:20:14 -0700691 mNotificationView.setVisibleNotificationsAsSeen();
Priyank Singhb0241582019-04-19 18:46:26 -0700692 setPanelExpanded(true);
693 }
694 }
695 });
Jonathan Koo007ff072019-05-10 09:24:08 -0700696 mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
Priyank Singhb0241582019-04-19 18:46:26 -0700697 animator.start();
698 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700699
700 @Override
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800701 protected QS createDefaultQSFragment() {
702 return new CarQSFragment();
703 }
704
Rakesh Iyer545e47d2017-01-23 18:10:21 -0800705 private BatteryController createBatteryController() {
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700706 mCarBatteryController = new CarBatteryController(mContext);
707 mCarBatteryController.addBatteryViewHandler(this);
708 return mCarBatteryController;
Victor Chan1c6d0582016-01-09 16:26:37 -0800709 }
710
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800711 @Override
Matthew Ng94380652019-04-08 13:43:07 -0700712 protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
Brad Stenning078235b2017-12-18 08:25:10 -0800713 mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800714 mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
Brad Stenning078235b2017-12-18 08:25:10 -0800715 mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
716
Brad Stenning224b5b32018-03-28 21:26:57 -0700717 buildNavBarWindows();
718 buildNavBarContent();
719 attachNavBarWindows();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700720
Charles Chen10ca70b2018-11-28 00:03:38 +0800721 // There has been a car customized nav bar on the default display, so just create nav bars
722 // on external displays.
Matthew Ng94380652019-04-08 13:43:07 -0700723 mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
Brad Stenning224b5b32018-03-28 21:26:57 -0700724 }
725
726 private void buildNavBarContent() {
727 if (mShowBottom) {
728 buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
729 R.layout.car_navigation_bar_unprovisioned);
730 }
731
732 if (mShowLeft) {
733 buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
734 R.layout.car_left_navigation_bar_unprovisioned);
735 }
736
Brad Stenning078235b2017-12-18 08:25:10 -0800737 if (mShowRight) {
Brad Stenning224b5b32018-03-28 21:26:57 -0700738 buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
739 R.layout.car_right_navigation_bar_unprovisioned);
740 }
741 }
742
743 private void buildNavBarWindows() {
744 if (mShowBottom) {
davidln54739152018-07-02 13:25:44 -0700745 mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
Brad Stenning224b5b32018-03-28 21:26:57 -0700746 R.layout.navigation_bar_window, null);
747 }
748 if (mShowLeft) {
749 mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
davidln54739152018-07-02 13:25:44 -0700750 R.layout.navigation_bar_window, null);
Brad Stenning224b5b32018-03-28 21:26:57 -0700751 }
752 if (mShowRight) {
753 mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
754 R.layout.navigation_bar_window, null);
Brad Stenning078235b2017-12-18 08:25:10 -0800755 }
756
757 }
758
Brad Stenning224b5b32018-03-28 21:26:57 -0700759 private void attachNavBarWindows() {
Brad Stenning078235b2017-12-18 08:25:10 -0800760
Brad Stenning224b5b32018-03-28 21:26:57 -0700761 if (mShowBottom) {
762 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
763 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
764 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
765 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
766 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
767 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
768 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
769 PixelFormat.TRANSLUCENT);
770 lp.setTitle("CarNavigationBar");
771 lp.windowAnimations = 0;
772 mWindowManager.addView(mNavigationBarWindow, lp);
773 }
Brad Stenning3b0d7642019-03-28 11:04:30 -0700774
Brad Stenning224b5b32018-03-28 21:26:57 -0700775 if (mShowLeft) {
776 int width = mContext.getResources().getDimensionPixelSize(
777 R.dimen.car_left_navigation_bar_width);
778 WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
779 width, LayoutParams.MATCH_PARENT,
780 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
781 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
782 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
783 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
784 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
785 PixelFormat.TRANSLUCENT);
786 leftlp.setTitle("LeftCarNavigationBar");
787 leftlp.windowAnimations = 0;
788 leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
789 leftlp.gravity = Gravity.LEFT;
790 mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
791 }
792 if (mShowRight) {
793 int width = mContext.getResources().getDimensionPixelSize(
794 R.dimen.car_right_navigation_bar_width);
795 WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
796 width, LayoutParams.MATCH_PARENT,
797 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
798 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
799 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
800 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
801 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
802 PixelFormat.TRANSLUCENT);
803 rightlp.setTitle("RightCarNavigationBar");
804 rightlp.windowAnimations = 0;
805 rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
806 rightlp.gravity = Gravity.RIGHT;
807 mWindowManager.addView(mRightNavigationBarWindow, rightlp);
808 }
809
810 }
811
812 private void buildBottomBar(int layout) {
Anthony Chen0801a5c2017-03-22 09:54:37 -0700813 // SystemUI requires that the navigation bar view have a parent. Since the regular
814 // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
815 // CarNavigationBarView.
Brad Stenning224b5b32018-03-28 21:26:57 -0700816 View.inflate(mContext, layout, mNavigationBarWindow);
Scott Randolphf4085822017-06-27 15:23:01 -0700817 mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
818 if (mNavigationBarView == null) {
819 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
Brad Stenning078235b2017-12-18 08:25:10 -0800820 throw new RuntimeException("Unable to build botom nav bar due to missing layout");
Scott Randolphf4085822017-06-27 15:23:01 -0700821 }
Brad Stenning078235b2017-12-18 08:25:10 -0800822 mNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700823 addTemperatureViewToController(mNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700824 mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800825 }
826
Brad Stenning224b5b32018-03-28 21:26:57 -0700827 private void buildLeft(int layout) {
828 View.inflate(mContext, layout, mLeftNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800829 mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
830 if (mLeftNavigationBarView == null) {
831 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
832 throw new RuntimeException("Unable to build left nav bar due to missing layout");
833 }
834 mLeftNavigationBarView.setStatusBar(this);
Brad Stenningf084d882018-08-08 13:34:21 -0700835 addTemperatureViewToController(mLeftNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700836 mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800837 }
838
839
Brad Stenning224b5b32018-03-28 21:26:57 -0700840 private void buildRight(int layout) {
841 View.inflate(mContext, layout, mRightNavigationBarWindow);
Brad Stenning078235b2017-12-18 08:25:10 -0800842 mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
843 if (mRightNavigationBarView == null) {
844 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
845 throw new RuntimeException("Unable to build right nav bar due to missing layout");
846 }
Brad Stenningf084d882018-08-08 13:34:21 -0700847 mRightNavigationBarView.setStatusBar(this);
848 addTemperatureViewToController(mRightNavigationBarView);
Brad Stenning3b0d7642019-03-28 11:04:30 -0700849 mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800850 }
851
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800852 @Override
Qiming Shib9dfb922017-04-13 16:23:18 +0800853 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Priyank Singhb0241582019-04-19 18:46:26 -0700854 //When executing dump() function simultaneously, we need to serialize them
Qiming Shib9dfb922017-04-13 16:23:18 +0800855 //to get mStackScroller's position correctly.
856 synchronized (mQueueLock) {
857 pw.println(" mStackScroller: " + viewInfo(mStackScroller));
858 pw.println(" mStackScroller: " + viewInfo(mStackScroller)
859 + " scroll " + mStackScroller.getScrollX()
860 + "," + mStackScroller.getScrollY());
861 }
862
davidln54739152018-07-02 13:25:44 -0700863 pw.print(" mTaskStackListener=");
864 pw.println(mTaskStackListener);
Brad Stenning078235b2017-12-18 08:25:10 -0800865 pw.print(" mCarFacetButtonController=");
866 pw.println(mCarFacetButtonController);
davidln54739152018-07-02 13:25:44 -0700867 pw.print(" mFullscreenUserSwitcher=");
868 pw.println(mFullscreenUserSwitcher);
Qiming Shib9dfb922017-04-13 16:23:18 +0800869 pw.print(" mCarBatteryController=");
870 pw.println(mCarBatteryController);
871 pw.print(" mBatteryMeterView=");
872 pw.println(mBatteryMeterView);
Qiming Shib9dfb922017-04-13 16:23:18 +0800873 pw.print(" mNavigationBarView=");
874 pw.println(mNavigationBarView);
875
876 if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
877 KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
878 }
879
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400880 FalsingManagerFactory.getInstance(mContext).dump(pw);
Qiming Shib9dfb922017-04-13 16:23:18 +0800881 FalsingLog.dump(pw);
882
883 pw.println("SharedPreferences:");
884 for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
davidln54739152018-07-02 13:25:44 -0700885 pw.print(" ");
886 pw.print(entry.getKey());
887 pw.print("=");
888 pw.println(entry.getValue());
Qiming Shib9dfb922017-04-13 16:23:18 +0800889 }
890 }
891
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800892 @Override
Anthony Chenda62fdcd52016-04-06 16:15:14 -0700893 public void showBatteryView() {
894 if (Log.isLoggable(TAG, Log.DEBUG)) {
895 Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
896 }
897
898 if (mBatteryMeterView != null) {
899 mBatteryMeterView.setVisibility(View.VISIBLE);
900 }
901 }
902
903 @Override
904 public void hideBatteryView() {
905 if (Log.isLoggable(TAG, Log.DEBUG)) {
906 Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
907 }
908
909 if (mBatteryMeterView != null) {
910 mBatteryMeterView.setVisibility(View.GONE);
911 }
912 }
913
Victor Chan1c6d0582016-01-09 16:26:37 -0800914 /**
Winson Chung67f5c8b2018-09-24 12:09:19 -0700915 * An implementation of TaskStackChangeListener, that listens for changes in the system
davidln54739152018-07-02 13:25:44 -0700916 * task stack and notifies the navigation bar.
Victor Chan1c6d0582016-01-09 16:26:37 -0800917 */
Winson Chung67f5c8b2018-09-24 12:09:19 -0700918 private class TaskStackListenerImpl extends TaskStackChangeListener {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800919 @Override
Victor Chan1c6d0582016-01-09 16:26:37 -0800920 public void onTaskStackChanged() {
Brad Stenninga5e714c2018-04-12 19:23:39 -0700921 try {
922 mCarFacetButtonController.taskChanged(
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700923 ActivityTaskManager.getService().getAllStackInfos());
Brad Stenninga5e714c2018-04-12 19:23:39 -0700924 } catch (Exception e) {
925 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
926 }
Victor Chan1c6d0582016-01-09 16:26:37 -0800927 }
davidlna06b4b22019-05-30 14:25:01 -0700928
929 @Override
930 public void onTaskDisplayChanged(int taskId, int newDisplayId) {
931 try {
932 mCarFacetButtonController.taskChanged(
933 ActivityTaskManager.getService().getAllStackInfos());
934 } catch (Exception e) {
935 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
936 }
937 }
Victor Chan1c6d0582016-01-09 16:26:37 -0800938 }
Rakesh Iyer2790a372016-01-22 15:33:39 -0800939
jovanakedba98c2018-09-14 15:46:24 -0700940 private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
941 // Check if we need to start the timer every time driving state changes.
942 startSwitchToGuestTimerIfDrivingOnKeyguard();
943 }
944
945 private void startSwitchToGuestTimerIfDrivingOnKeyguard() {
946 if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) {
947 // We're driving while keyguard is up.
948 mSwitchToGuestTimer.start();
949 } else {
950 mSwitchToGuestTimer.cancel();
951 }
952 }
953
Rakesh Iyer2790a372016-01-22 15:33:39 -0800954 @Override
955 protected void createUserSwitcher() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500956 UserSwitcherController userSwitcherController =
957 Dependency.get(UserSwitcherController.class);
958 if (userSwitcherController.useFullscreenUserSwitcher()) {
959 mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700960 mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
Rakesh Iyer2790a372016-01-22 15:33:39 -0800961 } else {
962 super.createUserSwitcher();
963 }
964 }
965
966 @Override
jovanak168dbdb2019-02-18 01:00:43 -0800967 public void setLockscreenUser(int newUserId) {
968 super.setLockscreenUser(newUserId);
969 // Try to dismiss the keyguard after every user switch.
970 dismissKeyguardWhenUserSwitcherNotDisplayed();
971 }
972
973 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400974 public void onStateChanged(int newState) {
975 super.onStateChanged(newState);
jovanakedba98c2018-09-14 15:46:24 -0700976
977 startSwitchToGuestTimerIfDrivingOnKeyguard();
978
jovanak168dbdb2019-02-18 01:00:43 -0800979 if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
980 hideUserSwitcher();
jovanak604ad512018-08-14 18:41:27 -0700981 } else {
jovanak168dbdb2019-02-18 01:00:43 -0800982 dismissKeyguardWhenUserSwitcherNotDisplayed();
983 }
984 }
985
986 /** Makes the full screen user switcher visible, if applicable. */
987 public void showUserSwitcher() {
988 if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
989 mFullscreenUserSwitcher.show(); // Makes the switcher visible.
990 }
991 }
992
993 private void hideUserSwitcher() {
994 if (mFullscreenUserSwitcher != null) {
jovanak604ad512018-08-14 18:41:27 -0700995 mFullscreenUserSwitcher.hide();
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700996 }
997 }
998
jovanak622def22019-06-11 11:51:54 -0700999 final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
1000 @Override
1001 public void onScreenTurnedOn() {
1002 dismissKeyguardWhenUserSwitcherNotDisplayed();
1003 }
1004 };
1005
jovanak168dbdb2019-02-18 01:00:43 -08001006 // We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
1007 private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
1008 if (mFullscreenUserSwitcher == null) {
1009 return; // Not using the full screen user switcher.
1010 }
1011
1012 if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER
1013 && !mFullscreenUserSwitcher.isVisible()) {
1014 // Current execution path continues to set state after this, thus we deffer the
1015 // dismissal to the next execution cycle.
1016 postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
Rakesh Iyer2790a372016-01-22 15:33:39 -08001017 }
1018 }
Rakesh Iyer74ebabe2016-03-11 10:10:43 -08001019
jovanak604ad512018-08-14 18:41:27 -07001020 public void postDismissKeyguard() {
1021 mHandler.post(this::dismissKeyguard);
1022 }
1023
1024 /**
1025 * Dismisses the keyguard and shows bouncer if authentication is necessary.
1026 */
1027 public void dismissKeyguard() {
jovanak622def22019-06-11 11:51:54 -07001028 // Don't dismiss keyguard when the screen is off.
1029 if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
1030 return;
1031 }
jovanak604ad512018-08-14 18:41:27 -07001032 executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
Brad Stenning8d1a51c2018-11-20 17:34:16 -08001033 true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
jovanak604ad512018-08-14 18:41:27 -07001034 }
1035
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001036 /**
1037 * Ensures that relevant child views are appropriately recreated when the device's density
1038 * changes.
1039 */
1040 @Override
Jason Monk9cac5ff2017-06-02 11:35:50 -04001041 public void onDensityOrFontScaleChanged() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001042 super.onDensityOrFontScaleChanged();
Guobin Zhangeef0e0c2019-02-01 11:54:24 -08001043 restartNavBars();
Anthony Chene658cc22017-04-27 11:17:35 -07001044 // Need to update the background on density changed in case the change was due to night
1045 // mode.
1046 mNotificationPanelBackground = getDefaultWallpaper();
1047 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
1048 }
1049
1050 /**
1051 * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
1052 */
1053 private Drawable getDefaultWallpaper() {
1054 return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001055 }
Brad Stenning19f236a2018-12-11 14:12:30 -08001056
Priyank Singhb0241582019-04-19 18:46:26 -07001057 private void setNotificationViewClipBounds(int height) {
Priyank Singhea4af132019-05-01 17:57:06 -07001058 if (height > mNotificationView.getHeight()) {
1059 height = mNotificationView.getHeight();
1060 }
Priyank Singhb0241582019-04-19 18:46:26 -07001061 Rect clipBounds = new Rect();
1062 clipBounds.set(0, 0, mNotificationView.getWidth(), height);
Priyank Singh245bd122019-04-21 15:42:44 -07001063 // Sets the clip region on the notification list view.
Priyank Singh647f9e62019-05-16 15:19:53 -07001064 mNotificationView.setClipBounds(clipBounds);
1065 if (mHandleBar != null) {
1066 ViewGroup.MarginLayoutParams lp =
1067 (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
1068 mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
1069 }
Priyank Singh245bd122019-04-21 15:42:44 -07001070 if (mNotificationView.getHeight() > 0) {
1071 // Calculates the alpha value for the background based on how much of the notification
1072 // shade is visible to the user. When the notification shade is completely open then
1073 // alpha value will be 1.
1074 float alpha = (float) height / mNotificationView.getHeight();
1075 Drawable background = mNotificationView.getBackground();
1076
1077 background.setAlpha((int) (alpha * 255));
1078 }
Priyank Singhb0241582019-04-19 18:46:26 -07001079 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001080
Priyank Singhb0241582019-04-19 18:46:26 -07001081 private void calculatePercentageFromBottom(float height) {
1082 if (mNotificationView.getHeight() > 0) {
1083 mPercentageFromBottom = (int) Math.abs(
1084 height / mNotificationView.getHeight() * 100);
1085 }
1086 }
1087
Brad Stenning3b0d7642019-03-28 11:04:30 -07001088 private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
1089 private static final int SWIPE_MAX_OFF_PATH = 75;
1090 private static final int SWIPE_THRESHOLD_VELOCITY = 200;
Priyank Singhb0241582019-04-19 18:46:26 -07001091
Priyank Singha47b6f72019-05-28 13:02:47 -07001092 /**
1093 * Only responsible for open hooks. Since once the panel opens it covers all elements
1094 * there is no need to merge with close.
1095 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001096 private abstract class OpenNotificationGestureListener extends
1097 GestureDetector.SimpleOnGestureListener {
1098
1099 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001100 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1101 float distanceY) {
1102
1103 if (mNotificationView.getVisibility() == View.INVISIBLE) {
1104 // when the on-scroll is called for the first time to open.
1105 mNotificationList.scrollToPosition(0);
1106 }
1107 mStatusBarWindowController.setPanelVisible(true);
1108 mNotificationView.setVisibility(View.VISIBLE);
1109
1110 // clips the view for the notification shade when the user scrolls to open.
1111 setNotificationViewClipBounds((int) event2.getRawY());
1112
1113 // Initially the scroll starts with height being zero. This checks protects from divide
1114 // by zero error.
1115 calculatePercentageFromBottom(event2.getRawY());
1116
1117 mIsTracking = true;
1118 return true;
1119 }
1120
1121
1122 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001123 public boolean onFling(MotionEvent event1, MotionEvent event2,
1124 float velocityX, float velocityY) {
Priyank Singhb0241582019-04-19 18:46:26 -07001125 if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
1126 mOpeningVelocity = velocityY;
Brad Stenning3b0d7642019-03-28 11:04:30 -07001127 openNotification();
1128 return true;
1129 }
Priyank Singhb0241582019-04-19 18:46:26 -07001130 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
Brad Stenning3b0d7642019-03-28 11:04:30 -07001131
1132 return false;
1133 }
Priyank Singhb0241582019-04-19 18:46:26 -07001134
Brad Stenning3b0d7642019-03-28 11:04:30 -07001135 protected abstract void openNotification();
Brad Stenning19f236a2018-12-11 14:12:30 -08001136 }
1137
Priyank Singha47b6f72019-05-28 13:02:47 -07001138 /**
1139 * To be installed on the open panel notification panel
1140 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001141 private abstract class CloseNotificationGestureListener extends
1142 GestureDetector.SimpleOnGestureListener {
1143
1144 @Override
Priyank Singhbae09ae2019-04-25 18:17:02 -07001145 public boolean onSingleTapUp(MotionEvent motionEvent) {
Priyank Singhb9ab0522019-05-03 15:36:25 -07001146 if (mPanelExpanded) {
1147 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
1148 }
1149 return true;
Priyank Singhbae09ae2019-04-25 18:17:02 -07001150 }
1151
1152 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001153 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1154 float distanceY) {
Priyank Singhbae09ae2019-04-25 18:17:02 -07001155 // should not clip while scroll to the bottom of the list.
Priyank Singhb0241582019-04-19 18:46:26 -07001156 if (!mNotificationListAtBottomAtTimeOfTouch) {
1157 return false;
1158 }
1159 float actualNotificationHeight =
1160 mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
1161 if (actualNotificationHeight > mNotificationView.getHeight()) {
1162 actualNotificationHeight = mNotificationView.getHeight();
1163 }
1164 if (mNotificationView.getHeight() > 0) {
1165 mPercentageFromBottom = (int) Math.abs(
1166 actualNotificationHeight / mNotificationView.getHeight() * 100);
1167 boolean isUp = distanceY > 0;
1168
1169 // This check is to figure out if onScroll was called while swiping the card at
1170 // bottom of the list. At that time we should not allow notification shade to
1171 // close. We are also checking for the upwards swipe gesture here because it is
1172 // possible if a user is closing the notification shade and while swiping starts
1173 // to open again but does not fling. At that time we should allow the
1174 // notification shade to close fully or else it would stuck in between.
1175 if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
1176 > SWIPE_DOWN_MIN_DISTANCE && isUp) {
1177 setNotificationViewClipBounds((int) actualNotificationHeight);
1178 mIsTracking = true;
1179 } else if (!isUp) {
1180 setNotificationViewClipBounds((int) actualNotificationHeight);
1181 }
1182 }
1183 // if we return true the the items in RV won't be scrollable.
1184 return false;
1185 }
1186
1187
1188 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001189 public boolean onFling(MotionEvent event1, MotionEvent event2,
1190 float velocityX, float velocityY) {
Priyank Singha01f2432019-05-17 16:33:57 -07001191 // should not fling if the touch does not start when view is at the bottom of the list.
1192 if (!mNotificationListAtBottomAtTimeOfTouch) {
Priyank Singhbae09ae2019-04-25 18:17:02 -07001193 return false;
1194 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001195 if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
1196 || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
1197 // swipe was not vertical or was not fast enough
1198 return false;
1199 }
1200 boolean isUp = velocityY < 0;
Priyank Singhb0241582019-04-19 18:46:26 -07001201 if (isUp) {
Brad Stenning3b0d7642019-03-28 11:04:30 -07001202 close();
1203 return true;
Priyank Singhb0241582019-04-19 18:46:26 -07001204 } else {
1205 // we should close the shade
1206 animateNotificationPanel(velocityY, false);
Brad Stenning3b0d7642019-03-28 11:04:30 -07001207 }
1208 return false;
1209 }
Priyank Singhb0241582019-04-19 18:46:26 -07001210
Brad Stenning3b0d7642019-03-28 11:04:30 -07001211 protected abstract void close();
1212 }
1213
Priyank Singha47b6f72019-05-28 13:02:47 -07001214 /**
1215 * To be installed on the nav bars.
1216 */
Brad Stenning3b0d7642019-03-28 11:04:30 -07001217 private abstract class NavBarCloseNotificationGestureListener extends
1218 CloseNotificationGestureListener {
1219 @Override
1220 public boolean onSingleTapUp(MotionEvent e) {
Priyank Singhb0241582019-04-19 18:46:26 -07001221 mClosingVelocity = DEFAULT_FLING_VELOCITY;
Priyank Singhb9ab0522019-05-03 15:36:25 -07001222 if (mPanelExpanded) {
1223 close();
1224 }
Brad Stenning3b0d7642019-03-28 11:04:30 -07001225 return super.onSingleTapUp(e);
1226 }
1227
1228 @Override
Priyank Singhb0241582019-04-19 18:46:26 -07001229 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1230 float distanceY) {
1231 calculatePercentageFromBottom(event2.getRawY());
1232 setNotificationViewClipBounds((int) event2.getRawY());
1233 return true;
1234 }
1235
1236 @Override
Brad Stenning3b0d7642019-03-28 11:04:30 -07001237 public void onLongPress(MotionEvent e) {
Priyank Singhb0241582019-04-19 18:46:26 -07001238 mClosingVelocity = DEFAULT_FLING_VELOCITY;
Brad Stenning3b0d7642019-03-28 11:04:30 -07001239 close();
1240 super.onLongPress(e);
1241 }
Adora Zhang0237b9c2019-03-08 12:27:09 -08001242 }
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001243
1244 /**
Priyank Singha47b6f72019-05-28 13:02:47 -07001245 * To be installed on the handle bar.
1246 */
1247 private class HandleBarCloseNotificationGestureListener extends
1248 GestureDetector.SimpleOnGestureListener {
1249
1250 @Override
1251 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
1252 float distanceY) {
1253 calculatePercentageFromBottom(event2.getRawY());
1254 // To prevent the jump in the clip bounds while closing the notification shade using
1255 // the handle bar we should calculate the height using the diff of event1 and event2.
1256 // This will help the notification shade to clip smoothly as the event2 value changes
1257 // as event1 value will be fixed.
1258 int clipHeight =
1259 mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
1260 setNotificationViewClipBounds(clipHeight);
1261 return true;
1262 }
1263 }
1264
1265 /**
1266 * SystemUi version of the notification manager that overrides methods such that the
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001267 * notifications end up in the status bar layouts instead of a standalone window.
1268 */
1269 private class CarSystemUIHeadsUpNotificationManager extends CarHeadsUpNotificationManager {
1270
1271 CarSystemUIHeadsUpNotificationManager(Context context,
1272 NotificationClickHandlerFactory clickHandlerFactory,
1273 NotificationDataManager notificationDataManager) {
1274 super(context, clickHandlerFactory, notificationDataManager);
1275 }
1276
1277 @Override
1278 protected View createHeadsUpPanel() {
1279 // In SystemUi the view is already in the window so just return a reference.
1280 return mStatusBarWindow.findViewById(R.id.notification_headsup);
1281 }
1282
1283 @Override
1284 protected void addHeadsUpPanelToDisplay() {
1285 // Set the panel initial state to invisible
1286 mHeadsUpPanel.setVisibility(View.INVISIBLE);
1287 }
1288
1289 @Override
Priyank Singh8bd920f2019-06-04 15:21:46 -07001290 protected void setInternalInsetsInfo(ViewTreeObserver.InternalInsetsInfo info,
1291 HeadsUpEntry currentNotification, boolean panelExpanded) {
1292 super.setInternalInsetsInfo(info, currentNotification, mPanelExpanded);
1293 }
1294
1295 @Override
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001296 protected void setHeadsUpVisible() {
Priyank Singh7e9344f2019-05-07 14:30:13 -07001297 // if the Notifications panel is showing don't show the Heads up
Priyank Singh8bd920f2019-06-04 15:21:46 -07001298 if (!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) {
Priyank Singh7e9344f2019-05-07 14:30:13 -07001299 return;
1300 }
Priyank Singh8bd920f2019-06-04 15:21:46 -07001301
Brad Stenning2ccc2a42019-04-15 10:17:01 -07001302 super.setHeadsUpVisible();
1303 if (mHeadsUpPanel.getVisibility() == View.VISIBLE) {
1304 mStatusBarWindowController.setHeadsUpShowing(true);
1305 mStatusBarWindowController.setForceStatusBarVisible(true);
1306 }
1307 }
1308
1309 @Override
1310 protected void removeNotificationFromPanel(HeadsUpEntry currentHeadsUpNotification) {
1311 super.removeNotificationFromPanel(currentHeadsUpNotification);
1312 // If the panel ended up empty and hidden we can remove it from SystemUi
1313 if (mHeadsUpPanel.getVisibility() != View.VISIBLE) {
1314 mStatusBarWindowController.setHeadsUpShowing(false);
1315 mStatusBarWindowController.setForceStatusBarVisible(false);
1316 }
1317 }
1318 }
Rakesh Iyer1186faa2015-12-07 16:48:46 -08001319}