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