1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.car; 18 19 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 20 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.ValueAnimator; 25 import android.annotation.Nullable; 26 import android.app.ActivityManager; 27 import android.app.ActivityTaskManager; 28 import android.car.Car; 29 import android.car.CarNotConnectedException; 30 import android.car.drivingstate.CarDrivingStateEvent; 31 import android.car.drivingstate.CarUxRestrictionsManager; 32 import android.car.hardware.power.CarPowerManager.CarPowerStateListener; 33 import android.car.media.CarAudioManager; 34 import android.content.BroadcastReceiver; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.graphics.PixelFormat; 39 import android.graphics.Rect; 40 import android.graphics.drawable.Drawable; 41 import android.inputmethodservice.InputMethodService; 42 import android.media.AudioManager; 43 import android.os.Build; 44 import android.os.Handler; 45 import android.os.IBinder; 46 import android.os.Looper; 47 import android.util.Log; 48 import android.view.Display; 49 import android.view.GestureDetector; 50 import android.view.Gravity; 51 import android.view.MotionEvent; 52 import android.view.View; 53 import android.view.ViewGroup; 54 import android.view.ViewGroup.LayoutParams; 55 import android.view.ViewTreeObserver; 56 import android.view.WindowManager; 57 58 import androidx.annotation.NonNull; 59 import androidx.recyclerview.widget.RecyclerView; 60 61 import com.android.car.notification.CarHeadsUpNotificationManager; 62 import com.android.car.notification.CarNotificationListener; 63 import com.android.car.notification.CarNotificationView; 64 import com.android.car.notification.CarUxRestrictionManagerWrapper; 65 import com.android.car.notification.HeadsUpEntry; 66 import com.android.car.notification.NotificationClickHandlerFactory; 67 import com.android.car.notification.NotificationDataManager; 68 import com.android.car.notification.NotificationViewController; 69 import com.android.car.notification.PreprocessingManager; 70 import com.android.internal.statusbar.RegisterStatusBarResult; 71 import com.android.keyguard.KeyguardUpdateMonitor; 72 import com.android.systemui.BatteryMeterView; 73 import com.android.systemui.CarSystemUIFactory; 74 import com.android.systemui.Dependency; 75 import com.android.systemui.ForegroundServiceController; 76 import com.android.systemui.Prefs; 77 import com.android.systemui.R; 78 import com.android.systemui.SystemUIFactory; 79 import com.android.systemui.car.SUWProgressController; 80 import com.android.systemui.classifier.FalsingLog; 81 import com.android.systemui.colorextraction.SysuiColorExtractor; 82 import com.android.systemui.fragments.FragmentHostManager; 83 import com.android.systemui.keyguard.ScreenLifecycle; 84 import com.android.systemui.keyguard.WakefulnessLifecycle; 85 import com.android.systemui.plugins.FalsingManager; 86 import com.android.systemui.plugins.qs.QS; 87 import com.android.systemui.qs.car.CarQSFragment; 88 import com.android.systemui.shared.system.ActivityManagerWrapper; 89 import com.android.systemui.shared.system.TaskStackChangeListener; 90 import com.android.systemui.statusbar.FlingAnimationUtils; 91 import com.android.systemui.statusbar.NavigationBarController; 92 import com.android.systemui.statusbar.NotificationListener; 93 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 94 import com.android.systemui.statusbar.NotificationMediaManager; 95 import com.android.systemui.statusbar.NotificationRemoteInputManager; 96 import com.android.systemui.statusbar.NotificationViewHierarchyManager; 97 import com.android.systemui.statusbar.StatusBarState; 98 import com.android.systemui.statusbar.car.hvac.HvacController; 99 import com.android.systemui.statusbar.car.hvac.TemperatureView; 100 import com.android.systemui.statusbar.notification.NotificationEntryManager; 101 import com.android.systemui.statusbar.notification.VisualStabilityManager; 102 import com.android.systemui.statusbar.notification.logging.NotificationLogger; 103 import com.android.systemui.statusbar.notification.row.NotificationGutsManager; 104 import com.android.systemui.statusbar.phone.AutoHideElement; 105 import com.android.systemui.statusbar.phone.BarTransitions; 106 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; 107 import com.android.systemui.statusbar.phone.LightBarController; 108 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; 109 import com.android.systemui.statusbar.phone.NotificationGroupManager; 110 import com.android.systemui.statusbar.phone.StatusBar; 111 import com.android.systemui.statusbar.phone.StatusBarIconController; 112 import com.android.systemui.statusbar.policy.BatteryController; 113 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 114 import com.android.systemui.statusbar.policy.KeyguardMonitor; 115 import com.android.systemui.statusbar.policy.NetworkController; 116 import com.android.systemui.statusbar.policy.UserSwitcherController; 117 import com.android.systemui.statusbar.policy.ZenModeController; 118 import com.android.systemui.volume.VolumeUI; 119 120 import java.io.FileDescriptor; 121 import java.io.PrintWriter; 122 import java.util.Map; 123 124 /** 125 * A status bar (and navigation bar) tailored for the automotive use case. 126 */ 127 public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler { 128 private static final String TAG = "CarStatusBar"; 129 private static final int MODE_INVALID = -1; 130 // used to calculate how fast to open or close the window 131 private static final float DEFAULT_FLING_VELOCITY = 0; 132 // max time a fling animation takes 133 private static final float FLING_ANIMATION_MAX_TIME = 0.5f; 134 // acceleration rate for the fling animation 135 private static final float FLING_SPEED_UP_FACTOR = 0.6f; 136 137 private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; 138 private float mClosingVelocity = DEFAULT_FLING_VELOCITY; 139 140 private float mBackgroundAlphaDiff; 141 private float mInitialBackgroundAlpha; 142 143 private TaskStackListenerImpl mTaskStackListener; 144 145 private FullscreenUserSwitcher mFullscreenUserSwitcher; 146 147 private CarBatteryController mCarBatteryController; 148 private BatteryMeterView mBatteryMeterView; 149 private Drawable mNotificationPanelBackground; 150 151 private ViewGroup mTopNavigationBarContainer; 152 private ViewGroup mNavigationBarWindow; 153 private ViewGroup mLeftNavigationBarWindow; 154 private ViewGroup mRightNavigationBarWindow; 155 private CarNavigationBarView mTopNavigationBarView; 156 private CarNavigationBarView mNavigationBarView; 157 private CarNavigationBarView mLeftNavigationBarView; 158 private CarNavigationBarView mRightNavigationBarView; 159 160 private final Object mQueueLock = new Object(); 161 private boolean mShowLeft; 162 private boolean mShowRight; 163 private boolean mShowBottom; 164 private CarFacetButtonController mCarFacetButtonController; 165 private ActivityManagerWrapper mActivityManagerWrapper; 166 private DeviceProvisionedController mDeviceProvisionedController; 167 private SUWProgressController mSUWProgressController; 168 private boolean mDeviceIsSetUpForUser = true; 169 private boolean mIsUserSetupInProgress = false; 170 private HvacController mHvacController; 171 private DrivingStateHelper mDrivingStateHelper; 172 private PowerManagerHelper mPowerManagerHelper; 173 private FlingAnimationUtils mFlingAnimationUtils; 174 private SwitchToGuestTimer mSwitchToGuestTimer; 175 private NotificationDataManager mNotificationDataManager; 176 private CarNotificationListener mCarNotificationListener; 177 private NotificationClickHandlerFactory mNotificationClickHandlerFactory; 178 private ScreenLifecycle mScreenLifecycle; 179 private CarAudioManager mCarAudioManager; 180 private VolumeUI mVolumeUI; 181 182 // The container for the notifications. 183 private CarNotificationView mNotificationView; 184 private RecyclerView mNotificationList; 185 // The handler bar view at the bottom of notification shade. 186 private View mHandleBar; 187 // The controller for the notification view. 188 private NotificationViewController mNotificationViewController; 189 // The state of if the notification list is currently showing the bottom. 190 private boolean mNotificationListAtBottom; 191 // Was the notification list at the bottom when the user first touched the screen 192 private boolean mNotificationListAtBottomAtTimeOfTouch; 193 // To be attached to the top navigation bar (i.e. status bar) to pull down the notification 194 // panel. 195 private View.OnTouchListener mTopNavBarNotificationTouchListener; 196 // To be attached to the navigation bars such that they can close the notification panel if 197 // it's open. 198 private View.OnTouchListener mNavBarNotificationTouchListener; 199 200 // Percentage from top of the screen after which the notification shade will open. This value 201 // will be used while opening the notification shade. 202 private int mSettleOpenPercentage; 203 // Percentage from top of the screen below which the notification shade will close. This 204 // value will be used while closing the notification shade. 205 private int mSettleClosePercentage; 206 // Percentage of notification shade open from top of the screen. 207 private int mPercentageFromBottom; 208 // If notification shade is animation to close or to open. 209 private boolean mIsNotificationAnimating; 210 211 // Tracks when the notification shade is being scrolled. This refers to the glass pane being 212 // scrolled not the recycler view. 213 private boolean mIsTracking; 214 private float mFirstTouchDownOnGlassPane; 215 216 // If the notification card inside the recycler view is being swiped. 217 private boolean mIsNotificationCardSwiping; 218 // If notification shade is being swiped vertically to close. 219 private boolean mIsSwipingVerticallyToClose; 220 // Whether heads-up notifications should be shown when shade is open. 221 private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen; 222 // If the nav bar should be hidden when the soft keyboard is visible. 223 private boolean mHideNavBarForKeyboard; 224 private boolean mBottomNavBarVisible; 225 226 private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper; 227 228 private final CarPowerStateListener mCarPowerStateListener = 229 (int state) -> { 230 // When the car powers on, clear all notifications and mute/unread states. 231 Log.d(TAG, "New car power state: " + state); 232 if (state == CarPowerStateListener.ON) { 233 if (mNotificationClickHandlerFactory != null) { 234 mNotificationClickHandlerFactory.clearAllNotifications(); 235 } 236 if (mNotificationDataManager != null) { 237 mNotificationDataManager.clearAll(); 238 } 239 } 240 }; 241 242 private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = 243 new CarAudioManager.CarVolumeCallback() { 244 @Override 245 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 246 if (mVolumeUI == null && (flags & AudioManager.FLAG_SHOW_UI) != 0) { 247 new Handler(Looper.getMainLooper()).post(() -> { 248 // Initialize Volume UI 249 mVolumeUI = new VolumeUI(); 250 mVolumeUI.mComponents = mComponents; 251 mVolumeUI.mContext = mContext; 252 mVolumeUI.start(); 253 }); 254 mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback); 255 } 256 } 257 258 @Override 259 public void onMasterMuteChanged(int zoneId, int flags) { 260 // ignored 261 } 262 }; 263 264 private boolean mBootCompleted = false; 265 private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 266 @Override 267 public void onReceive(Context context, Intent intent) { 268 mBootCompleted = true; 269 String action = intent.getAction(); 270 if (action != null && action.equals(Intent.ACTION_BOOT_COMPLETED)) { 271 initHvac(); 272 } 273 } 274 }; 275 276 private int mNavigationBarMode; 277 private BarTransitions mNavBarTransitions; 278 279 @Override start()280 public void start() { 281 // Non blocking call to connect to car service. Call this early so that we'll be connected 282 // asap. 283 ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext); 284 285 // Defer some actions for CarStatusBar initialization until after boot complete event 286 registerBootCompletedReceiver(); 287 288 // get the provisioned state before calling the parent class since it's that flow that 289 // builds the nav bar 290 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); 291 mSUWProgressController = new SUWProgressController(mContext); 292 mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup(); 293 mIsUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress(); 294 295 // Keyboard related setup, before nav bars are created. 296 mHideNavBarForKeyboard = mContext.getResources().getBoolean( 297 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); 298 mBottomNavBarVisible = false; 299 300 // Need to initialize screen lifecycle before calling super.start - before switcher is 301 // created. 302 mScreenLifecycle = Dependency.get(ScreenLifecycle.class); 303 mScreenLifecycle.addObserver(mScreenObserver); 304 305 // Need to initialize HVAC controller before calling super.start - before system bars are 306 // created. 307 mHvacController = new HvacController(mContext); 308 309 // Notification bar related setup. 310 mInitialBackgroundAlpha = (float) mContext.getResources().getInteger( 311 R.integer.config_initialNotificationBackgroundAlpha) / 100; 312 if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) { 313 throw new RuntimeException( 314 "Unable to setup notification bar due to incorrect initial background alpha" 315 + " percentage"); 316 } 317 float finalBackgroundAlpha = Math.max( 318 mInitialBackgroundAlpha, 319 (float) mContext.getResources().getInteger( 320 R.integer.config_finalNotificationBackgroundAlpha) / 100); 321 if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) { 322 throw new RuntimeException( 323 "Unable to setup notification bar due to incorrect final background alpha" 324 + " percentage"); 325 } 326 mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha; 327 328 super.start(); 329 mTaskStackListener = new TaskStackListenerImpl(); 330 mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); 331 mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener); 332 333 mNotificationPanel.setScrollingEnabled(true); 334 mSettleOpenPercentage = mContext.getResources().getInteger( 335 R.integer.notification_settle_open_percentage); 336 mSettleClosePercentage = mContext.getResources().getInteger( 337 R.integer.notification_settle_close_percentage); 338 mFlingAnimationUtils = new FlingAnimationUtils(mContext, 339 FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR); 340 341 createBatteryController(); 342 mCarBatteryController.startListening(); 343 344 mHvacController.connectToCarService(); 345 346 mSUWProgressController.addCallback( 347 new SUWProgressController.SUWProgressListener() { 348 @Override 349 public void onUserSetupInProgressChanged() { 350 mHandler.post(() -> restartNavBarsIfNecessary()); 351 } 352 }); 353 mDeviceProvisionedController.addCallback( 354 new DeviceProvisionedController.DeviceProvisionedListener() { 355 @Override 356 public void onUserSetupChanged() { 357 mHandler.post(() -> restartNavBarsIfNecessary()); 358 } 359 360 @Override 361 public void onUserSwitched() { 362 mSUWProgressController.onUserSwitched(); 363 mHandler.post(() -> restartNavBarsIfNecessary()); 364 } 365 }); 366 367 // Used by onDrivingStateChanged and it can be called inside 368 // DrivingStateHelper.connectToCarService() 369 mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); 370 371 // Register a listener for driving state changes. 372 mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); 373 mDrivingStateHelper.connectToCarService(); 374 375 mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener); 376 mPowerManagerHelper.connectToCarService(); 377 378 ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) 379 .addListener((car, ready) -> { 380 if (!ready || mCarAudioManager != null) { 381 return; 382 } 383 try { 384 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 385 Log.d(TAG, "Registering mVolumeChangeCallback."); 386 // This volume call back is never unregistered because CarStatusBar is 387 // never destroyed. 388 mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); 389 } catch (CarNotConnectedException e) { 390 Log.wtf(TAG, " mVolumeChangeCallback failed to connect to car ", e); 391 } 392 }); 393 394 mAutoHideController.setNavigationBar(new AutoHideElement() { 395 @Override 396 public void synchronizeState() { 397 checkNavBarModes(); 398 } 399 400 @Override 401 public boolean isSemiTransparent() { 402 return mNavigationBarMode == MODE_SEMI_TRANSPARENT; 403 } 404 }); 405 } 406 407 @Override getDependencies()408 protected void getDependencies() { 409 // Keyguard 410 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); 411 mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); 412 mScreenLifecycle = Dependency.get(ScreenLifecycle.class); 413 414 // Policy 415 mZenController = Dependency.get(ZenModeController.class); 416 if (Build.IS_USERDEBUG) { 417 mNetworkController = Dependency.get(NetworkController.class); 418 } 419 420 // Icon 421 mIconController = Dependency.get(StatusBarIconController.class); 422 mLightBarController = Dependency.get(LightBarController.class); 423 424 // Notifications 425 mEntryManager = Dependency.get(NotificationEntryManager.class); 426 mForegroundServiceController = Dependency.get(ForegroundServiceController.class); 427 mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class); 428 mGroupManager = Dependency.get(NotificationGroupManager.class); 429 mGutsManager = Dependency.get(NotificationGutsManager.class); 430 mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); 431 mMediaManager = Dependency.get(NotificationMediaManager.class); 432 mNotificationListener = Dependency.get(NotificationListener.class); 433 mNotificationLogger = Dependency.get(NotificationLogger.class); 434 mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); 435 mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); 436 mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); 437 438 // Others 439 mColorExtractor = Dependency.get(SysuiColorExtractor.class); 440 mNavigationBarController = Dependency.get(NavigationBarController.class); 441 mUserSwitcherController = Dependency.get(UserSwitcherController.class); 442 } 443 444 @Override setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean navbarColorManagedByIme)445 public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, 446 int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, 447 boolean navbarColorManagedByIme) { 448 // Ensure we store the systemUiVisibility flags before the super call overwrites it. 449 int oldVal = getSystemUiVisibility(); 450 451 super.setSystemUiVisibility(displayId, vis, fullscreenStackVis, dockedStackVis, mask, 452 fullscreenStackBounds, dockedStackBounds, navbarColorManagedByIme); 453 454 if (displayId != getDisplayId()) { 455 return; 456 } 457 458 int newVal = (oldVal & ~mask) | (vis & mask); 459 460 // update navigation bar mode 461 int nbMode = mNavigationBarWindow == null ? MODE_INVALID : computeNavBarMode(oldVal, 462 newVal); 463 boolean nbModeChanged = nbMode != MODE_INVALID; 464 if (nbModeChanged) { 465 if (mNavigationBarMode != nbMode) { 466 mNavigationBarMode = nbMode; 467 checkNavBarModes(); 468 } 469 mAutoHideController.touchAutoHide(); 470 } 471 472 mLightBarController.onNavigationVisibilityChanged( 473 vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); 474 } 475 476 @BarTransitions.TransitionMode computeNavBarMode(int oldVis, int newVis)477 private int computeNavBarMode(int oldVis, int newVis) { 478 int oldMode = navBarMode(oldVis); 479 int newMode = navBarMode(newVis); 480 if (oldMode == newMode) { 481 return -1; // no mode change 482 } 483 return newMode; 484 } 485 486 @BarTransitions.TransitionMode navBarMode(int vis)487 private int navBarMode(int vis) { 488 if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) { 489 return MODE_SEMI_TRANSPARENT; 490 } else { 491 return MODE_OPAQUE; 492 } 493 } 494 495 /** 496 * Checks current navigation bar mode and make transitions. 497 */ checkNavBarModes()498 private void checkNavBarModes() { 499 boolean anim = isDeviceInteractive() && mBottomNavBarVisible; 500 mNavBarTransitions.transitionTo(mNavigationBarMode, anim); 501 } 502 503 @Override setUpQuickSettingsTilePanel()504 protected void setUpQuickSettingsTilePanel() { 505 // ignore. 506 } 507 registerBootCompletedReceiver()508 private void registerBootCompletedReceiver() { 509 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 510 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 511 mContext.registerReceiver(mBootCompletedReceiver, bootCompletedFilter); 512 } 513 unregisterBootCompletedListener()514 private void unregisterBootCompletedListener() { 515 mContext.unregisterReceiver(mBootCompletedReceiver); 516 } 517 restartNavBarsIfNecessary()518 private void restartNavBarsIfNecessary() { 519 boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); 520 boolean currentUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress(); 521 if (mIsUserSetupInProgress != currentUserSetupInProgress 522 || mDeviceIsSetUpForUser != currentUserSetup) { 523 mDeviceIsSetUpForUser = currentUserSetup; 524 mIsUserSetupInProgress = currentUserSetupInProgress; 525 restartNavBars(); 526 } 527 } 528 529 /** 530 * Remove all content from navbars and rebuild them. Used to allow for different nav bars 531 * before and after the device is provisioned. . Also for change of density and font size. 532 */ restartNavBars()533 private void restartNavBars() { 534 // remove and reattach all hvac components such that we don't keep a reference to unused 535 // ui elements 536 if (mHvacController != null) { 537 mHvacController.removeAllComponents(); 538 } 539 mCarFacetButtonController.removeAll(); 540 541 if (mNavigationBarWindow != null) { 542 mNavigationBarWindow.removeAllViews(); 543 mNavigationBarView = null; 544 } 545 546 if (mLeftNavigationBarWindow != null) { 547 mLeftNavigationBarWindow.removeAllViews(); 548 mLeftNavigationBarView = null; 549 } 550 551 if (mRightNavigationBarWindow != null) { 552 mRightNavigationBarWindow.removeAllViews(); 553 mRightNavigationBarView = null; 554 } 555 556 buildNavBarContent(); 557 if (mBootCompleted) { 558 initHvac(); 559 } 560 // If the UI was rebuilt (day/night change) while the keyguard was up we need to 561 // correctly respect that state. 562 if (mIsKeyguard) { 563 updateNavBarForKeyguardContent(); 564 } 565 // CarFacetButtonController was reset therefore we need to re-add the status bar elements 566 // to the controller. 567 mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow); 568 569 // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the 570 // selection state that reflects the current task stack. 571 try { 572 mCarFacetButtonController.taskChanged( 573 ActivityTaskManager.getService().getAllStackInfos()); 574 } catch (Exception e) { 575 Log.e(TAG, "Getting StackInfo from activity manager failed", e); 576 } 577 } 578 addTemperatureViewToController(View v)579 private void addTemperatureViewToController(View v) { 580 if (v == null) return; 581 582 if (v instanceof TemperatureView) { 583 mHvacController.addHvacTextView((TemperatureView) v); 584 } else if (v instanceof ViewGroup) { 585 ViewGroup viewGroup = (ViewGroup) v; 586 for (int i = 0; i < viewGroup.getChildCount(); i++) { 587 addTemperatureViewToController(viewGroup.getChildAt(i)); 588 } 589 } 590 } 591 /** 592 * Allows for showing or hiding just the navigation bars. This is indented to be used when 593 * the full screen user selector is shown. 594 */ setNavBarVisibility(@iew.Visibility int visibility)595 void setNavBarVisibility(@View.Visibility int visibility) { 596 if (mNavigationBarWindow != null) { 597 mNavigationBarWindow.setVisibility(visibility); 598 } 599 if (mLeftNavigationBarWindow != null) { 600 mLeftNavigationBarWindow.setVisibility(visibility); 601 } 602 if (mRightNavigationBarWindow != null) { 603 mRightNavigationBarWindow.setVisibility(visibility); 604 } 605 } 606 607 608 @Override hideKeyguard()609 public boolean hideKeyguard() { 610 boolean result = super.hideKeyguard(); 611 if (mNavigationBarView != null) { 612 mNavigationBarView.hideKeyguardButtons(); 613 } 614 if (mLeftNavigationBarView != null) { 615 mLeftNavigationBarView.hideKeyguardButtons(); 616 } 617 if (mRightNavigationBarView != null) { 618 mRightNavigationBarView.hideKeyguardButtons(); 619 } 620 return result; 621 } 622 623 @Override showKeyguard()624 public void showKeyguard() { 625 super.showKeyguard(); 626 updateNavBarForKeyguardContent(); 627 } 628 629 /** 630 * Switch to the keyguard applicable content contained in the nav bars 631 */ updateNavBarForKeyguardContent()632 private void updateNavBarForKeyguardContent() { 633 if (mNavigationBarView != null) { 634 mNavigationBarView.showKeyguardButtons(); 635 } 636 if (mLeftNavigationBarView != null) { 637 mLeftNavigationBarView.showKeyguardButtons(); 638 } 639 if (mRightNavigationBarView != null) { 640 mRightNavigationBarView.showKeyguardButtons(); 641 } 642 } 643 644 @Override makeStatusBarView(@ullable RegisterStatusBarResult result)645 protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { 646 super.makeStatusBarView(result); 647 648 CarSystemUIFactory factory = SystemUIFactory.getInstance(); 649 mCarFacetButtonController = factory.getCarDependencyComponent() 650 .getCarFacetButtonController(); 651 mNotificationPanelBackground = getDefaultWallpaper(); 652 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground); 653 654 FragmentHostManager manager = FragmentHostManager.get(mStatusBarWindow); 655 manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { 656 mBatteryMeterView = fragment.getView().findViewById(R.id.battery); 657 658 // By default, the BatteryMeterView should not be visible. It will be toggled 659 // when a device has connected by bluetooth. 660 mBatteryMeterView.setVisibility(View.GONE); 661 }); 662 663 connectNotificationsUI(); 664 } 665 666 /** 667 * Attach the notification listeners and controllers to the UI as well as build all the 668 * touch listeners needed for opening and closing the notification panel 669 */ connectNotificationsUI()670 private void connectNotificationsUI() { 671 // Attached to the top navigation bar (i.e. status bar) to detect pull down of the 672 // notification shade. 673 GestureDetector openGestureDetector = new GestureDetector(mContext, 674 new OpenNotificationGestureListener() { 675 @Override 676 protected void openNotification() { 677 animateExpandNotificationsPanel(); 678 } 679 }); 680 // Attached to the notification ui to detect close request of the notification shade. 681 GestureDetector closeGestureDetector = new GestureDetector(mContext, 682 new CloseNotificationGestureListener() { 683 @Override 684 protected void close() { 685 if (mPanelExpanded) { 686 animateCollapsePanels(); 687 } 688 } 689 }); 690 // Attached to the NavBars to close the notification shade 691 GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext, 692 new NavBarCloseNotificationGestureListener() { 693 @Override 694 protected void close() { 695 if (mPanelExpanded) { 696 animateCollapsePanels(); 697 } 698 } 699 }); 700 701 // Attached to the Handle bar to close the notification shade 702 GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext, 703 new HandleBarCloseNotificationGestureListener()); 704 705 mTopNavBarNotificationTouchListener = (v, event) -> { 706 if (!mDeviceIsSetUpForUser || mIsUserSetupInProgress) { 707 return true; 708 } 709 boolean consumed = openGestureDetector.onTouchEvent(event); 710 if (consumed) { 711 return true; 712 } 713 maybeCompleteAnimation(event); 714 return true; 715 }; 716 717 mNavBarNotificationTouchListener = 718 (v, event) -> { 719 boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event); 720 if (consumed) { 721 return true; 722 } 723 maybeCompleteAnimation(event); 724 return true; 725 }; 726 727 mNotificationClickHandlerFactory = new NotificationClickHandlerFactory( 728 mBarService, 729 launchResult -> { 730 if (launchResult == ActivityManager.START_TASK_TO_FRONT 731 || launchResult == ActivityManager.START_SUCCESS) { 732 animateCollapsePanels(); 733 } 734 }); 735 736 mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper(); 737 738 if (mCarNotificationListener == null) { 739 // Only make and register a listener if we don't already have one since 740 // #connectNotificationsUI can be called multiple times on locale change. 741 mCarNotificationListener = new CarNotificationListener(); 742 mNotificationDataManager = new NotificationDataManager(); 743 744 CarHeadsUpNotificationManager carHeadsUpNotificationManager = 745 new CarSystemUIHeadsUpNotificationManager(mContext, 746 mNotificationClickHandlerFactory, mNotificationDataManager); 747 mCarNotificationListener.registerAsSystemService(mContext, 748 mCarUxRestrictionManagerWrapper, 749 carHeadsUpNotificationManager, mNotificationDataManager); 750 } 751 752 mNotificationDataManager.setOnUnseenCountUpdateListener(() -> { 753 if (mNavigationBarView != null && mNotificationDataManager != null) { 754 onUseenCountUpdate(mNotificationDataManager.getUnseenNotificationCount()); 755 } 756 }); 757 758 mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean( 759 R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen); 760 761 mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager); 762 763 mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view); 764 View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane); 765 mHandleBar = mStatusBarWindow.findViewById(R.id.handle_bar); 766 mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory); 767 mNotificationView.setNotificationDataManager(mNotificationDataManager); 768 769 // The glass pane is used to view touch events before passed to the notification list. 770 // This allows us to initialize gesture listeners and detect when to close the notifications 771 glassPane.setOnTouchListener((v, event) -> { 772 if (event.getActionMasked() == MotionEvent.ACTION_UP) { 773 mNotificationListAtBottomAtTimeOfTouch = false; 774 } 775 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 776 mFirstTouchDownOnGlassPane = event.getRawX(); 777 mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom; 778 // Reset the tracker when there is a touch down on the glass pane. 779 mIsTracking = false; 780 // Pass the down event to gesture detector so that it knows where the touch event 781 // started. 782 closeGestureDetector.onTouchEvent(event); 783 } 784 return false; 785 }); 786 787 mHandleBar.setOnTouchListener((v, event) -> { 788 handleBarCloseNotificationGestureDetector.onTouchEvent(event); 789 maybeCompleteAnimation(event); 790 return true; 791 }); 792 793 mNotificationList = mNotificationView.findViewById(R.id.notifications); 794 mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() { 795 @Override 796 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { 797 super.onScrolled(recyclerView, dx, dy); 798 if (!mNotificationList.canScrollVertically(1)) { 799 mNotificationListAtBottom = true; 800 return; 801 } 802 mNotificationListAtBottom = false; 803 mIsSwipingVerticallyToClose = false; 804 mNotificationListAtBottomAtTimeOfTouch = false; 805 } 806 }); 807 mNotificationList.setOnTouchListener(new View.OnTouchListener() { 808 @Override 809 public boolean onTouch(View v, MotionEvent event) { 810 mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX()) 811 > SWIPE_MAX_OFF_PATH; 812 if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) { 813 // We need to save the state here as if notification card is swiping we will 814 // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect 815 // closing the notification shade while the notification card is being swiped. 816 mIsSwipingVerticallyToClose = true; 817 } 818 819 // If the card is swiping we should not allow the notification shade to close. 820 // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that 821 // for us. We are also checking for mIsTracking because while swiping the 822 // notification shade to close if the user goes a bit horizontal while swiping 823 // upwards then also this should close. 824 if (mIsNotificationCardSwiping && !mIsTracking) { 825 mNotificationListAtBottomAtTimeOfTouch = false; 826 } 827 828 boolean handled = closeGestureDetector.onTouchEvent(event); 829 boolean isTracking = mIsTracking; 830 Rect rect = mNotificationView.getClipBounds(); 831 float clippedHeight = 0; 832 if (rect != null) { 833 clippedHeight = rect.bottom; 834 } 835 if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP 836 && mIsSwipingVerticallyToClose) { 837 if (mSettleClosePercentage < mPercentageFromBottom && isTracking) { 838 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false); 839 } else if (clippedHeight != mNotificationView.getHeight() && isTracking) { 840 // this can be caused when user is at the end of the list and trying to 841 // fling to top of the list by scrolling down. 842 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); 843 } 844 } 845 846 // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after 847 // the event has been passed to the closeGestureDetector above, such that the 848 // closeGestureDetector sees the up event before the state has changed. 849 if (event.getActionMasked() == MotionEvent.ACTION_UP) { 850 mNotificationListAtBottomAtTimeOfTouch = false; 851 } 852 return handled || isTracking; 853 } 854 }); 855 ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) 856 .addListener((car, ready) -> { 857 if (!ready) { 858 return; 859 } 860 CarUxRestrictionsManager carUxRestrictionsManager = 861 (CarUxRestrictionsManager) 862 car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); 863 mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager( 864 carUxRestrictionsManager); 865 866 mNotificationViewController = new NotificationViewController( 867 mNotificationView, 868 PreprocessingManager.getInstance(mContext), 869 mCarNotificationListener, 870 mCarUxRestrictionManagerWrapper, 871 mNotificationDataManager); 872 mNotificationViewController.enable(); 873 }); 874 } 875 876 /** 877 * This method is automatically called whenever there is an update to the number of unseen 878 * notifications. This method can be extended by OEMs to customize the desired logic. 879 */ onUseenCountUpdate(int unseenNotificationCount)880 protected void onUseenCountUpdate(int unseenNotificationCount) { 881 boolean hasUnseen = unseenNotificationCount > 0; 882 883 if (mNavigationBarView != null) { 884 mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); 885 } 886 887 if (mLeftNavigationBarView != null) { 888 mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); 889 } 890 891 if (mRightNavigationBarView != null) { 892 mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); 893 } 894 } 895 896 /** 897 * @return true if the notification panel is currently visible 898 */ isNotificationPanelOpen()899 boolean isNotificationPanelOpen() { 900 return mPanelExpanded; 901 } 902 903 @Override animateExpandNotificationsPanel()904 public void animateExpandNotificationsPanel() { 905 if (!mCommandQueue.panelsEnabled() || !mUserSetup) { 906 return; 907 } 908 // scroll to top 909 mNotificationList.scrollToPosition(0); 910 mStatusBarWindowController.setPanelVisible(true); 911 mNotificationView.setVisibility(View.VISIBLE); 912 animateNotificationPanel(mOpeningVelocity, false); 913 914 setPanelExpanded(true); 915 } 916 917 @Override animateCollapsePanels(int flags, boolean force, boolean delayed, float speedUpFactor)918 public void animateCollapsePanels(int flags, boolean force, boolean delayed, 919 float speedUpFactor) { 920 super.animateCollapsePanels(flags, force, delayed, speedUpFactor); 921 if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) { 922 return; 923 } 924 mStatusBarWindowController.setStatusBarFocusable(false); 925 mStatusBarWindow.cancelExpandHelper(); 926 mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); 927 928 animateNotificationPanel(mClosingVelocity, true); 929 930 if (!mIsTracking) { 931 mStatusBarWindowController.setPanelVisible(false); 932 mNotificationView.setVisibility(View.INVISIBLE); 933 } 934 935 setPanelExpanded(false); 936 } 937 maybeCompleteAnimation(MotionEvent event)938 private void maybeCompleteAnimation(MotionEvent event) { 939 if (event.getActionMasked() == MotionEvent.ACTION_UP 940 && mNotificationView.getVisibility() == View.VISIBLE) { 941 if (mSettleClosePercentage < mPercentageFromBottom) { 942 animateNotificationPanel(DEFAULT_FLING_VELOCITY, false); 943 } else { 944 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); 945 } 946 } 947 } 948 949 /** 950 * Animates the notification shade from one position to other. This is used to either open or 951 * close the notification shade completely with a velocity. If the animation is to close the 952 * notification shade this method also makes the view invisible after animation ends. 953 */ animateNotificationPanel(float velocity, boolean isClosing)954 private void animateNotificationPanel(float velocity, boolean isClosing) { 955 float to = 0; 956 if (!isClosing) { 957 to = mNotificationView.getHeight(); 958 } 959 960 Rect rect = mNotificationView.getClipBounds(); 961 if (rect != null) { 962 float from = rect.bottom; 963 animate(from, to, velocity, isClosing); 964 return; 965 } 966 967 // We will only be here if the shade is being opened programmatically. 968 ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver(); 969 notificationTreeObserver.addOnGlobalLayoutListener( 970 new ViewTreeObserver.OnGlobalLayoutListener() { 971 @Override 972 public void onGlobalLayout() { 973 ViewTreeObserver obs = mNotificationView.getViewTreeObserver(); 974 obs.removeOnGlobalLayoutListener(this); 975 float to = mNotificationView.getHeight(); 976 animate(/* from= */ 0, to, velocity, isClosing); 977 } 978 }); 979 } 980 animate(float from, float to, float velocity, boolean isClosing)981 private void animate(float from, float to, float velocity, boolean isClosing) { 982 if (mIsNotificationAnimating) { 983 return; 984 } 985 mIsNotificationAnimating = true; 986 mIsTracking = true; 987 ValueAnimator animator = ValueAnimator.ofFloat(from, to); 988 animator.addUpdateListener( 989 animation -> { 990 float animatedValue = (Float) animation.getAnimatedValue(); 991 setNotificationViewClipBounds((int) animatedValue); 992 }); 993 animator.addListener(new AnimatorListenerAdapter() { 994 @Override 995 public void onAnimationEnd(Animator animation) { 996 super.onAnimationEnd(animation); 997 mIsNotificationAnimating = false; 998 mIsTracking = false; 999 mOpeningVelocity = DEFAULT_FLING_VELOCITY; 1000 mClosingVelocity = DEFAULT_FLING_VELOCITY; 1001 if (isClosing) { 1002 mStatusBarWindowController.setPanelVisible(false); 1003 mNotificationView.setVisibility(View.INVISIBLE); 1004 mNotificationView.setClipBounds(null); 1005 mNotificationViewController.setIsInForeground(false); 1006 // let the status bar know that the panel is closed 1007 setPanelExpanded(false); 1008 mAutoHideController.userAutoHide(); 1009 } else { 1010 mAutoHideController.cancelAutoHide(); 1011 mNotificationViewController.setIsInForeground(true); 1012 // let the status bar know that the panel is open 1013 mNotificationView.setVisibleNotificationsAsSeen(); 1014 setPanelExpanded(true); 1015 } 1016 } 1017 }); 1018 mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity)); 1019 animator.start(); 1020 } 1021 1022 @Override createDefaultQSFragment()1023 protected QS createDefaultQSFragment() { 1024 return new CarQSFragment(); 1025 } 1026 createBatteryController()1027 private BatteryController createBatteryController() { 1028 mCarBatteryController = new CarBatteryController(mContext); 1029 mCarBatteryController.addBatteryViewHandler(this); 1030 return mCarBatteryController; 1031 } 1032 1033 @Override createNavigationBar(@ullable RegisterStatusBarResult result)1034 protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { 1035 mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar); 1036 mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); 1037 mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); 1038 1039 buildNavBarWindows(); 1040 buildNavBarContent(); 1041 attachNavBarWindows(); 1042 1043 // Try setting up the initial state of the nav bar if applicable. 1044 if (result != null) { 1045 setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken, 1046 result.mImeWindowVis, result.mImeBackDisposition, 1047 result.mShowImeSwitcher); 1048 } 1049 1050 // There has been a car customized nav bar on the default display, so just create nav bars 1051 // on external displays. 1052 mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result); 1053 } 1054 buildNavBarContent()1055 private void buildNavBarContent() { 1056 boolean shouldBuildNavBarContent = mDeviceIsSetUpForUser && !mIsUserSetupInProgress; 1057 1058 // Always build top bar. 1059 buildTopBar(shouldBuildNavBarContent ? R.layout.car_top_navigation_bar : 1060 R.layout.car_top_navigation_bar_unprovisioned); 1061 1062 if (mShowBottom) { 1063 buildBottomBar(shouldBuildNavBarContent ? R.layout.car_navigation_bar : 1064 R.layout.car_navigation_bar_unprovisioned); 1065 } 1066 1067 if (mShowLeft) { 1068 buildLeft(shouldBuildNavBarContent ? R.layout.car_left_navigation_bar : 1069 R.layout.car_left_navigation_bar_unprovisioned); 1070 } 1071 1072 if (mShowRight) { 1073 buildRight(shouldBuildNavBarContent ? R.layout.car_right_navigation_bar : 1074 R.layout.car_right_navigation_bar_unprovisioned); 1075 } 1076 } 1077 buildNavBarWindows()1078 private void buildNavBarWindows() { 1079 mTopNavigationBarContainer = mStatusBarWindow 1080 .findViewById(R.id.car_top_navigation_bar_container); 1081 1082 if (mShowBottom) { 1083 mNavigationBarWindow = (ViewGroup) View.inflate(mContext, 1084 R.layout.navigation_bar_window, null); 1085 mNavBarTransitions = new BarTransitions(mNavigationBarWindow, 1086 R.drawable.nav_background); 1087 } 1088 if (mShowLeft) { 1089 mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext, 1090 R.layout.navigation_bar_window, null); 1091 } 1092 if (mShowRight) { 1093 mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext, 1094 R.layout.navigation_bar_window, null); 1095 } 1096 1097 } 1098 1099 /** 1100 * We register for soft keyboard visibility events such that we can hide the navigation bar 1101 * giving more screen space to the IME. Note: this is optional and controlled by 1102 * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}. 1103 */ 1104 @Override setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)1105 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 1106 boolean showImeSwitcher) { 1107 if (!mHideNavBarForKeyboard) { 1108 return; 1109 } 1110 1111 if (mContext.getDisplay().getDisplayId() != displayId) { 1112 return; 1113 } 1114 1115 boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; 1116 showBottomNavBarWindow(isKeyboardVisible); 1117 } 1118 attachNavBarWindows()1119 private void attachNavBarWindows() { 1120 if (mShowBottom && !mBottomNavBarVisible) { 1121 mBottomNavBarVisible = true; 1122 1123 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1124 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1125 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1126 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1127 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1128 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1129 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1130 PixelFormat.TRANSLUCENT); 1131 lp.setTitle("CarNavigationBar"); 1132 lp.windowAnimations = 0; 1133 mWindowManager.addView(mNavigationBarWindow, lp); 1134 } 1135 1136 if (mShowLeft) { 1137 int width = mContext.getResources().getDimensionPixelSize( 1138 R.dimen.car_left_navigation_bar_width); 1139 WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( 1140 width, LayoutParams.MATCH_PARENT, 1141 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 1142 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1143 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1144 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1145 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1146 PixelFormat.TRANSLUCENT); 1147 leftlp.setTitle("LeftCarNavigationBar"); 1148 leftlp.windowAnimations = 0; 1149 leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; 1150 leftlp.gravity = Gravity.LEFT; 1151 mWindowManager.addView(mLeftNavigationBarWindow, leftlp); 1152 } 1153 if (mShowRight) { 1154 int width = mContext.getResources().getDimensionPixelSize( 1155 R.dimen.car_right_navigation_bar_width); 1156 WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( 1157 width, LayoutParams.MATCH_PARENT, 1158 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 1159 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1160 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1161 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1162 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1163 PixelFormat.TRANSLUCENT); 1164 rightlp.setTitle("RightCarNavigationBar"); 1165 rightlp.windowAnimations = 0; 1166 rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; 1167 rightlp.gravity = Gravity.RIGHT; 1168 mWindowManager.addView(mRightNavigationBarWindow, rightlp); 1169 } 1170 } 1171 showBottomNavBarWindow(boolean isKeyboardVisible)1172 private void showBottomNavBarWindow(boolean isKeyboardVisible) { 1173 if (!mShowBottom) { 1174 return; 1175 } 1176 1177 // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do 1178 // nothing. Same with if keyboard is not visible and bottom nav bar is visible. 1179 if (isKeyboardVisible ^ mBottomNavBarVisible) { 1180 return; 1181 } 1182 1183 mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE); 1184 setNotificationViewBottomMargin(isKeyboardVisible ? 0 : mNavigationBarWindow.getHeight()); 1185 mBottomNavBarVisible = !isKeyboardVisible; 1186 } 1187 buildTopBar(int layout)1188 private void buildTopBar(int layout) { 1189 mTopNavigationBarContainer.removeAllViews(); 1190 View.inflate(mContext, layout, mTopNavigationBarContainer); 1191 mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0); 1192 if (mTopNavigationBarView == null) { 1193 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar"); 1194 throw new RuntimeException("Unable to build top nav bar due to missing layout"); 1195 } 1196 mTopNavigationBarView.setStatusBar(this); 1197 mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener); 1198 } 1199 buildBottomBar(int layout)1200 private void buildBottomBar(int layout) { 1201 // SystemUI requires that the navigation bar view have a parent. Since the regular 1202 // StatusBar inflates navigation_bar_window as this parent view, use the same view for the 1203 // CarNavigationBarView. 1204 View.inflate(mContext, layout, mNavigationBarWindow); 1205 mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0); 1206 if (mNavigationBarView == null) { 1207 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); 1208 throw new RuntimeException("Unable to build bottom nav bar due to missing layout"); 1209 } 1210 mNavigationBarView.setStatusBar(this); 1211 mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); 1212 } 1213 buildLeft(int layout)1214 private void buildLeft(int layout) { 1215 View.inflate(mContext, layout, mLeftNavigationBarWindow); 1216 mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0); 1217 if (mLeftNavigationBarView == null) { 1218 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar"); 1219 throw new RuntimeException("Unable to build left nav bar due to missing layout"); 1220 } 1221 mLeftNavigationBarView.setStatusBar(this); 1222 mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); 1223 } 1224 1225 buildRight(int layout)1226 private void buildRight(int layout) { 1227 View.inflate(mContext, layout, mRightNavigationBarWindow); 1228 mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0); 1229 if (mRightNavigationBarView == null) { 1230 Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar"); 1231 throw new RuntimeException("Unable to build right nav bar due to missing layout"); 1232 } 1233 mRightNavigationBarView.setStatusBar(this); 1234 mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); 1235 } 1236 initHvac()1237 private void initHvac() { 1238 if (mHvacController == null) { 1239 mHvacController = Dependency.get(HvacController.class); 1240 mHvacController.connectToCarService(); 1241 } 1242 addTemperatureViewToController(mTopNavigationBarView); 1243 addTemperatureViewToController(mNavigationBarView); 1244 addTemperatureViewToController(mLeftNavigationBarView); 1245 addTemperatureViewToController(mRightNavigationBarView); 1246 } 1247 setNotificationViewBottomMargin(int bottomMargin)1248 private void setNotificationViewBottomMargin(int bottomMargin) { 1249 ViewGroup.MarginLayoutParams params = 1250 (ViewGroup.MarginLayoutParams) mNotificationView.getLayoutParams(); 1251 params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin); 1252 mNotificationView.setLayoutParams(params); 1253 } 1254 1255 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1256 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1257 //When executing dump() function simultaneously, we need to serialize them 1258 //to get mStackScroller's position correctly. 1259 synchronized (mQueueLock) { 1260 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 1261 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 1262 + " scroll " + mStackScroller.getScrollX() 1263 + "," + mStackScroller.getScrollY()); 1264 } 1265 1266 pw.print(" mTaskStackListener="); 1267 pw.println(mTaskStackListener); 1268 pw.print(" mCarFacetButtonController="); 1269 pw.println(mCarFacetButtonController); 1270 pw.print(" mFullscreenUserSwitcher="); 1271 pw.println(mFullscreenUserSwitcher); 1272 pw.print(" mCarBatteryController="); 1273 pw.println(mCarBatteryController); 1274 pw.print(" mBatteryMeterView="); 1275 pw.println(mBatteryMeterView); 1276 pw.print(" mNavigationBarView="); 1277 pw.println(mNavigationBarView); 1278 1279 if (KeyguardUpdateMonitor.getInstance(mContext) != null) { 1280 KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args); 1281 } 1282 1283 Dependency.get(FalsingManager.class).dump(pw); 1284 FalsingLog.dump(pw); 1285 1286 pw.println("SharedPreferences:"); 1287 for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { 1288 pw.print(" "); 1289 pw.print(entry.getKey()); 1290 pw.print("="); 1291 pw.println(entry.getValue()); 1292 } 1293 } 1294 1295 @Override showBatteryView()1296 public void showBatteryView() { 1297 if (Log.isLoggable(TAG, Log.DEBUG)) { 1298 Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView); 1299 } 1300 1301 if (mBatteryMeterView != null) { 1302 mBatteryMeterView.setVisibility(View.VISIBLE); 1303 } 1304 } 1305 1306 @Override hideBatteryView()1307 public void hideBatteryView() { 1308 if (Log.isLoggable(TAG, Log.DEBUG)) { 1309 Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView); 1310 } 1311 1312 if (mBatteryMeterView != null) { 1313 mBatteryMeterView.setVisibility(View.GONE); 1314 } 1315 } 1316 1317 /** 1318 * An implementation of TaskStackChangeListener, that listens for changes in the system 1319 * task stack and notifies the navigation bar. 1320 */ 1321 private class TaskStackListenerImpl extends TaskStackChangeListener { 1322 @Override onTaskStackChanged()1323 public void onTaskStackChanged() { 1324 try { 1325 mCarFacetButtonController.taskChanged( 1326 ActivityTaskManager.getService().getAllStackInfos()); 1327 } catch (Exception e) { 1328 Log.e(TAG, "Getting StackInfo from activity manager failed", e); 1329 } 1330 } 1331 1332 @Override onTaskDisplayChanged(int taskId, int newDisplayId)1333 public void onTaskDisplayChanged(int taskId, int newDisplayId) { 1334 try { 1335 mCarFacetButtonController.taskChanged( 1336 ActivityTaskManager.getService().getAllStackInfos()); 1337 } catch (Exception e) { 1338 Log.e(TAG, "Getting StackInfo from activity manager failed", e); 1339 } 1340 } 1341 } 1342 onDrivingStateChanged(CarDrivingStateEvent notUsed)1343 private void onDrivingStateChanged(CarDrivingStateEvent notUsed) { 1344 // Check if we need to start the timer every time driving state changes. 1345 startSwitchToGuestTimerIfDrivingOnKeyguard(); 1346 } 1347 startSwitchToGuestTimerIfDrivingOnKeyguard()1348 private void startSwitchToGuestTimerIfDrivingOnKeyguard() { 1349 if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) { 1350 // We're driving while keyguard is up. 1351 mSwitchToGuestTimer.start(); 1352 } else { 1353 mSwitchToGuestTimer.cancel(); 1354 } 1355 } 1356 1357 @Override createUserSwitcher()1358 protected void createUserSwitcher() { 1359 UserSwitcherController userSwitcherController = 1360 Dependency.get(UserSwitcherController.class); 1361 if (userSwitcherController.useFullscreenUserSwitcher()) { 1362 mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, 1363 mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); 1364 } else { 1365 super.createUserSwitcher(); 1366 } 1367 } 1368 1369 @Override setLockscreenUser(int newUserId)1370 public void setLockscreenUser(int newUserId) { 1371 super.setLockscreenUser(newUserId); 1372 // Try to dismiss the keyguard after every user switch. 1373 dismissKeyguardWhenUserSwitcherNotDisplayed(); 1374 } 1375 1376 @Override onStateChanged(int newState)1377 public void onStateChanged(int newState) { 1378 super.onStateChanged(newState); 1379 1380 startSwitchToGuestTimerIfDrivingOnKeyguard(); 1381 1382 if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) { 1383 hideUserSwitcher(); 1384 } else { 1385 dismissKeyguardWhenUserSwitcherNotDisplayed(); 1386 } 1387 } 1388 1389 /** Makes the full screen user switcher visible, if applicable. */ showUserSwitcher()1390 public void showUserSwitcher() { 1391 if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) { 1392 mFullscreenUserSwitcher.show(); // Makes the switcher visible. 1393 } 1394 } 1395 hideUserSwitcher()1396 private void hideUserSwitcher() { 1397 if (mFullscreenUserSwitcher != null) { 1398 mFullscreenUserSwitcher.hide(); 1399 } 1400 } 1401 1402 final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { 1403 @Override 1404 public void onScreenTurnedOn() { 1405 dismissKeyguardWhenUserSwitcherNotDisplayed(); 1406 } 1407 }; 1408 1409 // We automatically dismiss keyguard unless user switcher is being shown on the keyguard. dismissKeyguardWhenUserSwitcherNotDisplayed()1410 private void dismissKeyguardWhenUserSwitcherNotDisplayed() { 1411 if (mFullscreenUserSwitcher == null) { 1412 return; // Not using the full screen user switcher. 1413 } 1414 1415 if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER 1416 && !mFullscreenUserSwitcher.isVisible()) { 1417 // Current execution path continues to set state after this, thus we deffer the 1418 // dismissal to the next execution cycle. 1419 postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible. 1420 } 1421 } 1422 postDismissKeyguard()1423 public void postDismissKeyguard() { 1424 mHandler.post(this::dismissKeyguard); 1425 } 1426 1427 /** 1428 * Dismisses the keyguard and shows bouncer if authentication is necessary. 1429 */ dismissKeyguard()1430 public void dismissKeyguard() { 1431 // Don't dismiss keyguard when the screen is off. 1432 if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) { 1433 return; 1434 } 1435 executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */, 1436 true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); 1437 } 1438 1439 /** 1440 * Ensures that relevant child views are appropriately recreated when the device's density 1441 * changes. 1442 */ 1443 @Override onDensityOrFontScaleChanged()1444 public void onDensityOrFontScaleChanged() { 1445 super.onDensityOrFontScaleChanged(); 1446 restartNavBars(); 1447 // Need to update the background on density changed in case the change was due to night 1448 // mode. 1449 mNotificationPanelBackground = getDefaultWallpaper(); 1450 mScrimController.setScrimBehindDrawable(mNotificationPanelBackground); 1451 } 1452 1453 @Override onLocaleListChanged()1454 public void onLocaleListChanged() { 1455 // When locale changes we need to reload the notification panel with the new language 1456 if (mNotificationView == null) { 1457 return; 1458 } 1459 1460 LayoutParams params = mNotificationView.getLayoutParams(); 1461 int index = mStatusBarWindow.indexOfChild(mNotificationView); 1462 1463 mStatusBarWindow.removeView(mNotificationView); 1464 1465 View v = View.inflate(mContext, R.layout.notification_center_activity, null); 1466 mStatusBarWindow.addView(v, index, params); 1467 1468 restartNavBars(); 1469 connectNotificationsUI(); 1470 } 1471 1472 /** 1473 * Returns the {@link Drawable} that represents the wallpaper that the user has currently set. 1474 */ getDefaultWallpaper()1475 private Drawable getDefaultWallpaper() { 1476 return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper); 1477 } 1478 setNotificationViewClipBounds(int height)1479 private void setNotificationViewClipBounds(int height) { 1480 if (height > mNotificationView.getHeight()) { 1481 height = mNotificationView.getHeight(); 1482 } 1483 Rect clipBounds = new Rect(); 1484 clipBounds.set(0, 0, mNotificationView.getWidth(), height); 1485 // Sets the clip region on the notification list view. 1486 mNotificationView.setClipBounds(clipBounds); 1487 if (mHandleBar != null) { 1488 ViewGroup.MarginLayoutParams lp = 1489 (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams(); 1490 mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin); 1491 } 1492 if (mNotificationView.getHeight() > 0) { 1493 Drawable background = mNotificationView.getBackground().mutate(); 1494 background.setAlpha((int) (getBackgroundAlpha(height) * 255)); 1495 mNotificationView.setBackground(background); 1496 } 1497 } 1498 1499 /** 1500 * Calculates the alpha value for the background based on how much of the notification 1501 * shade is visible to the user. When the notification shade is completely open then 1502 * alpha value will be 1. 1503 */ getBackgroundAlpha(int height)1504 private float getBackgroundAlpha(int height) { 1505 return mInitialBackgroundAlpha + 1506 ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff); 1507 } 1508 calculatePercentageFromBottom(float height)1509 private void calculatePercentageFromBottom(float height) { 1510 if (mNotificationView.getHeight() > 0) { 1511 mPercentageFromBottom = (int) Math.abs( 1512 height / mNotificationView.getHeight() * 100); 1513 } 1514 } 1515 1516 private static final int SWIPE_DOWN_MIN_DISTANCE = 25; 1517 private static final int SWIPE_MAX_OFF_PATH = 75; 1518 private static final int SWIPE_THRESHOLD_VELOCITY = 200; 1519 1520 /** 1521 * Only responsible for open hooks. Since once the panel opens it covers all elements 1522 * there is no need to merge with close. 1523 */ 1524 private abstract class OpenNotificationGestureListener extends 1525 GestureDetector.SimpleOnGestureListener { 1526 1527 @Override onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY)1528 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, 1529 float distanceY) { 1530 1531 if (mNotificationView.getVisibility() == View.INVISIBLE) { 1532 // when the on-scroll is called for the first time to open. 1533 mNotificationList.scrollToPosition(0); 1534 } 1535 mStatusBarWindowController.setPanelVisible(true); 1536 mNotificationView.setVisibility(View.VISIBLE); 1537 1538 // clips the view for the notification shade when the user scrolls to open. 1539 setNotificationViewClipBounds((int) event2.getRawY()); 1540 1541 // Initially the scroll starts with height being zero. This checks protects from divide 1542 // by zero error. 1543 calculatePercentageFromBottom(event2.getRawY()); 1544 1545 mIsTracking = true; 1546 return true; 1547 } 1548 1549 1550 @Override onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY)1551 public boolean onFling(MotionEvent event1, MotionEvent event2, 1552 float velocityX, float velocityY) { 1553 if (velocityY > SWIPE_THRESHOLD_VELOCITY) { 1554 mOpeningVelocity = velocityY; 1555 openNotification(); 1556 return true; 1557 } 1558 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); 1559 1560 return false; 1561 } 1562 openNotification()1563 protected abstract void openNotification(); 1564 } 1565 1566 /** 1567 * To be installed on the open panel notification panel 1568 */ 1569 private abstract class CloseNotificationGestureListener extends 1570 GestureDetector.SimpleOnGestureListener { 1571 1572 @Override onSingleTapUp(MotionEvent motionEvent)1573 public boolean onSingleTapUp(MotionEvent motionEvent) { 1574 if (mPanelExpanded) { 1575 animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); 1576 } 1577 return true; 1578 } 1579 1580 @Override onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY)1581 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, 1582 float distanceY) { 1583 // should not clip while scroll to the bottom of the list. 1584 if (!mNotificationListAtBottomAtTimeOfTouch) { 1585 return false; 1586 } 1587 float actualNotificationHeight = 1588 mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY()); 1589 if (actualNotificationHeight > mNotificationView.getHeight()) { 1590 actualNotificationHeight = mNotificationView.getHeight(); 1591 } 1592 if (mNotificationView.getHeight() > 0) { 1593 mPercentageFromBottom = (int) Math.abs( 1594 actualNotificationHeight / mNotificationView.getHeight() * 100); 1595 boolean isUp = distanceY > 0; 1596 1597 // This check is to figure out if onScroll was called while swiping the card at 1598 // bottom of the list. At that time we should not allow notification shade to 1599 // close. We are also checking for the upwards swipe gesture here because it is 1600 // possible if a user is closing the notification shade and while swiping starts 1601 // to open again but does not fling. At that time we should allow the 1602 // notification shade to close fully or else it would stuck in between. 1603 if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight) 1604 > SWIPE_DOWN_MIN_DISTANCE && isUp) { 1605 setNotificationViewClipBounds((int) actualNotificationHeight); 1606 mIsTracking = true; 1607 } else if (!isUp) { 1608 setNotificationViewClipBounds((int) actualNotificationHeight); 1609 } 1610 } 1611 // if we return true the the items in RV won't be scrollable. 1612 return false; 1613 } 1614 1615 1616 @Override onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY)1617 public boolean onFling(MotionEvent event1, MotionEvent event2, 1618 float velocityX, float velocityY) { 1619 // should not fling if the touch does not start when view is at the bottom of the list. 1620 if (!mNotificationListAtBottomAtTimeOfTouch) { 1621 return false; 1622 } 1623 if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH 1624 || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { 1625 // swipe was not vertical or was not fast enough 1626 return false; 1627 } 1628 boolean isUp = velocityY < 0; 1629 if (isUp) { 1630 close(); 1631 return true; 1632 } else { 1633 // we should close the shade 1634 animateNotificationPanel(velocityY, false); 1635 } 1636 return false; 1637 } 1638 1639 protected abstract void close(); 1640 } 1641 1642 /** 1643 * To be installed on the nav bars. 1644 */ 1645 private abstract class NavBarCloseNotificationGestureListener extends 1646 CloseNotificationGestureListener { 1647 @Override 1648 public boolean onSingleTapUp(MotionEvent e) { 1649 mClosingVelocity = DEFAULT_FLING_VELOCITY; 1650 if (mPanelExpanded) { 1651 close(); 1652 } 1653 return super.onSingleTapUp(e); 1654 } 1655 1656 @Override 1657 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, 1658 float distanceY) { 1659 calculatePercentageFromBottom(event2.getRawY()); 1660 setNotificationViewClipBounds((int) event2.getRawY()); 1661 return true; 1662 } 1663 1664 @Override 1665 public void onLongPress(MotionEvent e) { 1666 mClosingVelocity = DEFAULT_FLING_VELOCITY; 1667 close(); 1668 super.onLongPress(e); 1669 } 1670 } 1671 1672 /** 1673 * To be installed on the handle bar. 1674 */ 1675 private class HandleBarCloseNotificationGestureListener extends 1676 GestureDetector.SimpleOnGestureListener { 1677 1678 @Override 1679 public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, 1680 float distanceY) { 1681 calculatePercentageFromBottom(event2.getRawY()); 1682 // To prevent the jump in the clip bounds while closing the notification shade using 1683 // the handle bar we should calculate the height using the diff of event1 and event2. 1684 // This will help the notification shade to clip smoothly as the event2 value changes 1685 // as event1 value will be fixed. 1686 int clipHeight = 1687 mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY()); 1688 setNotificationViewClipBounds(clipHeight); 1689 return true; 1690 } 1691 } 1692 1693 /** 1694 * SystemUi version of the notification manager that overrides methods such that the 1695 * notifications end up in the status bar layouts instead of a standalone window. 1696 */ 1697 private class CarSystemUIHeadsUpNotificationManager extends CarHeadsUpNotificationManager { 1698 1699 CarSystemUIHeadsUpNotificationManager(Context context, 1700 NotificationClickHandlerFactory clickHandlerFactory, 1701 NotificationDataManager notificationDataManager) { 1702 super(context, clickHandlerFactory, notificationDataManager); 1703 } 1704 1705 @Override 1706 protected View createHeadsUpPanel() { 1707 // In SystemUi the view is already in the window so just return a reference. 1708 return mStatusBarWindow.findViewById(R.id.notification_headsup); 1709 } 1710 1711 @Override 1712 protected void addHeadsUpPanelToDisplay() { 1713 // Set the panel initial state to invisible 1714 mHeadsUpPanel.setVisibility(View.INVISIBLE); 1715 } 1716 1717 @Override 1718 protected void setInternalInsetsInfo(ViewTreeObserver.InternalInsetsInfo info, 1719 HeadsUpEntry currentNotification, boolean panelExpanded) { 1720 super.setInternalInsetsInfo(info, currentNotification, mPanelExpanded); 1721 } 1722 1723 @Override 1724 protected void setHeadsUpVisible() { 1725 // if the Notifications panel is showing or SUW for user is in progress then don't show 1726 // heads up notifications 1727 if ((!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) 1728 || !mDeviceIsSetUpForUser || mIsUserSetupInProgress) { 1729 return; 1730 } 1731 1732 super.setHeadsUpVisible(); 1733 if (mHeadsUpPanel.getVisibility() == View.VISIBLE) { 1734 mStatusBarWindowController.setHeadsUpShowing(true); 1735 mStatusBarWindowController.setForceStatusBarVisible(true); 1736 } 1737 } 1738 1739 @Override 1740 protected void removeNotificationFromPanel(HeadsUpEntry currentHeadsUpNotification) { 1741 super.removeNotificationFromPanel(currentHeadsUpNotification); 1742 // If the panel ended up empty and hidden we can remove it from SystemUi 1743 if (mHeadsUpPanel.getVisibility() != View.VISIBLE) { 1744 mStatusBarWindowController.setHeadsUpShowing(false); 1745 mStatusBarWindowController.setForceStatusBarVisible(false); 1746 } 1747 } 1748 } 1749 } 1750