1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone.fragment; 16 17 import android.annotation.Nullable; 18 import android.annotation.SuppressLint; 19 import android.app.Fragment; 20 import android.database.ContentObserver; 21 import android.os.Bundle; 22 import android.os.Parcelable; 23 import android.os.Trace; 24 import android.os.UserHandle; 25 import android.provider.Settings; 26 import android.telephony.SubscriptionManager; 27 import android.util.ArrayMap; 28 import android.util.IndentingPrintWriter; 29 import android.util.SparseArray; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.LinearLayout; 34 35 import androidx.annotation.VisibleForTesting; 36 import androidx.core.animation.Animator; 37 38 import com.android.app.animation.Interpolators; 39 import com.android.app.animation.InterpolatorsAndroidX; 40 import com.android.keyguard.KeyguardUpdateMonitor; 41 import com.android.systemui.Dumpable; 42 import com.android.systemui.Flags; 43 import com.android.systemui.dagger.qualifiers.Main; 44 import com.android.systemui.demomode.DemoMode; 45 import com.android.systemui.demomode.DemoModeController; 46 import com.android.systemui.dump.DumpManager; 47 import com.android.systemui.plugins.statusbar.StatusBarStateController; 48 import com.android.systemui.res.R; 49 import com.android.systemui.shade.ShadeExpansionStateManager; 50 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; 51 import com.android.systemui.statusbar.CommandQueue; 52 import com.android.systemui.statusbar.OperatorNameView; 53 import com.android.systemui.statusbar.OperatorNameViewController; 54 import com.android.systemui.statusbar.StatusBarState; 55 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState; 56 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; 57 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; 58 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; 59 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor; 60 import com.android.systemui.statusbar.phone.NotificationIconAreaController; 61 import com.android.systemui.statusbar.phone.NotificationIconContainer; 62 import com.android.systemui.statusbar.phone.PhoneStatusBarView; 63 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; 64 import com.android.systemui.statusbar.phone.StatusBarLocation; 65 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; 66 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; 67 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable; 68 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; 69 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; 70 import com.android.systemui.statusbar.phone.ui.DarkIconManager; 71 import com.android.systemui.statusbar.phone.ui.StatusBarIconController; 72 import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder; 73 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; 74 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel; 75 import com.android.systemui.statusbar.policy.KeyguardStateController; 76 import com.android.systemui.statusbar.window.StatusBarWindowStateController; 77 import com.android.systemui.statusbar.window.StatusBarWindowStateListener; 78 import com.android.systemui.util.CarrierConfigTracker; 79 import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener; 80 import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; 81 import com.android.systemui.util.settings.SecureSettings; 82 83 import kotlin.Unit; 84 85 import kotlinx.coroutines.DisposableHandle; 86 87 import java.io.PrintWriter; 88 import java.util.ArrayList; 89 import java.util.Arrays; 90 import java.util.List; 91 import java.util.Map; 92 import java.util.Set; 93 import java.util.concurrent.Executor; 94 95 import javax.inject.Inject; 96 97 /** 98 * Contains the collapsed status bar and handles hiding/showing based on disable flags 99 * and keyguard state. Also manages lifecycle to make sure the views it contains are being 100 * updated by the StatusBarIconController and DarkIconManager while it is attached. 101 */ 102 @SuppressLint("ValidFragment") 103 public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks, 104 StatusBarStateController.StateListener, 105 SystemStatusAnimationCallback, Dumpable { 106 107 public static final String TAG = "CollapsedStatusBarFragment"; 108 private static final String EXTRA_PANEL_STATE = "panel_state"; 109 public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager"; 110 public static final int FADE_IN_DURATION = 320; 111 public static final int FADE_OUT_DURATION = 160; 112 public static final int FADE_IN_DELAY = 50; 113 private static final int SOURCE_SYSTEM_EVENT_ANIMATOR = 1; 114 private static final int SOURCE_OTHER = 2; 115 private StatusBarFragmentComponent mStatusBarFragmentComponent; 116 private PhoneStatusBarView mStatusBar; 117 private final StatusBarStateController mStatusBarStateController; 118 private final KeyguardStateController mKeyguardStateController; 119 private final PanelExpansionInteractor mPanelExpansionInteractor; 120 private MultiSourceMinAlphaController mEndSideAlphaController; 121 private LinearLayout mEndSideContent; 122 private View mClockView; 123 private View mOngoingActivityChip; 124 private View mNotificationIconAreaInner; 125 // Visibilities come in from external system callers via disable flags, but we also sometimes 126 // modify the visibilities internally. We need to store both so that we don't accidentally 127 // propagate our internally modified flags for too long. 128 private StatusBarVisibilityModel mLastSystemVisibility = 129 StatusBarVisibilityModel.createDefaultModel(); 130 private StatusBarVisibilityModel mLastModifiedVisibility = 131 StatusBarVisibilityModel.createDefaultModel(); 132 private DarkIconManager mDarkIconManager; 133 private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; 134 private final CommandQueue mCommandQueue; 135 private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger; 136 private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory; 137 private final OngoingCallController mOngoingCallController; 138 private final SystemStatusAnimationScheduler mAnimationScheduler; 139 private final StatusBarLocationPublisher mLocationPublisher; 140 private final NotificationIconAreaController mNotificationIconAreaController; 141 private final ShadeExpansionStateManager mShadeExpansionStateManager; 142 private final StatusBarIconController mStatusBarIconController; 143 private final CarrierConfigTracker mCarrierConfigTracker; 144 private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel; 145 private final CollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder; 146 private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; 147 private final DarkIconManager.Factory mDarkIconManagerFactory; 148 private final SecureSettings mSecureSettings; 149 private final Executor mMainExecutor; 150 private final DumpManager mDumpManager; 151 private final StatusBarWindowStateController mStatusBarWindowStateController; 152 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 153 private final NotificationIconContainerStatusBarViewBinder mNicViewBinder; 154 private final DemoModeController mDemoModeController; 155 156 private List<String> mBlockedIcons = new ArrayList<>(); 157 private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); 158 159 private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() { 160 @Override 161 public void onOngoingCallStateChanged(boolean animate) { 162 updateStatusBarVisibilities(animate); 163 } 164 }; 165 private OperatorNameViewController mOperatorNameViewController; 166 private StatusBarSystemEventDefaultAnimator mSystemEventAnimator; 167 168 private final CarrierConfigChangedListener mCarrierConfigCallback = 169 new CarrierConfigChangedListener() { 170 @Override 171 public void onCarrierConfigChanged() { 172 if (mOperatorNameViewController == null) { 173 initOperatorName(); 174 } else { 175 // Already initialized, KeyguardUpdateMonitorCallback will handle the update 176 } 177 } 178 }; 179 180 private final DefaultDataSubscriptionChangedListener mDefaultDataListener = 181 new DefaultDataSubscriptionChangedListener() { 182 @Override 183 public void onDefaultSubscriptionChanged(int subId) { 184 if (mOperatorNameViewController == null) { 185 initOperatorName(); 186 } 187 } 188 }; 189 190 /** 191 * Whether we've launched the secure camera over the lockscreen, but haven't yet received a 192 * status bar window state change afterward. 193 * 194 * We wait for this state change (which will tell us whether to show/hide the status bar icons) 195 * so that there is no flickering/jump cutting during the camera launch. 196 */ 197 private boolean mWaitingForWindowStateChangeAfterCameraLaunch = false; 198 199 /** 200 * True when a transition from lockscreen to dream has started, but haven't yet received a 201 * status bar window state change afterward. 202 * 203 * Similar to [mWaitingForWindowStateChangeAfterCameraLaunch]. 204 */ 205 private boolean mTransitionFromLockscreenToDreamStarted = false; 206 207 /** 208 * True if there's an active ongoing activity that should be showing a chip and false otherwise. 209 */ 210 private boolean mHasOngoingActivity; 211 212 /** 213 * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives 214 * a new status bar window state. 215 */ 216 private final StatusBarWindowStateListener mStatusBarWindowStateListener = state -> { 217 mWaitingForWindowStateChangeAfterCameraLaunch = false; 218 mTransitionFromLockscreenToDreamStarted = false; 219 }; 220 private DisposableHandle mNicBindingDisposable; 221 222 private boolean mAnimationsEnabled = true; 223 224 @Inject CollapsedStatusBarFragment( StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, ShadeExpansionStateManager shadeExpansionStateManager, StatusBarIconController statusBarIconController, DarkIconManager.Factory darkIconManagerFactory, CollapsedStatusBarViewModel collapsedStatusBarViewModel, CollapsedStatusBarViewBinder collapsedStatusBarViewBinder, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, KeyguardStateController keyguardStateController, PanelExpansionInteractor panelExpansionInteractor, StatusBarStateController statusBarStateController, NotificationIconContainerStatusBarViewBinder nicViewBinder, CommandQueue commandQueue, CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @Main Executor mainExecutor, DumpManager dumpManager, StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, DemoModeController demoModeController)225 public CollapsedStatusBarFragment( 226 StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, 227 OngoingCallController ongoingCallController, 228 SystemStatusAnimationScheduler animationScheduler, 229 StatusBarLocationPublisher locationPublisher, 230 NotificationIconAreaController notificationIconAreaController, 231 ShadeExpansionStateManager shadeExpansionStateManager, 232 StatusBarIconController statusBarIconController, 233 DarkIconManager.Factory darkIconManagerFactory, 234 CollapsedStatusBarViewModel collapsedStatusBarViewModel, 235 CollapsedStatusBarViewBinder collapsedStatusBarViewBinder, 236 StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, 237 KeyguardStateController keyguardStateController, 238 PanelExpansionInteractor panelExpansionInteractor, 239 StatusBarStateController statusBarStateController, 240 NotificationIconContainerStatusBarViewBinder nicViewBinder, 241 CommandQueue commandQueue, 242 CarrierConfigTracker carrierConfigTracker, 243 CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, 244 OperatorNameViewController.Factory operatorNameViewControllerFactory, 245 SecureSettings secureSettings, 246 @Main Executor mainExecutor, 247 DumpManager dumpManager, 248 StatusBarWindowStateController statusBarWindowStateController, 249 KeyguardUpdateMonitor keyguardUpdateMonitor, 250 DemoModeController demoModeController) { 251 mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; 252 mOngoingCallController = ongoingCallController; 253 mAnimationScheduler = animationScheduler; 254 mLocationPublisher = locationPublisher; 255 mNotificationIconAreaController = notificationIconAreaController; 256 mShadeExpansionStateManager = shadeExpansionStateManager; 257 mStatusBarIconController = statusBarIconController; 258 mCollapsedStatusBarViewModel = collapsedStatusBarViewModel; 259 mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder; 260 mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; 261 mDarkIconManagerFactory = darkIconManagerFactory; 262 mKeyguardStateController = keyguardStateController; 263 mPanelExpansionInteractor = panelExpansionInteractor; 264 mStatusBarStateController = statusBarStateController; 265 mNicViewBinder = nicViewBinder; 266 mCommandQueue = commandQueue; 267 mCarrierConfigTracker = carrierConfigTracker; 268 mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger; 269 mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; 270 mSecureSettings = secureSettings; 271 mMainExecutor = mainExecutor; 272 mDumpManager = dumpManager; 273 mStatusBarWindowStateController = statusBarWindowStateController; 274 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 275 mDemoModeController = demoModeController; 276 } 277 278 private final DemoMode mDemoModeCallback = new DemoMode() { 279 @Override 280 public List<String> demoCommands() { 281 return List.of(DemoMode.COMMAND_NOTIFICATIONS); 282 } 283 284 @Override 285 public void dispatchDemoCommand(String command, Bundle args) { 286 if (mNotificationIconAreaInner == null) return; 287 String visible = args.getString("visible"); 288 if ("false".equals(visible)) { 289 mNotificationIconAreaInner.setVisibility(View.INVISIBLE); 290 } else { 291 mNotificationIconAreaInner.setVisibility(View.VISIBLE); 292 } 293 } 294 295 @Override 296 public void onDemoModeFinished() { 297 if (mNotificationIconAreaInner == null) return; 298 mNotificationIconAreaInner.setVisibility(View.VISIBLE); 299 } 300 }; 301 302 @Override onCreate(Bundle savedInstanceState)303 public void onCreate(Bundle savedInstanceState) { 304 super.onCreate(savedInstanceState); 305 mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener); 306 if (NotificationIconContainerRefactor.isEnabled()) { 307 mDemoModeController.addCallback(mDemoModeCallback); 308 } 309 } 310 311 @Override onDestroy()312 public void onDestroy() { 313 super.onDestroy(); 314 mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener); 315 if (NotificationIconContainerRefactor.isEnabled()) { 316 mDemoModeController.removeCallback(mDemoModeCallback); 317 } 318 } 319 320 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)321 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 322 Bundle savedInstanceState) { 323 return inflater.inflate(R.layout.status_bar, container, false); 324 } 325 326 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)327 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 328 super.onViewCreated(view, savedInstanceState); 329 mDumpManager.registerDumpable(getClass().getSimpleName(), this); 330 mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create( 331 (PhoneStatusBarView) getView()); 332 mStatusBarFragmentComponent.init(); 333 mStartableStates.clear(); 334 for (Startable startable : mStatusBarFragmentComponent.getStartables()) { 335 mStartableStates.put(startable, Startable.State.STARTING); 336 startable.start(); 337 mStartableStates.put(startable, Startable.State.STARTED); 338 } 339 340 mStatusBar = (PhoneStatusBarView) view; 341 View contents = mStatusBar.findViewById(R.id.status_bar_contents); 342 contents.addOnLayoutChangeListener(mStatusBarLayoutListener); 343 updateStatusBarLocation(contents.getLeft(), contents.getRight()); 344 if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) { 345 mStatusBar.restoreHierarchyState( 346 savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE)); 347 } 348 mDarkIconManager = mDarkIconManagerFactory.create( 349 view.findViewById(R.id.statusIcons), StatusBarLocation.HOME); 350 mDarkIconManager.setShouldLog(true); 351 updateBlockedIcons(); 352 mStatusBarIconController.addIconGroup(mDarkIconManager); 353 mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content); 354 mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent); 355 mClockView = mStatusBar.findViewById(R.id.clock); 356 mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip); 357 showEndSideContent(false); 358 showClock(false); 359 initOperatorName(); 360 initNotificationIconArea(); 361 mSystemEventAnimator = getSystemEventAnimator(); 362 mCarrierConfigTracker.addCallback(mCarrierConfigCallback); 363 mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); 364 365 mCollapsedStatusBarViewBinder.bind( 366 mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener); 367 } 368 369 @Override onCameraLaunchGestureDetected(int source)370 public void onCameraLaunchGestureDetected(int source) { 371 mWaitingForWindowStateChangeAfterCameraLaunch = true; 372 } 373 374 @VisibleForTesting updateBlockedIcons()375 void updateBlockedIcons() { 376 mBlockedIcons.clear(); 377 378 // Reload the blocklist from res 379 List<String> blockList = Arrays.asList(getResources().getStringArray( 380 R.array.config_collapsed_statusbar_icon_blocklist)); 381 String vibrateIconSlot = getString(com.android.internal.R.string.status_bar_volume); 382 boolean showVibrateIcon = 383 mSecureSettings.getIntForUser( 384 Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 385 0, 386 UserHandle.USER_CURRENT) == 0; 387 388 // Filter out vibrate icon from the blocklist if the setting is on 389 for (int i = 0; i < blockList.size(); i++) { 390 if (blockList.get(i).equals(vibrateIconSlot)) { 391 if (showVibrateIcon) { 392 mBlockedIcons.add(blockList.get(i)); 393 } 394 } else { 395 mBlockedIcons.add(blockList.get(i)); 396 } 397 } 398 399 mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons)); 400 } 401 402 @VisibleForTesting getBlockedIcons()403 List<String> getBlockedIcons() { 404 return mBlockedIcons; 405 } 406 407 408 @VisibleForTesting enableAnimationsForTesting()409 void enableAnimationsForTesting() { 410 mAnimationsEnabled = true; 411 } 412 413 @VisibleForTesting disableAnimationsForTesting()414 void disableAnimationsForTesting() { 415 mAnimationsEnabled = false; 416 } 417 418 @Override onSaveInstanceState(Bundle outState)419 public void onSaveInstanceState(Bundle outState) { 420 super.onSaveInstanceState(outState); 421 SparseArray<Parcelable> states = new SparseArray<>(); 422 mStatusBar.saveHierarchyState(states); 423 outState.putSparseParcelableArray(EXTRA_PANEL_STATE, states); 424 } 425 426 @Override onResume()427 public void onResume() { 428 super.onResume(); 429 mCommandQueue.addCallback(this); 430 mStatusBarStateController.addCallback(this); 431 initOngoingCallChip(); 432 mAnimationScheduler.addCallback(this); 433 434 mSecureSettings.registerContentObserverForUserSync( 435 Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 436 false, 437 mVolumeSettingObserver, 438 UserHandle.USER_ALL); 439 } 440 441 @Override onPause()442 public void onPause() { 443 super.onPause(); 444 mCommandQueue.removeCallback(this); 445 mStatusBarStateController.removeCallback(this); 446 mOngoingCallController.removeCallback(mOngoingCallListener); 447 mAnimationScheduler.removeCallback(this); 448 mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver); 449 } 450 451 @Override onDestroyView()452 public void onDestroyView() { 453 super.onDestroyView(); 454 mStatusBarIconController.removeIconGroup(mDarkIconManager); 455 mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); 456 mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); 457 458 for (Startable startable : mStatusBarFragmentComponent.getStartables()) { 459 mStartableStates.put(startable, Startable.State.STOPPING); 460 startable.stop(); 461 mStartableStates.put(startable, Startable.State.STOPPED); 462 } 463 mDumpManager.unregisterDumpable(getClass().getSimpleName()); 464 if (NotificationIconContainerRefactor.isEnabled()) { 465 if (mNicBindingDisposable != null) { 466 mNicBindingDisposable.dispose(); 467 mNicBindingDisposable = null; 468 } 469 } 470 } 471 472 /** Initializes views related to the notification icon area. */ initNotificationIconArea()473 public void initNotificationIconArea() { 474 Trace.beginSection("CollapsedStatusBarFragment#initNotifIconArea"); 475 ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area); 476 if (NotificationIconContainerRefactor.isEnabled()) { 477 LayoutInflater.from(getContext()) 478 .inflate(R.layout.notification_icon_area, notificationIconArea, true); 479 NotificationIconContainer notificationIcons = 480 notificationIconArea.requireViewById(R.id.notificationIcons); 481 mNotificationIconAreaInner = notificationIcons; 482 mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); 483 } else { 484 mNotificationIconAreaInner = 485 mNotificationIconAreaController.getNotificationInnerAreaView(); 486 if (mNotificationIconAreaInner.getParent() != null) { 487 ((ViewGroup) mNotificationIconAreaInner.getParent()) 488 .removeView(mNotificationIconAreaInner); 489 } 490 notificationIconArea.addView(mNotificationIconAreaInner); 491 } 492 493 updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false); 494 Trace.endSection(); 495 } 496 497 /** 498 * Returns the dagger component for this fragment. 499 * 500 * TODO(b/205609837): Eventually, the dagger component should encapsulate all status bar 501 * fragment functionality and we won't need to expose it here anymore. 502 */ 503 @Nullable getStatusBarFragmentComponent()504 public StatusBarFragmentComponent getStatusBarFragmentComponent() { 505 return mStatusBarFragmentComponent; 506 } 507 508 private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener = 509 new StatusBarVisibilityChangeListener() { 510 @Override 511 public void onStatusBarVisibilityMaybeChanged() { 512 updateStatusBarVisibilities(/* animate= */ true); 513 } 514 515 @Override 516 public void onTransitionFromLockscreenToDreamStarted() { 517 mTransitionFromLockscreenToDreamStarted = true; 518 } 519 520 @Override 521 public void onOngoingActivityStatusChanged(boolean hasOngoingActivity) { 522 mHasOngoingActivity = hasOngoingActivity; 523 updateStatusBarVisibilities(/* animate= */ true); 524 } 525 }; 526 527 @Override disable(int displayId, int state1, int state2, boolean animate)528 public void disable(int displayId, int state1, int state2, boolean animate) { 529 if (displayId != getContext().getDisplayId()) { 530 return; 531 } 532 mCollapsedStatusBarFragmentLogger 533 .logDisableFlagChange(new DisableState(state1, state2)); 534 mLastSystemVisibility = 535 StatusBarVisibilityModel.createModelFromFlags(state1, state2); 536 updateStatusBarVisibilities(animate); 537 } 538 updateStatusBarVisibilities(boolean animate)539 private void updateStatusBarVisibilities(boolean animate) { 540 StatusBarVisibilityModel previousModel = mLastModifiedVisibility; 541 StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility); 542 mCollapsedStatusBarFragmentLogger.logVisibilityModel(newModel); 543 mLastModifiedVisibility = newModel; 544 545 if (newModel.getShowSystemInfo() != previousModel.getShowSystemInfo()) { 546 if (newModel.getShowSystemInfo()) { 547 showEndSideContent(animate); 548 showOperatorName(animate); 549 } else { 550 hideEndSideContent(animate); 551 hideOperatorName(animate); 552 } 553 } 554 555 // The ongoing activity chip and notification icon visibilities are intertwined, so update 556 // both if either change. 557 boolean notifsChanged = 558 newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons(); 559 boolean ongoingActivityChanged = 560 newModel.getShowOngoingActivityChip() != previousModel.getShowOngoingActivityChip(); 561 if (notifsChanged || ongoingActivityChanged) { 562 updateNotificationIconAreaAndOngoingActivityChip(animate); 563 } 564 565 // The clock may have already been hidden, but we might want to shift its 566 // visibility to GONE from INVISIBLE or vice versa 567 if (newModel.getShowClock() != previousModel.getShowClock() 568 || mClockView.getVisibility() != clockHiddenMode()) { 569 if (newModel.getShowClock()) { 570 showClock(animate); 571 } else { 572 hideClock(animate); 573 } 574 } 575 } 576 calculateInternalModel( StatusBarVisibilityModel externalModel)577 private StatusBarVisibilityModel calculateInternalModel( 578 StatusBarVisibilityModel externalModel) { 579 // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead. 580 boolean headsUpVisible = 581 mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible(); 582 583 if (!mKeyguardStateController.isLaunchTransitionFadingAway() 584 && !mKeyguardStateController.isKeyguardFadingAway() 585 && shouldHideStatusBar() 586 && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD 587 && headsUpVisible)) { 588 // Hide everything 589 return new StatusBarVisibilityModel( 590 /* showClock= */ false, 591 /* showNotificationIcons= */ false, 592 /* showOngoingActivityChip= */ false, 593 /* showSystemInfo= */ false); 594 } 595 596 boolean showClock = externalModel.getShowClock() && !headsUpVisible; 597 598 boolean showOngoingActivityChip; 599 if (Flags.statusBarScreenSharingChips()) { 600 // If this flag is on, the ongoing activity status comes from 601 // CollapsedStatusBarViewBinder, which updates the mHasOngoingActivity variable. 602 showOngoingActivityChip = mHasOngoingActivity; 603 } else { 604 // If this flag is off, the only ongoing activity is the ongoing call, and we pull it 605 // from the controller directly. 606 showOngoingActivityChip = mOngoingCallController.hasOngoingCall(); 607 } 608 609 return new StatusBarVisibilityModel( 610 showClock, 611 externalModel.getShowNotificationIcons(), 612 showOngoingActivityChip && !headsUpVisible, 613 externalModel.getShowSystemInfo()); 614 } 615 616 /** 617 * Updates the visibility of the notification icon area and ongoing activity chip based on 618 * mLastModifiedVisibility. 619 */ updateNotificationIconAreaAndOngoingActivityChip(boolean animate)620 private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) { 621 StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility; 622 boolean disableNotifications = !visibilityModel.getShowNotificationIcons(); 623 boolean hasOngoingActivity = visibilityModel.getShowOngoingActivityChip(); 624 625 // Hide notifications if the disable flag is set or we have an ongoing activity. 626 if (disableNotifications || hasOngoingActivity) { 627 hideNotificationIconArea(animate && !hasOngoingActivity); 628 } else { 629 showNotificationIconArea(animate); 630 } 631 632 // Show the ongoing activity chip only if there is an ongoing activity *and* notification 633 // icons are allowed. (The ongoing activity chip occupies the same area as the notification, 634 // icons so if the icons are disabled then the activity chip should be, too.) 635 boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications; 636 if (showOngoingActivityChip) { 637 showOngoingActivityChip(animate); 638 } else { 639 hideOngoingActivityChip(animate); 640 } 641 if (!Flags.statusBarScreenSharingChips()) { 642 mOngoingCallController.notifyChipVisibilityChanged(showOngoingActivityChip); 643 } 644 } 645 shouldHideStatusBar()646 private boolean shouldHideStatusBar() { 647 if (!mShadeExpansionStateManager.isClosed() 648 && mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()) { 649 return true; 650 } 651 652 // When launching the camera over the lockscreen, the icons become visible momentarily 653 // before animating out, since we're not yet aware that the launching camera activity is 654 // fullscreen. Even once the activity finishes launching, it takes a short time before WM 655 // decides that the top app wants to hide the icons and tells us to hide them. To ensure 656 // that this high-visibility animation is smooth, keep the icons hidden during a camera 657 // launch until we receive a window state change which indicates that the activity is done 658 // launching and WM has decided to show/hide the icons. For extra safety (to ensure the 659 // icons don't remain hidden somehow) we double check that the camera is still showing, the 660 // status bar window isn't hidden, and we're still occluded as well, though these checks 661 // are typically unnecessary. 662 // 663 // TODO(b/273314977): Can this be deleted now that we have the 664 // [isTransitioningFromLockscreenToOccluded] check below? 665 final boolean hideIconsForSecureCamera = 666 (mWaitingForWindowStateChangeAfterCameraLaunch || 667 !mStatusBarWindowStateController.windowIsShowing()) && 668 mKeyguardUpdateMonitor.isSecureCameraLaunchedOverKeyguard() && 669 mKeyguardStateController.isOccluded(); 670 671 if (hideIconsForSecureCamera) { 672 return true; 673 } 674 675 // Similar to [hideIconsForSecureCamera]: When dream is launched over lockscreen, the icons 676 // are momentarily visible because the dream animation has finished, but SysUI has not been 677 // informed that the dream is full-screen. For extra safety, we double-check that we're 678 // still dreaming. 679 final boolean hideIconsForDream = 680 mTransitionFromLockscreenToDreamStarted 681 && mKeyguardUpdateMonitor.isDreaming() 682 && mKeyguardStateController.isOccluded(); 683 if (hideIconsForDream) { 684 return true; 685 } 686 687 // While the status bar is transitioning from lockscreen to an occluded, we don't yet know 688 // if the occluding activity is fullscreen or not. If it *is* fullscreen, we don't want to 689 // briefly show the status bar just to immediately hide it again. So, we wait for the 690 // transition to occluding to finish before allowing us to potentially show the status bar 691 // again. (This status bar is always hidden on keyguard, so it's safe to continue hiding it 692 // during this transition.) See b/273314977. 693 if (mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) { 694 return true; 695 } 696 697 return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer(); 698 } 699 hideEndSideContent(boolean animate)700 private void hideEndSideContent(boolean animate) { 701 if (!animate) { 702 mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER); 703 } else { 704 mEndSideAlphaController.animateToAlpha(/*alpha*/ 0f, SOURCE_OTHER, FADE_OUT_DURATION, 705 InterpolatorsAndroidX.ALPHA_OUT, /*startDelay*/ 0); 706 } 707 } 708 showEndSideContent(boolean animate)709 private void showEndSideContent(boolean animate) { 710 if (!animate) { 711 mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER); 712 return; 713 } 714 if (mKeyguardStateController.isKeyguardFadingAway()) { 715 mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER, 716 mKeyguardStateController.getKeyguardFadingAwayDuration(), 717 InterpolatorsAndroidX.LINEAR_OUT_SLOW_IN, 718 mKeyguardStateController.getKeyguardFadingAwayDelay()); 719 } else { 720 mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER, FADE_IN_DURATION, 721 InterpolatorsAndroidX.ALPHA_IN, FADE_IN_DELAY); 722 } 723 } 724 hideClock(boolean animate)725 private void hideClock(boolean animate) { 726 animateHiddenState(mClockView, clockHiddenMode(), animate); 727 } 728 showClock(boolean animate)729 private void showClock(boolean animate) { 730 animateShow(mClockView, animate); 731 } 732 733 /** Hides the ongoing activity chip. */ hideOngoingActivityChip(boolean animate)734 private void hideOngoingActivityChip(boolean animate) { 735 animateHiddenState(mOngoingActivityChip, View.GONE, animate); 736 } 737 738 /** 739 * Displays the ongoing activity chip. 740 * 741 * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the 742 * ongoing call information, If that flag is enabled, it will support different kinds of ongoing 743 * activities. See b/332662551. 744 */ showOngoingActivityChip(boolean animate)745 private void showOngoingActivityChip(boolean animate) { 746 animateShow(mOngoingActivityChip, animate); 747 } 748 749 /** 750 * If panel is expanded/expanding it usually means QS shade is opening, so 751 * don't set the clock GONE otherwise it'll mess up the animation. 752 */ clockHiddenMode()753 private int clockHiddenMode() { 754 if (!mShadeExpansionStateManager.isClosed() && !mKeyguardStateController.isShowing() 755 && !mStatusBarStateController.isDozing()) { 756 return View.INVISIBLE; 757 } 758 return View.GONE; 759 } 760 hideNotificationIconArea(boolean animate)761 public void hideNotificationIconArea(boolean animate) { 762 animateHide(mNotificationIconAreaInner, animate); 763 } 764 showNotificationIconArea(boolean animate)765 public void showNotificationIconArea(boolean animate) { 766 animateShow(mNotificationIconAreaInner, animate); 767 } 768 hideOperatorName(boolean animate)769 public void hideOperatorName(boolean animate) { 770 if (mOperatorNameViewController != null) { 771 animateHide(mOperatorNameViewController.getView(), animate); 772 } 773 } 774 showOperatorName(boolean animate)775 public void showOperatorName(boolean animate) { 776 if (mOperatorNameViewController != null) { 777 animateShow(mOperatorNameViewController.getView(), animate); 778 } 779 } 780 781 /** 782 * Animate a view to INVISIBLE or GONE 783 */ animateHiddenState(final View v, int state, boolean animate)784 private void animateHiddenState(final View v, int state, boolean animate) { 785 v.animate().cancel(); 786 if (!animate || !mAnimationsEnabled) { 787 v.setAlpha(0f); 788 v.setVisibility(state); 789 return; 790 } 791 792 v.animate() 793 .alpha(0f) 794 .setDuration(FADE_OUT_DURATION) 795 .setStartDelay(0) 796 .setInterpolator(Interpolators.ALPHA_OUT) 797 .withEndAction(() -> v.setVisibility(state)); 798 } 799 800 /** 801 * Hides a view. 802 */ animateHide(final View v, boolean animate)803 private void animateHide(final View v, boolean animate) { 804 animateHiddenState(v, View.INVISIBLE, animate); 805 } 806 807 /** 808 * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable. 809 */ animateShow(View v, boolean animate)810 private void animateShow(View v, boolean animate) { 811 v.animate().cancel(); 812 v.setVisibility(View.VISIBLE); 813 if (!animate || !mAnimationsEnabled) { 814 v.setAlpha(1f); 815 return; 816 } 817 v.animate() 818 .alpha(1f) 819 .setDuration(FADE_IN_DURATION) 820 .setInterpolator(Interpolators.ALPHA_IN) 821 .setStartDelay(FADE_IN_DELAY) 822 823 // We need to clean up any pending end action from animateHide if we call 824 // both hide and show in the same frame before the animation actually gets started. 825 // cancel() doesn't really remove the end action. 826 .withEndAction(null); 827 828 // Synchronize the motion with the Keyguard fading if necessary. 829 if (mKeyguardStateController.isKeyguardFadingAway()) { 830 v.animate() 831 .setDuration(mKeyguardStateController.getKeyguardFadingAwayDuration()) 832 .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) 833 .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) 834 .start(); 835 } 836 } 837 initOperatorName()838 private void initOperatorName() { 839 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 840 if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) { 841 View view = mStatusBar.findViewById(R.id.operator_name); 842 mOperatorNameViewController = 843 mOperatorNameViewControllerFactory.create((OperatorNameView) view); 844 mOperatorNameViewController.init(); 845 // This view should not be visible on lock-screen 846 if (mKeyguardStateController.isShowing()) { 847 hideOperatorName(false); 848 } 849 } 850 } 851 initOngoingCallChip()852 private void initOngoingCallChip() { 853 mOngoingCallController.addCallback(mOngoingCallListener); 854 mOngoingCallController.setChipView(mOngoingActivityChip); 855 } 856 857 @Override onStateChanged(int newState)858 public void onStateChanged(int newState) { } 859 860 @Override onDozingChanged(boolean isDozing)861 public void onDozingChanged(boolean isDozing) { 862 updateStatusBarVisibilities(/* animate= */ false); 863 } 864 865 @Nullable 866 @Override onSystemEventAnimationBegin()867 public Animator onSystemEventAnimationBegin() { 868 return mSystemEventAnimator.onSystemEventAnimationBegin(); 869 } 870 871 @Nullable 872 @Override onSystemEventAnimationFinish(boolean hasPersistentDot)873 public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) { 874 return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot); 875 } 876 getSystemEventAnimator()877 private StatusBarSystemEventDefaultAnimator getSystemEventAnimator() { 878 return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> { 879 mEndSideAlphaController.setAlpha(alpha, SOURCE_SYSTEM_EVENT_ANIMATOR); 880 return Unit.INSTANCE; 881 }, (translationX) -> { 882 mEndSideContent.setTranslationX(translationX); 883 return Unit.INSTANCE; 884 }, /*isAnimationRunning*/ false); 885 } 886 updateStatusBarLocation(int left, int right)887 private void updateStatusBarLocation(int left, int right) { 888 int leftMargin = left - mStatusBar.getLeft(); 889 int rightMargin = mStatusBar.getRight() - right; 890 891 mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin); 892 } 893 894 private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) { 895 @Override 896 public void onChange(boolean selfChange) { 897 updateBlockedIcons(); 898 } 899 }; 900 901 // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot 902 private View.OnLayoutChangeListener mStatusBarLayoutListener = 903 (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 904 if (left != oldLeft || right != oldRight) { 905 updateStatusBarLocation(left, right); 906 } 907 }; 908 909 @Override dump(PrintWriter printWriter, String[] args)910 public void dump(PrintWriter printWriter, String[] args) { 911 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" "); 912 pw.println("mHasOngoingActivity=" + mHasOngoingActivity); 913 pw.println("mAnimationsEnabled=" + mAnimationsEnabled); 914 StatusBarFragmentComponent component = mStatusBarFragmentComponent; 915 if (component == null) { 916 pw.println("StatusBarFragmentComponent is null"); 917 } else { 918 Set<Startable> startables = component.getStartables(); 919 pw.println("Startables: " + startables.size()); 920 pw.increaseIndent(); 921 for (Startable startable : startables) { 922 Startable.State startableState = mStartableStates.getOrDefault(startable, 923 Startable.State.NONE); 924 pw.println(startable + ", state: " + startableState); 925 } 926 pw.decreaseIndent(); 927 } 928 } 929 } 930