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