| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.statusbar.phone; |
| |
| import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; |
| import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; |
| import static android.app.StatusBarManager.WindowVisibleState; |
| import static android.app.StatusBarManager.windowStateToString; |
| import static android.view.InsetsState.ITYPE_STATUS_BAR; |
| import static android.view.InsetsState.containsType; |
| import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; |
| import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; |
| import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS; |
| |
| import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO; |
| import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; |
| import static androidx.lifecycle.Lifecycle.State.RESUMED; |
| |
| import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; |
| import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL; |
| import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; |
| import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; |
| import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; |
| import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; |
| import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; |
| import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; |
| import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; |
| import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; |
| |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.app.ActivityOptions; |
| import android.app.ActivityTaskManager; |
| import android.app.IWallpaperManager; |
| import android.app.KeyguardManager; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.app.StatusBarManager; |
| import android.app.TaskInfo; |
| import android.app.TaskStackBuilder; |
| import android.app.UiModeManager; |
| import android.app.WallpaperInfo; |
| import android.app.WallpaperManager; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentCallbacks2; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.IPackageManager; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Point; |
| import android.graphics.PointF; |
| import android.hardware.devicestate.DeviceStateManager; |
| import android.metrics.LogMaker; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.PowerManager; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.Trace; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.service.dreams.IDreamManager; |
| import android.service.notification.StatusBarNotification; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import android.util.DisplayMetrics; |
| import android.util.EventLog; |
| import android.util.IndentingPrintWriter; |
| import android.util.Log; |
| import android.util.MathUtils; |
| import android.util.Slog; |
| import android.view.Display; |
| import android.view.IRemoteAnimationRunner; |
| import android.view.IWindowManager; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.ThreadedRenderer; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.WindowInsetsController.Appearance; |
| import android.view.WindowManager; |
| import android.view.WindowManagerGlobal; |
| import android.view.accessibility.AccessibilityManager; |
| import android.widget.DateTimeView; |
| |
| import androidx.annotation.NonNull; |
| import androidx.lifecycle.Lifecycle; |
| import androidx.lifecycle.LifecycleRegistry; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.colorextraction.ColorExtractor; |
| import com.android.internal.jank.InteractionJankMonitor; |
| import com.android.internal.logging.MetricsLogger; |
| import com.android.internal.logging.UiEvent; |
| import com.android.internal.logging.UiEventLogger; |
| import com.android.internal.logging.UiEventLoggerImpl; |
| import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
| import com.android.internal.statusbar.IStatusBarService; |
| import com.android.internal.statusbar.RegisterStatusBarResult; |
| import com.android.keyguard.KeyguardUpdateMonitor; |
| import com.android.keyguard.KeyguardUpdateMonitorCallback; |
| import com.android.keyguard.ViewMediatorCallback; |
| import com.android.systemui.ActivityIntentHelper; |
| import com.android.systemui.AutoReinflateContainer; |
| import com.android.systemui.CoreStartable; |
| import com.android.systemui.DejankUtils; |
| import com.android.systemui.EventLogTags; |
| import com.android.systemui.InitController; |
| import com.android.systemui.Prefs; |
| import com.android.systemui.R; |
| import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; |
| import com.android.systemui.animation.ActivityLaunchAnimator; |
| import com.android.systemui.animation.DelegateLaunchAnimatorController; |
| import com.android.systemui.assist.AssistManager; |
| import com.android.systemui.biometrics.AuthRippleController; |
| import com.android.systemui.broadcast.BroadcastDispatcher; |
| import com.android.systemui.camera.CameraIntents; |
| import com.android.systemui.charging.WiredChargingRippleController; |
| import com.android.systemui.charging.WirelessChargingAnimation; |
| import com.android.systemui.classifier.FalsingCollector; |
| import com.android.systemui.colorextraction.SysuiColorExtractor; |
| import com.android.systemui.dagger.SysUISingleton; |
| import com.android.systemui.dagger.qualifiers.Main; |
| import com.android.systemui.dagger.qualifiers.UiBackground; |
| import com.android.systemui.demomode.DemoMode; |
| import com.android.systemui.demomode.DemoModeController; |
| import com.android.systemui.emergency.EmergencyGesture; |
| import com.android.systemui.flags.FeatureFlags; |
| import com.android.systemui.flags.Flags; |
| import com.android.systemui.fragments.ExtensionFragmentListener; |
| import com.android.systemui.fragments.FragmentHostManager; |
| import com.android.systemui.fragments.FragmentService; |
| import com.android.systemui.keyguard.KeyguardService; |
| import com.android.systemui.keyguard.KeyguardUnlockAnimationController; |
| import com.android.systemui.keyguard.KeyguardViewMediator; |
| import com.android.systemui.keyguard.ScreenLifecycle; |
| import com.android.systemui.keyguard.WakefulnessLifecycle; |
| import com.android.systemui.navigationbar.NavigationBarController; |
| import com.android.systemui.navigationbar.NavigationBarView; |
| import com.android.systemui.plugins.DarkIconDispatcher; |
| import com.android.systemui.plugins.FalsingManager; |
| import com.android.systemui.plugins.OverlayPlugin; |
| import com.android.systemui.plugins.PluginDependencyProvider; |
| import com.android.systemui.plugins.PluginListener; |
| import com.android.systemui.plugins.qs.QS; |
| import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; |
| import com.android.systemui.plugins.statusbar.StatusBarStateController; |
| import com.android.systemui.qs.QSFragment; |
| import com.android.systemui.qs.QSPanelController; |
| import com.android.systemui.recents.ScreenPinningRequest; |
| import com.android.systemui.scrim.ScrimView; |
| import com.android.systemui.settings.brightness.BrightnessSliderController; |
| import com.android.systemui.shared.plugins.PluginManager; |
| import com.android.systemui.statusbar.AutoHideUiElement; |
| import com.android.systemui.statusbar.BackDropView; |
| import com.android.systemui.statusbar.CircleReveal; |
| import com.android.systemui.statusbar.CommandQueue; |
| import com.android.systemui.statusbar.GestureRecorder; |
| import com.android.systemui.statusbar.KeyboardShortcuts; |
| import com.android.systemui.statusbar.KeyguardIndicationController; |
| import com.android.systemui.statusbar.LiftReveal; |
| import com.android.systemui.statusbar.LightRevealScrim; |
| import com.android.systemui.statusbar.LockscreenShadeTransitionController; |
| import com.android.systemui.statusbar.NotificationLockscreenUserManager; |
| import com.android.systemui.statusbar.NotificationMediaManager; |
| import com.android.systemui.statusbar.NotificationPresenter; |
| import com.android.systemui.statusbar.NotificationRemoteInputManager; |
| import com.android.systemui.statusbar.NotificationShadeDepthController; |
| import com.android.systemui.statusbar.NotificationShadeWindowController; |
| import com.android.systemui.statusbar.NotificationShelfController; |
| import com.android.systemui.statusbar.NotificationViewHierarchyManager; |
| import com.android.systemui.statusbar.PowerButtonReveal; |
| import com.android.systemui.statusbar.PulseExpansionHandler; |
| import com.android.systemui.statusbar.StatusBarState; |
| import com.android.systemui.statusbar.SysuiStatusBarStateController; |
| import com.android.systemui.statusbar.core.StatusBarInitializer; |
| import com.android.systemui.statusbar.notification.DynamicPrivacyController; |
| import com.android.systemui.statusbar.notification.NotifPipelineFlags; |
| import com.android.systemui.statusbar.notification.NotificationActivityStarter; |
| import com.android.systemui.statusbar.notification.NotificationEntryManager; |
| import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; |
| import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; |
| import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; |
| import com.android.systemui.statusbar.notification.init.NotificationsController; |
| import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; |
| import com.android.systemui.statusbar.notification.logging.NotificationLogger; |
| import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; |
| import com.android.systemui.statusbar.notification.row.NotificationGutsManager; |
| import com.android.systemui.statusbar.notification.stack.NotificationListContainer; |
| import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; |
| import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; |
| import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; |
| import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; |
| import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; |
| import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent; |
| import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; |
| import com.android.systemui.statusbar.policy.BatteryController; |
| import com.android.systemui.statusbar.policy.BrightnessMirrorController; |
| import com.android.systemui.statusbar.policy.ConfigurationController; |
| import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; |
| import com.android.systemui.statusbar.policy.DeviceProvisionedController; |
| import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; |
| import com.android.systemui.statusbar.policy.ExtensionController; |
| import com.android.systemui.statusbar.policy.KeyguardStateController; |
| import com.android.systemui.statusbar.policy.UserInfoControllerImpl; |
| import com.android.systemui.statusbar.policy.UserSwitcherController; |
| import com.android.systemui.statusbar.window.StatusBarWindowController; |
| import com.android.systemui.statusbar.window.StatusBarWindowStateController; |
| import com.android.systemui.util.DumpUtilsKt; |
| import com.android.systemui.util.WallpaperController; |
| import com.android.systemui.util.concurrency.DelayableExecutor; |
| import com.android.systemui.util.concurrency.MessageRouter; |
| import com.android.systemui.volume.VolumeComponent; |
| import com.android.wm.shell.bubbles.Bubbles; |
| import com.android.wm.shell.startingsurface.SplashscreenContentDrawer; |
| import com.android.wm.shell.startingsurface.StartingSurface; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.concurrent.Executor; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| |
| import dagger.Lazy; |
| |
| /** |
| * A class handling initialization and coordination between some of the key central surfaces in |
| * System UI: The notification shade, the keyguard (lockscreen), and the status bar. |
| * |
| * This class is not our ideal architecture because it doesn't enforce much isolation between these |
| * three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would |
| * break it up into three modules -- one for each of those three surfaces -- and we would define any |
| * APIs that are needed for these surfaces to communicate with each other when necessary. |
| * |
| * <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is |
| * to break up this class into many small classes, and any code added here will slow down that goal. |
| * </b> |
| */ |
| @SysUISingleton |
| public class CentralSurfacesImpl extends CoreStartable implements |
| CentralSurfaces { |
| |
| private static final String BANNER_ACTION_CANCEL = |
| "com.android.systemui.statusbar.banner_action_cancel"; |
| private static final String BANNER_ACTION_SETUP = |
| "com.android.systemui.statusbar.banner_action_setup"; |
| |
| private static final int MSG_OPEN_SETTINGS_PANEL = 1002; |
| private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; |
| // 1020-1040 reserved for BaseStatusBar |
| |
| /** |
| * The delay to reset the hint text when the hint animation is finished running. |
| */ |
| private static final int HINT_RESET_DELAY_MS = 1200; |
| |
| private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); |
| |
| /** |
| * If true, the system is in the half-boot-to-decryption-screen state. |
| * Prudently disable QS and notifications. |
| */ |
| public static final boolean ONLY_CORE_APPS; |
| |
| static { |
| boolean onlyCoreApps; |
| try { |
| IPackageManager packageManager = |
| IPackageManager.Stub.asInterface(ServiceManager.getService("package")); |
| onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps(); |
| } catch (RemoteException e) { |
| onlyCoreApps = false; |
| } |
| ONLY_CORE_APPS = onlyCoreApps; |
| } |
| |
| private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; |
| private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; |
| private float mTransitionToFullShadeProgress = 0f; |
| private NotificationListContainer mNotifListContainer; |
| |
| private final KeyguardStateController.Callback mKeyguardStateControllerCallback = |
| new KeyguardStateController.Callback() { |
| @Override |
| public void onKeyguardShowingChanged() { |
| boolean occluded = mKeyguardStateController.isOccluded(); |
| mStatusBarHideIconsForBouncerManager.setIsOccludedAndTriggerUpdate(occluded); |
| mScrimController.setKeyguardOccluded(occluded); |
| } |
| }; |
| |
| void onStatusBarWindowStateChanged(@WindowVisibleState int state) { |
| updateBubblesVisibility(); |
| mStatusBarWindowState = state; |
| } |
| |
| @Override |
| public void acquireGestureWakeLock(long time) { |
| mGestureWakeLock.acquire(time); |
| } |
| |
| @Override |
| public boolean setAppearance(int appearance) { |
| if (mAppearance != appearance) { |
| mAppearance = appearance; |
| return updateBarMode(barMode(isTransientShown(), appearance)); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int getBarMode() { |
| return mStatusBarMode; |
| } |
| |
| @Override |
| public void resendMessage(int msg) { |
| mMessageRouter.cancelMessages(msg); |
| mMessageRouter.sendMessage(msg); |
| } |
| |
| @Override |
| public void resendMessage(Object msg) { |
| mMessageRouter.cancelMessages(msg.getClass()); |
| mMessageRouter.sendMessage(msg); |
| } |
| |
| @Override |
| public int getDisabled1() { |
| return mDisabled1; |
| } |
| |
| @Override |
| public void setDisabled1(int disabled) { |
| mDisabled1 = disabled; |
| } |
| |
| @Override |
| public int getDisabled2() { |
| return mDisabled2; |
| } |
| |
| @Override |
| public void setDisabled2(int disabled) { |
| mDisabled2 = disabled; |
| } |
| |
| @Override |
| public void setLastCameraLaunchSource(int source) { |
| mLastCameraLaunchSource = source; |
| } |
| |
| @Override |
| public void setLaunchCameraOnFinishedGoingToSleep(boolean launch) { |
| mLaunchCameraOnFinishedGoingToSleep = launch; |
| } |
| |
| @Override |
| public void setLaunchCameraOnFinishedWaking(boolean launch) { |
| mLaunchCameraWhenFinishedWaking = launch; |
| } |
| |
| @Override |
| public void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) { |
| mLaunchEmergencyActionOnFinishedGoingToSleep = launch; |
| } |
| |
| @Override |
| public void setLaunchEmergencyActionOnFinishedWaking(boolean launch) { |
| mLaunchEmergencyActionWhenFinishedWaking = launch; |
| } |
| |
| @Override |
| public QSPanelController getQSPanelController() { |
| return mQSPanelController; |
| } |
| |
| /** */ |
| @Override |
| public void animateExpandNotificationsPanel() { |
| mCommandQueueCallbacks.animateExpandNotificationsPanel(); |
| } |
| |
| /** */ |
| @Override |
| public void animateExpandSettingsPanel(@Nullable String subpanel) { |
| mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel); |
| } |
| |
| /** */ |
| @Override |
| public void animateCollapsePanels(int flags, boolean force) { |
| mCommandQueueCallbacks.animateCollapsePanels(flags, force); |
| } |
| |
| /** */ |
| @Override |
| public void togglePanel() { |
| mCommandQueueCallbacks.togglePanel(); |
| } |
| /** |
| * The {@link StatusBarState} of the status bar. |
| */ |
| protected int mState; // TODO: remove this. Just use StatusBarStateController |
| protected boolean mBouncerShowing; |
| private boolean mBouncerShowingOverDream; |
| |
| private final PhoneStatusBarPolicy mIconPolicy; |
| |
| private final VolumeComponent mVolumeComponent; |
| private BrightnessMirrorController mBrightnessMirrorController; |
| private boolean mBrightnessMirrorVisible; |
| private BiometricUnlockController mBiometricUnlockController; |
| private final LightBarController mLightBarController; |
| private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; |
| @Nullable |
| protected LockscreenWallpaper mLockscreenWallpaper; |
| private final AutoHideController mAutoHideController; |
| |
| private final Point mCurrentDisplaySize = new Point(); |
| |
| protected NotificationShadeWindowView mNotificationShadeWindowView; |
| protected PhoneStatusBarView mStatusBarView; |
| private PhoneStatusBarViewController mPhoneStatusBarViewController; |
| private PhoneStatusBarTransitions mStatusBarTransitions; |
| private AuthRippleController mAuthRippleController; |
| @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING; |
| protected final NotificationShadeWindowController mNotificationShadeWindowController; |
| private final StatusBarWindowController mStatusBarWindowController; |
| private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; |
| @VisibleForTesting |
| DozeServiceHost mDozeServiceHost; |
| private boolean mWakeUpComingFromTouch; |
| private PointF mWakeUpTouchLocation; |
| private LightRevealScrim mLightRevealScrim; |
| private PowerButtonReveal mPowerButtonReveal; |
| |
| private final Object mQueueLock = new Object(); |
| |
| private final PulseExpansionHandler mPulseExpansionHandler; |
| private final NotificationWakeUpCoordinator mWakeUpCoordinator; |
| private final KeyguardBypassController mKeyguardBypassController; |
| private final KeyguardStateController mKeyguardStateController; |
| private final HeadsUpManagerPhone mHeadsUpManager; |
| private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; |
| private final DynamicPrivacyController mDynamicPrivacyController; |
| private final FalsingCollector mFalsingCollector; |
| private final FalsingManager mFalsingManager; |
| private final BroadcastDispatcher mBroadcastDispatcher; |
| private final ConfigurationController mConfigurationController; |
| protected NotificationShadeWindowViewController mNotificationShadeWindowViewController; |
| private final DozeParameters mDozeParameters; |
| private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; |
| private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory; |
| private final PluginManager mPluginManager; |
| private final ShadeController mShadeController; |
| private final InitController mInitController; |
| |
| private final PluginDependencyProvider mPluginDependencyProvider; |
| private final KeyguardDismissUtil mKeyguardDismissUtil; |
| private final ExtensionController mExtensionController; |
| private final UserInfoControllerImpl mUserInfoControllerImpl; |
| private final DemoModeController mDemoModeController; |
| private final NotificationsController mNotificationsController; |
| private final OngoingCallController mOngoingCallController; |
| private final StatusBarSignalPolicy mStatusBarSignalPolicy; |
| private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; |
| |
| // expanded notifications |
| // the sliding/resizing panel within the notification window |
| protected NotificationPanelViewController mNotificationPanelViewController; |
| |
| // settings |
| private QSPanelController mQSPanelController; |
| |
| KeyguardIndicationController mKeyguardIndicationController; |
| |
| private View mReportRejectedTouch; |
| |
| private boolean mExpandedVisible; |
| |
| protected final NotificationEntryManager mEntryManager; |
| private final NotificationGutsManager mGutsManager; |
| private final NotificationLogger mNotificationLogger; |
| private final NotificationViewHierarchyManager mViewHierarchyManager; |
| private final PanelExpansionStateManager mPanelExpansionStateManager; |
| private final KeyguardViewMediator mKeyguardViewMediator; |
| protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; |
| private final BrightnessSliderController.Factory mBrightnessSliderFactory; |
| private final FeatureFlags mFeatureFlags; |
| private final FragmentService mFragmentService; |
| private final ScreenOffAnimationController mScreenOffAnimationController; |
| private final WallpaperController mWallpaperController; |
| private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; |
| private final MessageRouter mMessageRouter; |
| private final WallpaperManager mWallpaperManager; |
| |
| private CentralSurfacesComponent mCentralSurfacesComponent; |
| |
| // Flags for disabling the status bar |
| // Two variables becaseu the first one evidently ran out of room for new flags. |
| private int mDisabled1 = 0; |
| private int mDisabled2 = 0; |
| |
| /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ |
| private @Appearance int mAppearance; |
| |
| private boolean mTransientShown; |
| |
| private final DisplayMetrics mDisplayMetrics; |
| |
| // XXX: gesture research |
| private final GestureRecorder mGestureRec = DEBUG_GESTURES |
| ? new GestureRecorder("/sdcard/statusbar_gestures.dat") |
| : null; |
| |
| private final ScreenPinningRequest mScreenPinningRequest; |
| |
| private final MetricsLogger mMetricsLogger; |
| |
| // ensure quick settings is disabled until the current user makes it through the setup wizard |
| @VisibleForTesting |
| protected boolean mUserSetup = false; |
| |
| @VisibleForTesting |
| public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum { |
| @UiEvent(doc = "Secured lockscreen is opened.") |
| LOCKSCREEN_OPEN_SECURE(405), |
| |
| @UiEvent(doc = "Lockscreen without security is opened.") |
| LOCKSCREEN_OPEN_INSECURE(406), |
| |
| @UiEvent(doc = "Secured lockscreen is closed.") |
| LOCKSCREEN_CLOSE_SECURE(407), |
| |
| @UiEvent(doc = "Lockscreen without security is closed.") |
| LOCKSCREEN_CLOSE_INSECURE(408), |
| |
| @UiEvent(doc = "Secured bouncer is opened.") |
| BOUNCER_OPEN_SECURE(409), |
| |
| @UiEvent(doc = "Bouncer without security is opened.") |
| BOUNCER_OPEN_INSECURE(410), |
| |
| @UiEvent(doc = "Secured bouncer is closed.") |
| BOUNCER_CLOSE_SECURE(411), |
| |
| @UiEvent(doc = "Bouncer without security is closed.") |
| BOUNCER_CLOSE_INSECURE(412); |
| |
| private final int mId; |
| |
| StatusBarUiEvent(int id) { |
| mId = id; |
| } |
| |
| @Override |
| public int getId() { |
| return mId; |
| } |
| } |
| |
| private final DelayableExecutor mMainExecutor; |
| |
| private int mInteractingWindows; |
| private @TransitionMode int mStatusBarMode; |
| |
| private final ViewMediatorCallback mKeyguardViewMediatorCallback; |
| private final ScrimController mScrimController; |
| protected DozeScrimController mDozeScrimController; |
| private final Executor mUiBgExecutor; |
| |
| protected boolean mDozing; |
| private boolean mIsFullscreen; |
| |
| boolean mCloseQsBeforeScreenOff; |
| |
| private final NotificationMediaManager mMediaManager; |
| private final NotificationLockscreenUserManager mLockscreenUserManager; |
| private final NotificationRemoteInputManager mRemoteInputManager; |
| private boolean mWallpaperSupported; |
| |
| private Runnable mLaunchTransitionEndRunnable; |
| private Runnable mLaunchTransitionCancelRunnable; |
| private boolean mLaunchCameraWhenFinishedWaking; |
| private boolean mLaunchCameraOnFinishedGoingToSleep; |
| private boolean mLaunchEmergencyActionWhenFinishedWaking; |
| private boolean mLaunchEmergencyActionOnFinishedGoingToSleep; |
| private int mLastCameraLaunchSource; |
| protected PowerManager.WakeLock mGestureWakeLock; |
| |
| private final int[] mTmpInt2 = new int[2]; |
| |
| // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. |
| private int mLastLoggedStateFingerprint; |
| private boolean mIsLaunchingActivityOverLockscreen; |
| |
| private final UserSwitcherController mUserSwitcherController; |
| private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); |
| protected final BatteryController mBatteryController; |
| protected boolean mPanelExpanded; |
| private UiModeManager mUiModeManager; |
| private LogMaker mStatusBarStateLog; |
| protected final NotificationIconAreaController mNotificationIconAreaController; |
| @Nullable private View mAmbientIndicationContainer; |
| private final SysuiColorExtractor mColorExtractor; |
| private final ScreenLifecycle mScreenLifecycle; |
| private final WakefulnessLifecycle mWakefulnessLifecycle; |
| |
| private boolean mNoAnimationOnNextBarModeChange; |
| private final SysuiStatusBarStateController mStatusBarStateController; |
| |
| private final ActivityLaunchAnimator mActivityLaunchAnimator; |
| private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; |
| protected NotificationPresenter mPresenter; |
| private NotificationActivityStarter mNotificationActivityStarter; |
| private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; |
| private final Optional<Bubbles> mBubblesOptional; |
| private final Bubbles.BubbleExpandListener mBubbleExpandListener; |
| private final Optional<StartingSurface> mStartingSurfaceOptional; |
| private final NotifPipelineFlags mNotifPipelineFlags; |
| |
| private final ActivityIntentHelper mActivityIntentHelper; |
| private NotificationStackScrollLayoutController mStackScrollerController; |
| |
| private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener = |
| (extractor, which) -> updateTheme(); |
| |
| private final InteractionJankMonitor mJankMonitor; |
| |
| |
| /** |
| * Public constructor for CentralSurfaces. |
| * |
| * CentralSurfaces is considered optional, and therefore can not be marked as @Inject directly. |
| * Instead, an @Provide method is included. See {@link StatusBarPhoneModule}. |
| */ |
| @SuppressWarnings("OptionalUsedAsFieldOrParameterType") |
| @Inject |
| public CentralSurfacesImpl( |
| Context context, |
| NotificationsController notificationsController, |
| FragmentService fragmentService, |
| LightBarController lightBarController, |
| AutoHideController autoHideController, |
| StatusBarWindowController statusBarWindowController, |
| StatusBarWindowStateController statusBarWindowStateController, |
| KeyguardUpdateMonitor keyguardUpdateMonitor, |
| StatusBarSignalPolicy statusBarSignalPolicy, |
| PulseExpansionHandler pulseExpansionHandler, |
| NotificationWakeUpCoordinator notificationWakeUpCoordinator, |
| KeyguardBypassController keyguardBypassController, |
| KeyguardStateController keyguardStateController, |
| HeadsUpManagerPhone headsUpManagerPhone, |
| DynamicPrivacyController dynamicPrivacyController, |
| FalsingManager falsingManager, |
| FalsingCollector falsingCollector, |
| BroadcastDispatcher broadcastDispatcher, |
| NotificationEntryManager notificationEntryManager, |
| NotificationGutsManager notificationGutsManager, |
| NotificationLogger notificationLogger, |
| NotificationInterruptStateProvider notificationInterruptStateProvider, |
| NotificationViewHierarchyManager notificationViewHierarchyManager, |
| PanelExpansionStateManager panelExpansionStateManager, |
| KeyguardViewMediator keyguardViewMediator, |
| DisplayMetrics displayMetrics, |
| MetricsLogger metricsLogger, |
| @UiBackground Executor uiBgExecutor, |
| NotificationMediaManager notificationMediaManager, |
| NotificationLockscreenUserManager lockScreenUserManager, |
| NotificationRemoteInputManager remoteInputManager, |
| UserSwitcherController userSwitcherController, |
| BatteryController batteryController, |
| SysuiColorExtractor colorExtractor, |
| ScreenLifecycle screenLifecycle, |
| WakefulnessLifecycle wakefulnessLifecycle, |
| SysuiStatusBarStateController statusBarStateController, |
| Optional<Bubbles> bubblesOptional, |
| VisualStabilityManager visualStabilityManager, |
| DeviceProvisionedController deviceProvisionedController, |
| NavigationBarController navigationBarController, |
| AccessibilityFloatingMenuController accessibilityFloatingMenuController, |
| Lazy<AssistManager> assistManagerLazy, |
| ConfigurationController configurationController, |
| NotificationShadeWindowController notificationShadeWindowController, |
| DozeParameters dozeParameters, |
| ScrimController scrimController, |
| Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, |
| Lazy<BiometricUnlockController> biometricUnlockControllerLazy, |
| DozeServiceHost dozeServiceHost, |
| PowerManager powerManager, |
| ScreenPinningRequest screenPinningRequest, |
| DozeScrimController dozeScrimController, |
| VolumeComponent volumeComponent, |
| CommandQueue commandQueue, |
| CentralSurfacesComponent.Factory centralSurfacesComponentFactory, |
| PluginManager pluginManager, |
| ShadeController shadeController, |
| StatusBarKeyguardViewManager statusBarKeyguardViewManager, |
| ViewMediatorCallback viewMediatorCallback, |
| InitController initController, |
| @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler, |
| PluginDependencyProvider pluginDependencyProvider, |
| KeyguardDismissUtil keyguardDismissUtil, |
| ExtensionController extensionController, |
| UserInfoControllerImpl userInfoControllerImpl, |
| PhoneStatusBarPolicy phoneStatusBarPolicy, |
| KeyguardIndicationController keyguardIndicationController, |
| DemoModeController demoModeController, |
| Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, |
| StatusBarTouchableRegionManager statusBarTouchableRegionManager, |
| NotificationIconAreaController notificationIconAreaController, |
| BrightnessSliderController.Factory brightnessSliderFactory, |
| ScreenOffAnimationController screenOffAnimationController, |
| WallpaperController wallpaperController, |
| OngoingCallController ongoingCallController, |
| StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, |
| LockscreenShadeTransitionController lockscreenShadeTransitionController, |
| FeatureFlags featureFlags, |
| KeyguardUnlockAnimationController keyguardUnlockAnimationController, |
| @Main DelayableExecutor delayableExecutor, |
| @Main MessageRouter messageRouter, |
| WallpaperManager wallpaperManager, |
| Optional<StartingSurface> startingSurfaceOptional, |
| ActivityLaunchAnimator activityLaunchAnimator, |
| NotifPipelineFlags notifPipelineFlags, |
| InteractionJankMonitor jankMonitor, |
| DeviceStateManager deviceStateManager, |
| WiredChargingRippleController wiredChargingRippleController, |
| IDreamManager dreamManager) { |
| super(context); |
| mNotificationsController = notificationsController; |
| mFragmentService = fragmentService; |
| mLightBarController = lightBarController; |
| mAutoHideController = autoHideController; |
| mStatusBarWindowController = statusBarWindowController; |
| mKeyguardUpdateMonitor = keyguardUpdateMonitor; |
| mPulseExpansionHandler = pulseExpansionHandler; |
| mWakeUpCoordinator = notificationWakeUpCoordinator; |
| mKeyguardBypassController = keyguardBypassController; |
| mKeyguardStateController = keyguardStateController; |
| mHeadsUpManager = headsUpManagerPhone; |
| mKeyguardIndicationController = keyguardIndicationController; |
| mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; |
| mDynamicPrivacyController = dynamicPrivacyController; |
| mFalsingCollector = falsingCollector; |
| mFalsingManager = falsingManager; |
| mBroadcastDispatcher = broadcastDispatcher; |
| mEntryManager = notificationEntryManager; |
| mGutsManager = notificationGutsManager; |
| mNotificationLogger = notificationLogger; |
| mNotificationInterruptStateProvider = notificationInterruptStateProvider; |
| mViewHierarchyManager = notificationViewHierarchyManager; |
| mPanelExpansionStateManager = panelExpansionStateManager; |
| mKeyguardViewMediator = keyguardViewMediator; |
| mDisplayMetrics = displayMetrics; |
| mMetricsLogger = metricsLogger; |
| mUiBgExecutor = uiBgExecutor; |
| mMediaManager = notificationMediaManager; |
| mLockscreenUserManager = lockScreenUserManager; |
| mRemoteInputManager = remoteInputManager; |
| mUserSwitcherController = userSwitcherController; |
| mBatteryController = batteryController; |
| mColorExtractor = colorExtractor; |
| mScreenLifecycle = screenLifecycle; |
| mWakefulnessLifecycle = wakefulnessLifecycle; |
| mStatusBarStateController = statusBarStateController; |
| mBubblesOptional = bubblesOptional; |
| mVisualStabilityManager = visualStabilityManager; |
| mDeviceProvisionedController = deviceProvisionedController; |
| mNavigationBarController = navigationBarController; |
| mAccessibilityFloatingMenuController = accessibilityFloatingMenuController; |
| mAssistManagerLazy = assistManagerLazy; |
| mConfigurationController = configurationController; |
| mNotificationShadeWindowController = notificationShadeWindowController; |
| mDozeServiceHost = dozeServiceHost; |
| mPowerManager = powerManager; |
| mDozeParameters = dozeParameters; |
| mScrimController = scrimController; |
| mLockscreenWallpaperLazy = lockscreenWallpaperLazy; |
| mScreenPinningRequest = screenPinningRequest; |
| mDozeScrimController = dozeScrimController; |
| mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; |
| mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy; |
| mVolumeComponent = volumeComponent; |
| mCommandQueue = commandQueue; |
| mCentralSurfacesComponentFactory = centralSurfacesComponentFactory; |
| mPluginManager = pluginManager; |
| mShadeController = shadeController; |
| mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; |
| mKeyguardViewMediatorCallback = viewMediatorCallback; |
| mInitController = initController; |
| mPluginDependencyProvider = pluginDependencyProvider; |
| mKeyguardDismissUtil = keyguardDismissUtil; |
| mExtensionController = extensionController; |
| mUserInfoControllerImpl = userInfoControllerImpl; |
| mIconPolicy = phoneStatusBarPolicy; |
| mDemoModeController = demoModeController; |
| mNotificationIconAreaController = notificationIconAreaController; |
| mBrightnessSliderFactory = brightnessSliderFactory; |
| mWallpaperController = wallpaperController; |
| mOngoingCallController = ongoingCallController; |
| mStatusBarSignalPolicy = statusBarSignalPolicy; |
| mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; |
| mFeatureFlags = featureFlags; |
| mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; |
| mMainExecutor = delayableExecutor; |
| mMessageRouter = messageRouter; |
| mWallpaperManager = wallpaperManager; |
| mJankMonitor = jankMonitor; |
| |
| mLockscreenShadeTransitionController = lockscreenShadeTransitionController; |
| mStartingSurfaceOptional = startingSurfaceOptional; |
| mNotifPipelineFlags = notifPipelineFlags; |
| mDreamManager = dreamManager; |
| lockscreenShadeTransitionController.setCentralSurfaces(this); |
| statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); |
| |
| mScreenOffAnimationController = screenOffAnimationController; |
| |
| mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged); |
| |
| mBubbleExpandListener = |
| (isExpanding, key) -> mContext.getMainExecutor().execute(() -> { |
| mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged"); |
| updateScrimController(); |
| }); |
| |
| mActivityIntentHelper = new ActivityIntentHelper(mContext); |
| mActivityLaunchAnimator = activityLaunchAnimator; |
| |
| // The status bar background may need updating when the ongoing call status changes. |
| mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode()); |
| |
| // TODO(b/190746471): Find a better home for this. |
| DateTimeView.setReceiverHandler(timeTickHandler); |
| |
| mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class, |
| data -> toggleKeyboardShortcuts(data.mDeviceId)); |
| mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU, |
| id -> dismissKeyboardShortcuts()); |
| mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class, |
| data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel)); |
| mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT, |
| id -> onLaunchTransitionTimeout()); |
| |
| deviceStateManager.registerCallback(mMainExecutor, |
| new FoldStateListener(mContext, this::onFoldedStateChanged)); |
| wiredChargingRippleController.registerCallbacks(); |
| } |
| |
| @Override |
| public void start() { |
| mScreenLifecycle.addObserver(mScreenObserver); |
| mWakefulnessLifecycle.addObserver(mWakefulnessObserver); |
| mUiModeManager = mContext.getSystemService(UiModeManager.class); |
| if (mBubblesOptional.isPresent()) { |
| mBubblesOptional.get().setExpandListener(mBubbleExpandListener); |
| } |
| |
| mStatusBarSignalPolicy.init(); |
| mKeyguardIndicationController.init(); |
| |
| mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); |
| mStatusBarStateController.addCallback(mStateListener, |
| SysuiStatusBarStateController.RANK_STATUS_BAR); |
| |
| mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); |
| |
| mDisplay = mContext.getDisplay(); |
| mDisplayId = mDisplay.getDisplayId(); |
| updateDisplaySize(); |
| mStatusBarHideIconsForBouncerManager.setDisplayId(mDisplayId); |
| |
| // start old BaseStatusBar.start(). |
| mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); |
| mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( |
| Context.DEVICE_POLICY_SERVICE); |
| |
| mAccessibilityManager = (AccessibilityManager) |
| mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); |
| |
| mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController); |
| mBarService = IStatusBarService.Stub.asInterface( |
| ServiceManager.getService(Context.STATUS_BAR_SERVICE)); |
| |
| mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); |
| mWallpaperSupported = mWallpaperManager.isWallpaperSupported(); |
| |
| RegisterStatusBarResult result = null; |
| try { |
| result = mBarService.registerStatusBar(mCommandQueue); |
| } catch (RemoteException ex) { |
| ex.rethrowFromSystemServer(); |
| } |
| |
| createAndAddWindows(result); |
| |
| if (mWallpaperSupported) { |
| // Make sure we always have the most current wallpaper info. |
| IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); |
| mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter, |
| null /* handler */, UserHandle.ALL); |
| mWallpaperChangedReceiver.onReceive(mContext, null); |
| } else if (DEBUG) { |
| Log.v(TAG, "start(): no wallpaper service "); |
| } |
| |
| // Set up the initial notification state. This needs to happen before CommandQueue.disable() |
| setUpPresenter(); |
| |
| if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) { |
| showTransientUnchecked(); |
| } |
| mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance, |
| result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior, |
| result.mRequestedVisibilities, result.mPackageName, result.mLetterboxDetails); |
| |
| // StatusBarManagerService has a back up of IME token and it's restored here. |
| mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken, |
| result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); |
| |
| // Set up the initial icon state |
| int numIcons = result.mIcons.size(); |
| for (int i = 0; i < numIcons; i++) { |
| mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i)); |
| } |
| |
| if (DEBUG) { |
| Log.d(TAG, String.format( |
| "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x", |
| numIcons, |
| result.mDisabledFlags1, |
| result.mAppearance, |
| result.mImeWindowVis)); |
| } |
| |
| IntentFilter internalFilter = new IntentFilter(); |
| internalFilter.addAction(BANNER_ACTION_CANCEL); |
| internalFilter.addAction(BANNER_ACTION_SETUP); |
| mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF, |
| null, Context.RECEIVER_EXPORTED_UNAUDITED); |
| |
| if (mWallpaperSupported) { |
| IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface( |
| ServiceManager.getService(Context.WALLPAPER_SERVICE)); |
| try { |
| wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */); |
| } catch (RemoteException e) { |
| // Just pass, nothing critical. |
| } |
| } |
| |
| // end old BaseStatusBar.start(). |
| |
| // Lastly, call to the icon policy to install/update all the icons. |
| mIconPolicy.init(); |
| |
| mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { |
| @Override |
| public void onUnlockedChanged() { |
| logStateToEventlog(); |
| } |
| |
| @Override |
| public void onKeyguardGoingAwayChanged() { |
| // The light reveal scrim should always be fully revealed by the time the keyguard |
| // is done going away. Double check that this is true. |
| if (!mKeyguardStateController.isKeyguardGoingAway()) { |
| if (mLightRevealScrim.getRevealAmount() != 1f) { |
| Log.e(TAG, "Keyguard is done going away, but someone left the light reveal " |
| + "scrim at reveal amount: " + mLightRevealScrim.getRevealAmount()); |
| } |
| |
| // If the auth ripple is still playing, let it finish. |
| if (!mAuthRippleController.isAnimatingLightRevealScrim()) { |
| mLightRevealScrim.setRevealAmount(1f); |
| } |
| } |
| } |
| }); |
| startKeyguard(); |
| |
| mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); |
| mDozeServiceHost.initialize( |
| this, |
| mStatusBarKeyguardViewManager, |
| mNotificationShadeWindowViewController, |
| mNotificationPanelViewController, |
| mAmbientIndicationContainer); |
| updateLightRevealScrimVisibility(); |
| |
| mConfigurationController.addCallback(mConfigurationListener); |
| |
| mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback); |
| mLifecycle.setCurrentState(RESUMED); |
| |
| mAccessibilityFloatingMenuController.init(); |
| |
| // set the initial view visibility |
| int disabledFlags1 = result.mDisabledFlags1; |
| int disabledFlags2 = result.mDisabledFlags2; |
| mInitController.addPostInitTask( |
| () -> setUpDisableFlags(disabledFlags1, disabledFlags2)); |
| |
| mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener); |
| |
| mPluginManager.addPluginListener( |
| new PluginListener<OverlayPlugin>() { |
| private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>(); |
| |
| @Override |
| public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { |
| mMainExecutor.execute( |
| () -> plugin.setup(getNotificationShadeWindowView(), |
| getNavigationBarView(), |
| new Callback(plugin), mDozeParameters)); |
| } |
| |
| @Override |
| public void onPluginDisconnected(OverlayPlugin plugin) { |
| mMainExecutor.execute(() -> { |
| mOverlays.remove(plugin); |
| mNotificationShadeWindowController |
| .setForcePluginOpen(mOverlays.size() != 0, this); |
| }); |
| } |
| |
| class Callback implements OverlayPlugin.Callback { |
| private final OverlayPlugin mPlugin; |
| |
| Callback(OverlayPlugin plugin) { |
| mPlugin = plugin; |
| } |
| |
| @Override |
| public void onHoldStatusBarOpenChange() { |
| if (mPlugin.holdStatusBarOpen()) { |
| mOverlays.add(mPlugin); |
| } else { |
| mOverlays.remove(mPlugin); |
| } |
| mMainExecutor.execute(() -> { |
| mNotificationShadeWindowController |
| .setStateListener(b -> mOverlays.forEach( |
| o -> o.setCollapseDesired(b))); |
| mNotificationShadeWindowController |
| .setForcePluginOpen(mOverlays.size() != 0, this); |
| }); |
| } |
| } |
| }, OverlayPlugin.class, true /* Allow multiple plugins */); |
| |
| mStartingSurfaceOptional.ifPresent(startingSurface -> startingSurface.setSysuiProxy( |
| (requestTopUi, componentTag) -> mMainExecutor.execute(() -> |
| mNotificationShadeWindowController.setRequestTopUi( |
| requestTopUi, componentTag)))); |
| } |
| |
| private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) { |
| Trace.beginSection("CentralSurfaces#onFoldedStateChanged"); |
| onFoldedStateChangedInternal(isFolded, willGoToSleep); |
| Trace.endSection(); |
| } |
| |
| private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) { |
| // Folded state changes are followed by a screen off event. |
| // By default turning off the screen also closes the shade. |
| // We want to make sure that the shade status is kept after |
| // folding/unfolding. |
| boolean isShadeOpen = mShadeController.isShadeOpen(); |
| boolean leaveOpen = isShadeOpen && !willGoToSleep; |
| if (DEBUG) { |
| Log.d(TAG, String.format( |
| "#onFoldedStateChanged(): " |
| + "isFolded=%s, " |
| + "willGoToSleep=%s, " |
| + "isShadeOpen=%s, " |
| + "leaveOpen=%s", |
| isFolded, willGoToSleep, isShadeOpen, leaveOpen)); |
| } |
| if (leaveOpen) { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); |
| if (mKeyguardStateController.isShowing()) { |
| // When device state changes on keyguard we don't want to keep the state of |
| // the shade and instead we open clean state of keyguard with shade closed. |
| // Normally some parts of QS state (like expanded/collapsed) are persisted and |
| // that causes incorrect UI rendering, especially when changing state with QS |
| // expanded. To prevent that we can close QS which resets QS and some parts of |
| // the shade to its default state. Read more in b/201537421 |
| mCloseQsBeforeScreenOff = true; |
| } |
| } |
| } |
| |
| // ================================================================================ |
| // Constructing the view |
| // ================================================================================ |
| protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { |
| updateDisplaySize(); // populates mDisplayMetrics |
| updateResources(); |
| updateTheme(); |
| |
| inflateStatusBarWindow(); |
| mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener()); |
| mWallpaperController.setRootView(mNotificationShadeWindowView); |
| |
| // TODO: Deal with the ugliness that comes from having some of the status bar broken out |
| // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. |
| mNotificationLogger.setUpWithContainer(mNotifListContainer); |
| mNotificationIconAreaController.setupShelf(mNotificationShelfController); |
| mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator); |
| mUserSwitcherController.init(mNotificationShadeWindowView); |
| |
| // Allow plugins to reference DarkIconDispatcher and StatusBarStateController |
| mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class); |
| mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class); |
| |
| // Set up CollapsedStatusBarFragment and PhoneStatusBarView |
| StatusBarInitializer initializer = mCentralSurfacesComponent.getStatusBarInitializer(); |
| initializer.setStatusBarViewUpdatedListener( |
| (statusBarView, statusBarViewController, statusBarTransitions) -> { |
| mStatusBarView = statusBarView; |
| mPhoneStatusBarViewController = statusBarViewController; |
| mStatusBarTransitions = statusBarTransitions; |
| mNotificationShadeWindowViewController |
| .setStatusBarViewController(mPhoneStatusBarViewController); |
| // Ensure we re-propagate panel expansion values to the panel controller and |
| // any listeners it may have, such as PanelBar. This will also ensure we |
| // re-display the notification panel if necessary (for example, if |
| // a heads-up notification was being displayed and should continue being |
| // displayed). |
| mNotificationPanelViewController.updatePanelExpansionAndVisibility(); |
| setBouncerShowingForStatusBarComponents(mBouncerShowing); |
| checkBarModes(); |
| }); |
| initializer.initializeStatusBar(mCentralSurfacesComponent); |
| |
| mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView); |
| mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener()); |
| if (!mNotifPipelineFlags.isNewPipelineEnabled()) { |
| mHeadsUpManager.addListener(mVisualStabilityManager); |
| } |
| mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); |
| |
| createNavigationBar(result); |
| |
| if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) { |
| mLockscreenWallpaper = mLockscreenWallpaperLazy.get(); |
| } |
| |
| mNotificationPanelViewController.setKeyguardIndicationController( |
| mKeyguardIndicationController); |
| |
| mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById( |
| R.id.ambient_indication_container); |
| |
| mAutoHideController.setStatusBar(new AutoHideUiElement() { |
| @Override |
| public void synchronizeState() { |
| checkBarModes(); |
| } |
| |
| @Override |
| public boolean shouldHideOnTouch() { |
| return !mRemoteInputManager.isRemoteInputActive(); |
| } |
| |
| @Override |
| public boolean isVisible() { |
| return isTransientShown(); |
| } |
| |
| @Override |
| public void hide() { |
| clearTransient(); |
| } |
| }); |
| |
| ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind); |
| ScrimView notificationsScrim = mNotificationShadeWindowView |
| .findViewById(R.id.scrim_notifications); |
| ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front); |
| |
| mScrimController.setScrimVisibleListener(scrimsVisible -> { |
| mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible); |
| }); |
| mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront); |
| |
| mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim); |
| mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> { |
| Runnable updateOpaqueness = () -> { |
| mNotificationShadeWindowController.setLightRevealScrimOpaque( |
| mLightRevealScrim.isScrimOpaque()); |
| mScreenOffAnimationController |
| .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque()); |
| }; |
| if (opaque) { |
| // Delay making the view opaque for a frame, because it needs some time to render |
| // otherwise this can lead to a flicker where the scrim doesn't cover the screen |
| mLightRevealScrim.post(updateOpaqueness); |
| } else { |
| updateOpaqueness.run(); |
| } |
| }); |
| |
| mScreenOffAnimationController.initialize(this, mLightRevealScrim); |
| updateLightRevealScrimVisibility(); |
| |
| mNotificationPanelViewController.initDependencies( |
| this, |
| this::makeExpandedInvisible, |
| mNotificationShelfController); |
| |
| BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop); |
| mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), |
| backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper); |
| float maxWallpaperZoom = mContext.getResources().getFloat( |
| com.android.internal.R.dimen.config_wallpaperMaxScale); |
| mNotificationShadeDepthControllerLazy.get().addListener(depth -> { |
| float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth); |
| backdrop.setPivotX(backdrop.getWidth() / 2f); |
| backdrop.setPivotY(backdrop.getHeight() / 2f); |
| backdrop.setScaleX(scale); |
| backdrop.setScaleY(scale); |
| }); |
| |
| // Set up the quick settings tile panel |
| final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame); |
| if (container != null) { |
| FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); |
| ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, |
| mExtensionController |
| .newExtension(QS.class) |
| .withPlugin(QS.class) |
| .withDefault(this::createDefaultQSFragment) |
| .build()); |
| mBrightnessMirrorController = new BrightnessMirrorController( |
| mNotificationShadeWindowView, |
| mNotificationPanelViewController, |
| mNotificationShadeDepthControllerLazy.get(), |
| mBrightnessSliderFactory, |
| (visible) -> { |
| mBrightnessMirrorVisible = visible; |
| updateScrimController(); |
| }); |
| fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { |
| QS qs = (QS) f; |
| if (qs instanceof QSFragment) { |
| mQSPanelController = ((QSFragment) qs).getQSPanelController(); |
| ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController); |
| } |
| }); |
| } |
| |
| mReportRejectedTouch = mNotificationShadeWindowView |
| .findViewById(R.id.report_rejected_touch); |
| if (mReportRejectedTouch != null) { |
| updateReportRejectedTouchVisibility(); |
| mReportRejectedTouch.setOnClickListener(v -> { |
| Uri session = mFalsingManager.reportRejectedTouch(); |
| if (session == null) { return; } |
| |
| StringWriter message = new StringWriter(); |
| message.write("Build info: "); |
| message.write(SystemProperties.get("ro.build.description")); |
| message.write("\nSerial number: "); |
| message.write(SystemProperties.get("ro.serialno")); |
| message.write("\n"); |
| |
| startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND) |
| .setType("*/*") |
| .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report") |
| .putExtra(Intent.EXTRA_STREAM, session) |
| .putExtra(Intent.EXTRA_TEXT, message.toString()), |
| "Share rejected touch report") |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), |
| true /* onlyProvisioned */, true /* dismissShade */); |
| }); |
| } |
| |
| if (!mPowerManager.isInteractive()) { |
| mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); |
| } |
| mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, |
| "sysui:GestureWakeLock"); |
| |
| // receive broadcasts |
| registerBroadcastReceiver(); |
| |
| IntentFilter demoFilter = new IntentFilter(); |
| if (DEBUG_MEDIA_FAKE_ARTWORK) { |
| demoFilter.addAction(ACTION_FAKE_ARTWORK); |
| } |
| mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter, |
| android.Manifest.permission.DUMP, null, |
| Context.RECEIVER_EXPORTED_UNAUDITED); |
| |
| // listen for USER_SETUP_COMPLETE setting (per-user) |
| mDeviceProvisionedController.addCallback(mUserSetupObserver); |
| mUserSetupObserver.onUserSetupChanged(); |
| |
| // disable profiling bars, since they overlap and clutter the output on app windows |
| ThreadedRenderer.overrideProperty("disableProfileBars", "true"); |
| |
| // Private API call to make the shadows look better for Recents |
| ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); |
| } |
| |
| |
| /** |
| * When swiping up to dismiss the lock screen, the panel expansion fraction goes from 1f to 0f. |
| * This results in the clock/notifications/other content disappearing off the top of the screen. |
| * |
| * We also use the expansion fraction to animate in the app/launcher surface from the bottom of |
| * the screen, 'pushing' off the notifications and other content. To do this, we dispatch the |
| * expansion fraction to the KeyguardViewMediator if we're in the process of dismissing the |
| * keyguard. |
| */ |
| private void dispatchPanelExpansionForKeyguardDismiss(float fraction, boolean trackingTouch) { |
| // Things that mean we're not swiping to dismiss the keyguard, and should ignore this |
| // expansion: |
| // - Keyguard isn't even visible. |
| // - Keyguard is occluded. Expansion changes here are the shade being expanded over the |
| // occluding activity. |
| // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt). |
| // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no |
| // device lock set, canDismissLockScreen returns true even though you should not be able |
| // to dismiss the lock screen until entering the SIM PIN. |
| // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the |
| // keyguard. |
| if (!isKeyguardShowing() |
| || isOccluded() |
| || !mKeyguardStateController.canDismissLockScreen() |
| || mKeyguardViewMediator.isAnySimPinSecure() |
| || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) { |
| return; |
| } |
| |
| // Otherwise, we should let the keyguard know about this if we're tracking touch, or if we |
| // are already animating the keyguard dismiss (since we will need to either finish or cancel |
| // the animation). |
| if (trackingTouch |
| || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe() |
| || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { |
| mKeyguardStateController.notifyKeyguardDismissAmountChanged( |
| 1f - fraction, trackingTouch); |
| } |
| } |
| |
| private void onPanelExpansionChanged(PanelExpansionChangeEvent event) { |
| float fraction = event.getFraction(); |
| boolean tracking = event.getTracking(); |
| dispatchPanelExpansionForKeyguardDismiss(fraction, tracking); |
| |
| if (fraction == 0 || fraction == 1) { |
| if (getNavigationBarView() != null) { |
| getNavigationBarView().onStatusBarPanelStateChanged(); |
| } |
| if (getNotificationPanelViewController() != null) { |
| getNotificationPanelViewController().updateSystemUiStateFlags(); |
| } |
| } |
| } |
| |
| @NonNull |
| @Override |
| public Lifecycle getLifecycle() { |
| return mLifecycle; |
| } |
| |
| @VisibleForTesting |
| protected void registerBroadcastReceiver() { |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); |
| filter.addAction(Intent.ACTION_SCREEN_OFF); |
| mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL); |
| } |
| |
| protected QS createDefaultQSFragment() { |
| return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class); |
| } |
| |
| private void setUpPresenter() { |
| // Set up the initial notification state. |
| mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback); |
| mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener); |
| mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( |
| mNotificationShadeWindowViewController, |
| mNotifListContainer, |
| mHeadsUpManager, |
| mJankMonitor); |
| mNotificationShelfController.setOnActivatedListener(mPresenter); |
| mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); |
| mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter); |
| mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); |
| mNotificationsController.initialize( |
| mPresenter, |
| mNotifListContainer, |
| mStackScrollerController.getNotifStackController(), |
| mNotificationActivityStarter, |
| mCentralSurfacesComponent.getBindRowCallback()); |
| } |
| |
| /** |
| * Post-init task of {@link #start()} |
| * @param state1 disable1 flags |
| * @param state2 disable2 flags |
| */ |
| protected void setUpDisableFlags(int state1, int state2) { |
| mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */); |
| } |
| |
| /** |
| * Ask the display to wake up if currently dozing, else do nothing |
| * |
| * @param time when to wake up |
| * @param where the view requesting the wakeup |
| * @param why the reason for the wake up |
| */ |
| @Override |
| public void wakeUpIfDozing(long time, View where, String why) { |
| if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) { |
| mPowerManager.wakeUp( |
| time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why); |
| mWakeUpComingFromTouch = true; |
| |
| // NOTE, the incoming view can sometimes be the entire container... unsure if |
| // this location is valuable enough |
| if (where != null) { |
| where.getLocationInWindow(mTmpInt2); |
| mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, |
| mTmpInt2[1] + where.getHeight() / 2); |
| } else { |
| mWakeUpTouchLocation = new PointF(-1, -1); |
| } |
| mFalsingCollector.onScreenOnFromTouch(); |
| } |
| } |
| |
| // TODO(b/117478341): This was left such that CarStatusBar can override this method. |
| // Try to remove this. |
| protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { |
| mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result); |
| } |
| |
| /** |
| * Returns the {@link android.view.View.OnTouchListener} that will be invoked when the |
| * background window of the status bar is clicked. |
| */ |
| protected View.OnTouchListener getStatusBarWindowTouchListener() { |
| return (v, event) -> { |
| mAutoHideController.checkUserAutoHide(event); |
| mRemoteInputManager.checkRemoteInputOutside(event); |
| if (event.getAction() == MotionEvent.ACTION_UP) { |
| if (mExpandedVisible) { |
| mShadeController.animateCollapsePanels(); |
| } |
| } |
| return mNotificationShadeWindowView.onTouchEvent(event); |
| }; |
| } |
| |
| private void inflateStatusBarWindow() { |
| if (mCentralSurfacesComponent != null) { |
| // Tear down |
| for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) { |
| s.stop(); |
| } |
| } |
| mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create(); |
| mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent); |
| |
| mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView(); |
| mNotificationShadeWindowViewController = mCentralSurfacesComponent |
| .getNotificationShadeWindowViewController(); |
| mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); |
| mNotificationShadeWindowViewController.setupExpandedStatusBar(); |
| mNotificationPanelViewController = |
| mCentralSurfacesComponent.getNotificationPanelViewController(); |
| mCentralSurfacesComponent.getLockIconViewController().init(); |
| mStackScrollerController = |
| mCentralSurfacesComponent.getNotificationStackScrollLayoutController(); |
| mStackScroller = mStackScrollerController.getView(); |
| mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer(); |
| mPresenter = mCentralSurfacesComponent.getNotificationPresenter(); |
| mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter(); |
| mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController(); |
| mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController(); |
| mAuthRippleController.init(); |
| |
| mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener()); |
| |
| // Listen for demo mode changes |
| mDemoModeController.addCallback(mDemoModeCallback); |
| |
| if (mCommandQueueCallbacks != null) { |
| mCommandQueue.removeCallback(mCommandQueueCallbacks); |
| } |
| mCommandQueueCallbacks = |
| mCentralSurfacesComponent.getCentralSurfacesCommandQueueCallbacks(); |
| // Connect in to the status bar manager service |
| mCommandQueue.addCallback(mCommandQueueCallbacks); |
| |
| // Perform all other initialization for CentralSurfacesScope |
| for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) { |
| s.start(); |
| } |
| } |
| |
| protected void startKeyguard() { |
| Trace.beginSection("CentralSurfaces#startKeyguard"); |
| mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); |
| mBiometricUnlockController.setBiometricModeListener( |
| new BiometricUnlockController.BiometricModeListener() { |
| @Override |
| public void onResetMode() { |
| setWakeAndUnlocking(false); |
| } |
| |
| @Override |
| public void onModeChanged(int mode) { |
| switch (mode) { |
| case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM: |
| case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING: |
| case BiometricUnlockController.MODE_WAKE_AND_UNLOCK: |
| setWakeAndUnlocking(true); |
| } |
| } |
| |
| @Override |
| public void notifyBiometricAuthModeChanged() { |
| CentralSurfacesImpl.this.notifyBiometricAuthModeChanged(); |
| } |
| |
| private void setWakeAndUnlocking(boolean wakeAndUnlocking) { |
| if (getNavigationBarView() != null) { |
| getNavigationBarView().setWakeAndUnlocking(wakeAndUnlocking); |
| } |
| } |
| }); |
| mKeyguardViewMediator.registerCentralSurfaces( |
| /* statusBar= */ this, |
| mNotificationPanelViewController, |
| mPanelExpansionStateManager, |
| mBiometricUnlockController, |
| mStackScroller, |
| mKeyguardBypassController); |
| mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); |
| mKeyguardIndicationController |
| .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); |
| mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); |
| mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager); |
| mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); |
| |
| mLightBarController.setBiometricUnlockController(mBiometricUnlockController); |
| mMediaManager.setBiometricUnlockController(mBiometricUnlockController); |
| mKeyguardDismissUtil.setDismissHandler(this::executeWhenUnlocked); |
| Trace.endSection(); |
| } |
| |
| @Override |
| public NotificationShadeWindowView getNotificationShadeWindowView() { |
| return mNotificationShadeWindowView; |
| } |
| |
| @Override |
| public NotificationShadeWindowViewController getNotificationShadeWindowViewController() { |
| return mNotificationShadeWindowViewController; |
| } |
| |
| @Override |
| public NotificationPanelViewController getNotificationPanelViewController() { |
| return mNotificationPanelViewController; |
| } |
| |
| @Override |
| public ViewGroup getBouncerContainer() { |
| return mNotificationShadeWindowViewController.getBouncerContainer(); |
| } |
| |
| @Override |
| public int getStatusBarHeight() { |
| return mStatusBarWindowController.getStatusBarHeight(); |
| } |
| |
| /** |
| * Disable QS if device not provisioned. |
| * If the user switcher is simple then disable QS during setup because |
| * the user intends to use the lock screen user switcher, QS in not needed. |
| */ |
| @Override |
| public void updateQsExpansionEnabled() { |
| final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned() |
| && (mUserSetup || mUserSwitcherController == null |
| || !mUserSwitcherController.isSimpleUserSwitcher()) |
| && !isShadeDisabled() |
| && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) |
| && !mDozing |
| && !ONLY_CORE_APPS; |
| mNotificationPanelViewController.setQsExpansionEnabledPolicy(expandEnabled); |
| Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled); |
| } |
| |
| @Override |
| public boolean isShadeDisabled() { |
| return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0; |
| } |
| |
| /** |
| * Request a notification update |
| * @param reason why we're requesting a notification update |
| */ |
| @Override |
| public void requestNotificationUpdate(String reason) { |
| mNotificationsController.requestNotificationUpdate(reason); |
| } |
| |
| /** |
| * Asks {@link KeyguardUpdateMonitor} to run face auth. |
| */ |
| @Override |
| public void requestFaceAuth(boolean userInitiatedRequest) { |
| if (!mKeyguardStateController.canDismissLockScreen()) { |
| mKeyguardUpdateMonitor.requestFaceAuth(userInitiatedRequest); |
| } |
| } |
| |
| private void updateReportRejectedTouchVisibility() { |
| if (mReportRejectedTouch == null) { |
| return; |
| } |
| mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing |
| && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); |
| } |
| |
| @Override |
| public boolean areNotificationAlertsDisabled() { |
| return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, |
| int flags) { |
| startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, flags); |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean dismissShade) { |
| startActivityDismissingKeyguard(intent, false /* onlyProvisioned */, dismissShade); |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean dismissShade, |
| @Nullable ActivityLaunchAnimator.Controller animationController, |
| boolean showOverLockscreenWhenLocked) { |
| startActivity(intent, dismissShade, animationController, showOverLockscreenWhenLocked, |
| getActivityUserHandle(intent)); |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean dismissShade, |
| @Nullable ActivityLaunchAnimator.Controller animationController, |
| boolean showOverLockscreenWhenLocked, UserHandle userHandle) { |
| // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't |
| // want to show the activity above it. |
| if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) { |
| startActivityDismissingKeyguard(intent, false, dismissShade, |
| false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, |
| 0 /* flags */, animationController, userHandle); |
| return; |
| } |
| |
| boolean animate = |
| animationController != null && shouldAnimateLaunch(true /* isActivityIntent */, |
| showOverLockscreenWhenLocked); |
| |
| ActivityLaunchAnimator.Controller controller = null; |
| if (animate) { |
| // Wrap the animation controller to dismiss the shade and set |
| // mIsLaunchingActivityOverLockscreen during the animation. |
| ActivityLaunchAnimator.Controller delegate = wrapAnimationController( |
| animationController, dismissShade); |
| controller = new DelegateLaunchAnimatorController(delegate) { |
| @Override |
| public void onIntentStarted(boolean willAnimate) { |
| getDelegate().onIntentStarted(willAnimate); |
| |
| if (willAnimate) { |
| CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = true; |
| } |
| } |
| |
| @Override |
| public void onLaunchAnimationStart(boolean isExpandingFullyAbove) { |
| super.onLaunchAnimationStart(isExpandingFullyAbove); |
| |
| // Double check that the keyguard is still showing and not going away, but if so |
| // set the keyguard occluded. Typically, WM will let KeyguardViewMediator know |
| // directly, but we're overriding that to play the custom launch animation, so |
| // we need to take care of that here. The unocclude animation is not overridden, |
| // so WM will call KeyguardViewMediator's unocclude animation runner when the |
| // activity is exited. |
| if (mKeyguardStateController.isShowing() |
| && !mKeyguardStateController.isKeyguardGoingAway()) { |
| Log.d(TAG, "Setting occluded = true in #startActivity."); |
| mKeyguardViewMediator.setOccluded(true /* isOccluded */, |
| true /* animate */); |
| } |
| } |
| |
| @Override |
| public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { |
| // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the |
| // animation so that we can assume that mIsLaunchingActivityOverLockscreen |
| // being true means that we will collapse the shade (or at least run the |
| // post collapse runnables) later on. |
| CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false; |
| getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove); |
| } |
| |
| @Override |
| public void onLaunchAnimationCancelled() { |
| // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the |
| // animation so that we can assume that mIsLaunchingActivityOverLockscreen |
| // being true means that we will collapse the shade (or at least run the |
| // post collapse runnables) later on. |
| CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false; |
| getDelegate().onLaunchAnimationCancelled(); |
| } |
| }; |
| } else if (dismissShade) { |
| // The animation will take care of dismissing the shade at the end of the animation. If |
| // we don't animate, collapse it directly. |
| collapseShade(); |
| } |
| |
| // We should exit the dream to prevent the activity from starting below the |
| // dream. |
| if (mKeyguardUpdateMonitor.isDreaming()) { |
| awakenDreams(); |
| } |
| |
| mActivityLaunchAnimator.startIntentWithAnimation(controller, animate, |
| intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder |
| .create(mContext) |
| .addNextIntent(intent) |
| .startActivities( |
| CentralSurfaces.getActivityOptions(getDisplayId(), adapter), |
| userHandle)); |
| } |
| |
| /** |
| * Whether we are currently animating an activity launch above the lockscreen (occluding |
| * activity). |
| */ |
| @Override |
| public boolean isLaunchingActivityOverLockscreen() { |
| return mIsLaunchingActivityOverLockscreen; |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) { |
| startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade); |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean dismissShade, Callback callback) { |
| startActivityDismissingKeyguard(intent, false, dismissShade, |
| false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0, |
| null /* animationController */, getActivityUserHandle(intent)); |
| } |
| |
| @Override |
| public void setQsExpanded(boolean expanded) { |
| mNotificationShadeWindowController.setQsExpanded(expanded); |
| mNotificationPanelViewController.setStatusAccessibilityImportance(expanded |
| ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS |
| : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); |
| mNotificationPanelViewController.updateSystemUiStateFlags(); |
| if (getNavigationBarView() != null) { |
| getNavigationBarView().onStatusBarPanelStateChanged(); |
| } |
| } |
| |
| @Override |
| public boolean isWakeUpComingFromTouch() { |
| return mWakeUpComingFromTouch; |
| } |
| |
| @Override |
| public boolean isFalsingThresholdNeeded() { |
| return true; |
| } |
| |
| /** |
| * To be called when there's a state change in StatusBarKeyguardViewManager. |
| */ |
| @Override |
| public void onKeyguardViewManagerStatesUpdated() { |
| logStateToEventlog(); |
| } |
| |
| @Override |
| public void setPanelExpanded(boolean isExpanded) { |
| if (mPanelExpanded != isExpanded) { |
| mNotificationLogger.onPanelExpandedChanged(isExpanded); |
| } |
| mPanelExpanded = isExpanded; |
| mStatusBarHideIconsForBouncerManager.setPanelExpandedAndTriggerUpdate(isExpanded); |
| mNotificationShadeWindowController.setPanelExpanded(isExpanded); |
| mStatusBarStateController.setPanelExpanded(isExpanded); |
| if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { |
| if (DEBUG) { |
| Log.v(TAG, "clearing notification effects from Height"); |
| } |
| clearNotificationEffects(); |
| } |
| |
| if (!isExpanded) { |
| mRemoteInputManager.onPanelCollapsed(); |
| } |
| } |
| |
| @Override |
| public ViewGroup getNotificationScrollLayout() { |
| return mStackScroller; |
| } |
| |
| @Override |
| public boolean isPulsing() { |
| return mDozeServiceHost.isPulsing(); |
| } |
| |
| @androidx.annotation.Nullable |
| @Override |
| public View getAmbientIndicationContainer() { |
| return mAmbientIndicationContainer; |
| } |
| |
| /** |
| * When the keyguard is showing and covered by a "showWhenLocked" activity it |
| * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager} |
| * |
| * @return whether the keyguard is currently occluded |
| */ |
| @Override |
| public boolean isOccluded() { |
| return mKeyguardStateController.isOccluded(); |
| } |
| |
| /** A launch animation was cancelled. */ |
| //TODO: These can / should probably be moved to NotificationPresenter or ShadeController |
| @Override |
| public void onLaunchAnimationCancelled(boolean isLaunchForActivity) { |
| if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing() |
| && isLaunchForActivity) { |
| onClosingFinished(); |
| } else { |
| mShadeController.collapsePanel(true /* animate */); |
| } |
| } |
| |
| /** A launch animation ended. */ |
| @Override |
| public void onLaunchAnimationEnd(boolean launchIsFullScreen) { |
| if (!mPresenter.isCollapsing()) { |
| onClosingFinished(); |
| } |
| if (launchIsFullScreen) { |
| instantCollapseNotificationPanel(); |
| } |
| } |
| |
| /** |
| * Whether we should animate an activity launch. |
| * |
| * Note: This method must be called *before* dismissing the keyguard. |
| */ |
| @Override |
| public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) { |
| // TODO(b/184121838): Support launch animations when occluded. |
| if (isOccluded()) { |
| return false; |
| } |
| |
| // Always animate if we are not showing the keyguard or if we animate over the lockscreen |
| // (without unlocking it). |
| if (showOverLockscreen || !mKeyguardStateController.isShowing()) { |
| return true; |
| } |
| |
| // If we are locked and have to dismiss the keyguard, only animate if remote unlock |
| // animations are enabled. We also don't animate non-activity launches as they can break the |
| // animation. |
| // TODO(b/184121838): Support non activity launches on the lockscreen. |
| return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation; |
| } |
| |
| /** Whether we should animate an activity launch. */ |
| @Override |
| public boolean shouldAnimateLaunch(boolean isActivityIntent) { |
| return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */); |
| } |
| |
| @Override |
| public boolean isDeviceInVrMode() { |
| return mPresenter.isDeviceInVrMode(); |
| } |
| |
| @Override |
| public NotificationPresenter getPresenter() { |
| return mPresenter; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public void setBarStateForTest(int state) { |
| mState = state; |
| } |
| |
| static class AnimateExpandSettingsPanelMessage { |
| final String mSubpanel; |
| |
| AnimateExpandSettingsPanelMessage(String subpanel) { |
| mSubpanel = subpanel; |
| } |
| } |
| |
| private void maybeEscalateHeadsUp() { |
| mHeadsUpManager.getAllEntries().forEach(entry -> { |
| final StatusBarNotification sbn = entry.getSbn(); |
| final Notification notification = sbn.getNotification(); |
| if (notification.fullScreenIntent != null) { |
| if (DEBUG) { |
| Log.d(TAG, "converting a heads up to fullScreen"); |
| } |
| try { |
| EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, |
| sbn.getKey()); |
| wakeUpForFullScreenIntent(); |
| notification.fullScreenIntent.send(); |
| entry.notifyFullScreenIntentLaunched(); |
| } catch (PendingIntent.CanceledException e) { |
| } |
| } |
| }); |
| mHeadsUpManager.releaseAllImmediately(); |
| } |
| |
| @Override |
| public void wakeUpForFullScreenIntent() { |
| if (isGoingToSleep() || mDozing) { |
| mPowerManager.wakeUp( |
| SystemClock.uptimeMillis(), |
| PowerManager.WAKE_REASON_APPLICATION, |
| "com.android.systemui:full_screen_intent"); |
| mWakeUpComingFromTouch = false; |
| mWakeUpTouchLocation = null; |
| } |
| } |
| |
| @Override |
| public void makeExpandedVisible(boolean force) { |
| if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); |
| if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) { |
| return; |
| } |
| |
| mExpandedVisible = true; |
| |
| // Expand the window to encompass the full screen in anticipation of the drag. |
| // This is only possible to do atomically because the status bar is at the top of the screen! |
| mNotificationShadeWindowController.setPanelVisible(true); |
| |
| visibilityChanged(true); |
| mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */); |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); |
| } |
| |
| @Override |
| public void postAnimateCollapsePanels() { |
| mMainExecutor.execute(mShadeController::animateCollapsePanels); |
| } |
| |
| @Override |
| public void postAnimateForceCollapsePanels() { |
| mMainExecutor.execute( |
| () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, |
| true /* force */)); |
| } |
| |
| @Override |
| public void postAnimateOpenPanels() { |
| mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL); |
| } |
| |
| @Override |
| public boolean isExpandedVisible() { |
| return mExpandedVisible; |
| } |
| |
| @Override |
| public boolean isPanelExpanded() { |
| return mPanelExpanded; |
| } |
| |
| /** |
| * Called when another window is about to transfer it's input focus. |
| */ |
| @Override |
| public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) { |
| if (!mCommandQueue.panelsEnabled()) { |
| return; |
| } |
| |
| if (start) { |
| mNotificationPanelViewController.startWaitingForOpenPanelGesture(); |
| } else { |
| mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity); |
| } |
| } |
| |
| @Override |
| public void animateCollapseQuickSettings() { |
| if (mState == StatusBarState.SHADE) { |
| mNotificationPanelViewController.collapsePanel( |
| true, false /* delayed */, 1.0f /* speedUpFactor */); |
| } |
| } |
| |
| void makeExpandedInvisible() { |
| if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible |
| + " mExpandedVisible=" + mExpandedVisible); |
| |
| if (!mExpandedVisible || mNotificationShadeWindowView == null) { |
| return; |
| } |
| |
| // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) |
| mNotificationPanelViewController.collapsePanel(/*animate=*/ false, false /* delayed*/, |
| 1.0f /* speedUpFactor */); |
| |
| mNotificationPanelViewController.closeQs(); |
| |
| mExpandedVisible = false; |
| visibilityChanged(false); |
| |
| // Update the visibility of notification shade and status bar window. |
| mNotificationShadeWindowController.setPanelVisible(false); |
| mStatusBarWindowController.setForceStatusBarVisible(false); |
| |
| // Close any guts that might be visible |
| mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, |
| true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */); |
| |
| mShadeController.runPostCollapseRunnables(); |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); |
| if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) { |
| showBouncerOrLockScreenIfKeyguard(); |
| } else if (DEBUG) { |
| Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen"); |
| } |
| mCommandQueue.recomputeDisableFlags( |
| mDisplayId, |
| mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */); |
| |
| // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in |
| // the bouncer appear animation. |
| if (!mStatusBarKeyguardViewManager.isShowing()) { |
| WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); |
| } |
| } |
| |
| /** Called when a touch event occurred on {@link PhoneStatusBarView}. */ |
| @Override |
| public void onTouchEvent(MotionEvent event) { |
| // TODO(b/202981994): Move this touch debugging to a central location. (Right now, it's |
| // split between NotificationPanelViewController and here.) |
| if (DEBUG_GESTURES) { |
| if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { |
| EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, |
| event.getActionMasked(), (int) event.getX(), (int) event.getY(), |
| mDisabled1, mDisabled2); |
| } |
| |
| } |
| |
| if (SPEW) { |
| Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1=" |
| + mDisabled1 + " mDisabled2=" + mDisabled2); |
| } else if (CHATTY) { |
| if (event.getAction() != MotionEvent.ACTION_MOVE) { |
| Log.d(TAG, String.format( |
| "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x", |
| MotionEvent.actionToString(event.getAction()), |
| event.getRawX(), event.getRawY(), mDisabled1, mDisabled2)); |
| } |
| } |
| |
| if (DEBUG_GESTURES) { |
| mGestureRec.add(event); |
| } |
| |
| if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { |
| final boolean upOrCancel = |
| event.getAction() == MotionEvent.ACTION_UP || |
| event.getAction() == MotionEvent.ACTION_CANCEL; |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible); |
| } |
| } |
| |
| @Override |
| public GestureRecorder getGestureRecorder() { |
| return mGestureRec; |
| } |
| |
| @Override |
| public BiometricUnlockController getBiometricUnlockController() { |
| return mBiometricUnlockController; |
| } |
| |
| @Override |
| public void showTransientUnchecked() { |
| if (!mTransientShown) { |
| mTransientShown = true; |
| mNoAnimationOnNextBarModeChange = true; |
| maybeUpdateBarMode(); |
| } |
| } |
| |
| @Override |
| public void clearTransient() { |
| if (mTransientShown) { |
| mTransientShown = false; |
| maybeUpdateBarMode(); |
| } |
| } |
| |
| private void maybeUpdateBarMode() { |
| final int barMode = barMode(mTransientShown, mAppearance); |
| if (updateBarMode(barMode)) { |
| mLightBarController.onStatusBarModeChanged(barMode); |
| updateBubblesVisibility(); |
| } |
| } |
| |
| private boolean updateBarMode(int barMode) { |
| if (mStatusBarMode != barMode) { |
| mStatusBarMode = barMode; |
| checkBarModes(); |
| mAutoHideController.touchAutoHide(); |
| return true; |
| } |
| return false; |
| } |
| |
| private @TransitionMode int barMode(boolean isTransient, int appearance) { |
| final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS; |
| if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) { |
| return MODE_SEMI_TRANSPARENT; |
| } else if (isTransient) { |
| return MODE_SEMI_TRANSPARENT; |
| } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) { |
| return MODE_LIGHTS_OUT; |
| } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) { |
| return MODE_LIGHTS_OUT_TRANSPARENT; |
| } else if ((appearance & APPEARANCE_OPAQUE_STATUS_BARS) != 0) { |
| return MODE_OPAQUE; |
| } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) != 0) { |
| return MODE_SEMI_TRANSPARENT; |
| } else { |
| return MODE_TRANSPARENT; |
| } |
| } |
| |
| @Override |
| public void showWirelessChargingAnimation(int batteryLevel) { |
| showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0); |
| } |
| |
| protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel, |
| long animationDelay) { |
| WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null, |
| transmittingBatteryLevel, batteryLevel, |
| new WirelessChargingAnimation.Callback() { |
| @Override |
| public void onAnimationStarting() { |
| mNotificationShadeWindowController.setRequestTopUi(true, TAG); |
| } |
| |
| @Override |
| public void onAnimationEnded() { |
| mNotificationShadeWindowController.setRequestTopUi(false, TAG); |
| } |
| }, false, sUiEventLogger).show(animationDelay); |
| } |
| |
| @Override |
| public void checkBarModes() { |
| if (mDemoModeController.isInDemoMode()) return; |
| if (mStatusBarTransitions != null) { |
| checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarTransitions); |
| } |
| mNavigationBarController.checkNavBarModes(mDisplayId); |
| mNoAnimationOnNextBarModeChange = false; |
| } |
| |
| // Called by NavigationBarFragment |
| @Override |
| public void setQsScrimEnabled(boolean scrimEnabled) { |
| mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled); |
| } |
| |
| /** Temporarily hides Bubbles if the status bar is hidden. */ |
| @Override |
| public void updateBubblesVisibility() { |
| mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged( |
| mStatusBarMode != MODE_LIGHTS_OUT |
| && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT)); |
| } |
| |
| void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState, |
| BarTransitions transitions) { |
| final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive |
| && windowState != WINDOW_STATE_HIDDEN; |
| transitions.transitionTo(mode, anim); |
| } |
| |
| private void finishBarAnimations() { |
| if (mStatusBarTransitions != null) { |
| mStatusBarTransitions.finishAnimations(); |
| } |
| mNavigationBarController.finishBarAnimations(mDisplayId); |
| } |
| |
| private final Runnable mCheckBarModes = this::checkBarModes; |
| |
| @Override |
| public void setInteracting(int barWindow, boolean interacting) { |
| mInteractingWindows = interacting |
| ? (mInteractingWindows | barWindow) |
| : (mInteractingWindows & ~barWindow); |
| if (mInteractingWindows != 0) { |
| mAutoHideController.suspendAutoHide(); |
| } else { |
| mAutoHideController.resumeSuspendedAutoHide(); |
| } |
| checkBarModes(); |
| } |
| |
| private void dismissVolumeDialog() { |
| if (mVolumeComponent != null) { |
| mVolumeComponent.dismissNow(); |
| } |
| } |
| |
| @Override |
| public void dump(PrintWriter pwOriginal, String[] args) { |
| IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal); |
| synchronized (mQueueLock) { |
| pw.println("Current Status Bar state:"); |
| pw.println(" mExpandedVisible=" + mExpandedVisible); |
| pw.println(" mDisplayMetrics=" + mDisplayMetrics); |
| pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)); |
| pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller) |
| + " scroll " + mStackScroller.getScrollX() |
| + "," + mStackScroller.getScrollY()); |
| } |
| |
| pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); |
| pw.print(" mStatusBarWindowState="); |
| pw.println(windowStateToString(mStatusBarWindowState)); |
| pw.print(" mStatusBarMode="); |
| pw.println(BarTransitions.modeToString(mStatusBarMode)); |
| pw.print(" mDozing="); pw.println(mDozing); |
| pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported); |
| |
| pw.println(" ShadeWindowView: "); |
| if (mNotificationShadeWindowViewController != null) { |
| mNotificationShadeWindowViewController.dump(pw, args); |
| CentralSurfaces.dumpBarTransitions( |
| pw, "PhoneStatusBarTransitions", mStatusBarTransitions); |
| } |
| |
| pw.println(" mMediaManager: "); |
| if (mMediaManager != null) { |
| mMediaManager.dump(pw, args); |
| } |
| |
| pw.println(" Panels: "); |
| if (mNotificationPanelViewController != null) { |
| pw.println(" mNotificationPanel=" |
| + mNotificationPanelViewController.getView() + " params=" |
| + mNotificationPanelViewController.getView().getLayoutParams().debug("")); |
| pw.print (" "); |
| mNotificationPanelViewController.dump(pw, args); |
| } |
| pw.println(" mStackScroller: " + mStackScroller + " (dump moved)"); |
| pw.println(" Theme:"); |
| String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + ""; |
| pw.println(" dark theme: " + nightMode + |
| " (auto: " + UiModeManager.MODE_NIGHT_AUTO + |
| ", yes: " + UiModeManager.MODE_NIGHT_YES + |
| ", no: " + UiModeManager.MODE_NIGHT_NO + ")"); |
| final boolean lightWpTheme = mContext.getThemeResId() |
| == R.style.Theme_SystemUI_LightWallpaper; |
| pw.println(" light wallpaper theme: " + lightWpTheme); |
| |
| if (mKeyguardIndicationController != null) { |
| mKeyguardIndicationController.dump(pw, args); |
| } |
| |
| if (mScrimController != null) { |
| mScrimController.dump(pw, args); |
| } |
| |
| if (mLightRevealScrim != null) { |
| pw.println( |
| "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect()); |
| pw.println( |
| "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount()); |
| } |
| |
| if (mStatusBarKeyguardViewManager != null) { |
| mStatusBarKeyguardViewManager.dump(pw); |
| } |
| |
| mNotificationsController.dump(pw, args, DUMPTRUCK); |
| |
| if (DEBUG_GESTURES) { |
| pw.print(" status bar gestures: "); |
| mGestureRec.dump(pw, args); |
| } |
| |
| if (mHeadsUpManager != null) { |
| mHeadsUpManager.dump(pw, args); |
| } else { |
| pw.println(" mHeadsUpManager: null"); |
| } |
| |
| if (mStatusBarTouchableRegionManager != null) { |
| mStatusBarTouchableRegionManager.dump(pw, args); |
| } else { |
| pw.println(" mStatusBarTouchableRegionManager: null"); |
| } |
| |
| if (mLightBarController != null) { |
| mLightBarController.dump(pw, args); |
| } |
| |
| pw.println("SharedPreferences:"); |
| for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { |
| pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); |
| } |
| |
| pw.println("Camera gesture intents:"); |
| pw.println(" Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext)); |
| pw.println(" Secure camera: " + CameraIntents.getSecureCameraIntent(mContext)); |
| pw.println(" Override package: " |
| + CameraIntents.getOverrideCameraPackage(mContext)); |
| } |
| |
| @Override |
| public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { |
| makeStatusBarView(result); |
| mNotificationShadeWindowController.attach(); |
| mStatusBarWindowController.attach(); |
| } |
| |
| // called by makeStatusbar and also by PhoneStatusBarView |
| void updateDisplaySize() { |
| mDisplay.getMetrics(mDisplayMetrics); |
| mDisplay.getSize(mCurrentDisplaySize); |
| if (DEBUG_GESTURES) { |
| mGestureRec.tag("display", |
| String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); |
| } |
| } |
| |
| @Override |
| public float getDisplayDensity() { |
| return mDisplayMetrics.density; |
| } |
| |
| @Override |
| public float getDisplayWidth() { |
| return mDisplayMetrics.widthPixels; |
| } |
| |
| @Override |
| public float getDisplayHeight() { |
| return mDisplayMetrics.heightPixels; |
| } |
| |
| @Override |
| public int getRotation() { |
| return mDisplay.getRotation(); |
| } |
| |
| @Override |
| public int getDisplayId() { |
| return mDisplayId; |
| } |
| |
| @Override |
| public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, |
| boolean dismissShade, int flags) { |
| startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, |
| false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, |
| flags, null /* animationController */, getActivityUserHandle(intent)); |
| } |
| |
| @Override |
| public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, |
| boolean dismissShade) { |
| startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0); |
| } |
| |
| @Override |
| public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, |
| final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching, |
| final Callback callback, int flags, |
| @Nullable ActivityLaunchAnimator.Controller animationController, |
| final UserHandle userHandle) { |
| if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return; |
| |
| final boolean willLaunchResolverActivity = |
| mActivityIntentHelper.wouldLaunchResolverActivity(intent, |
| mLockscreenUserManager.getCurrentUserId()); |
| |
| boolean animate = |
| animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch( |
| true /* isActivityIntent */); |
| ActivityLaunchAnimator.Controller animController = |
| animationController != null ? wrapAnimationController(animationController, |
| dismissShade) : null; |
| |
| // If we animate, we will dismiss the shade only once the animation is done. This is taken |
| // care of by the StatusBarLaunchAnimationController. |
| boolean dismissShadeDirectly = dismissShade && animController == null; |
| |
| Runnable runnable = () -> { |
| mAssistManagerLazy.get().hideAssist(); |
| intent.setFlags( |
| Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| intent.addFlags(flags); |
| int[] result = new int[]{ActivityManager.START_CANCELED}; |
| |
| mActivityLaunchAnimator.startIntentWithAnimation(animController, |
| animate, intent.getPackage(), (adapter) -> { |
| ActivityOptions options = new ActivityOptions( |
| CentralSurfaces.getActivityOptions(mDisplayId, adapter)); |
| |
| // We know that the intent of the caller is to dismiss the keyguard and |
| // this runnable is called right after the keyguard is solved, so we tell |
| // WM that we should dismiss it to avoid flickers when opening an activity |
| // that can also be shown over the keyguard. |
| options.setDismissKeyguard(); |
| options.setDisallowEnterPictureInPictureWhileLaunching( |
| disallowEnterPictureInPictureWhileLaunching); |
| if (CameraIntents.isInsecureCameraIntent(intent)) { |
| // Normally an activity will set it's requested rotation |
| // animation on its window. However when launching an activity |
| // causes the orientation to change this is too late. In these cases |
| // the default animation is used. This doesn't look good for |
| // the camera (as it rotates the camera contents out of sync |
| // with physical reality). So, we ask the WindowManager to |
| // force the crossfade animation if an orientation change |
| // happens to occur during the launch. |
| options.setRotationAnimationHint( |
| WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); |
| } |
| if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) { |
| // Settings Panel is implemented as activity(not a dialog), so |
| // underlying app is paused and may enter picture-in-picture mode |
| // as a result. |
| // So we need to disable picture-in-picture mode here |
| // if it is volume panel. |
| options.setDisallowEnterPictureInPictureWhileLaunching(true); |
| } |
| |
| try { |
| result[0] = ActivityTaskManager.getService().startActivityAsUser( |
| null, mContext.getBasePackageName(), |
| mContext.getAttributionTag(), |
| intent, |
| intent.resolveTypeIfNeeded(mContext.getContentResolver()), |
| null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, |
| options.toBundle(), userHandle.getIdentifier()); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Unable to start activity", e); |
| } |
| return result[0]; |
| }); |
| |
| if (callback != null) { |
| callback.onActivityStarted(result[0]); |
| } |
| }; |
| Runnable cancelRunnable = () -> { |
| if (callback != null) { |
| callback.onActivityStarted(ActivityManager.START_CANCELED); |
| } |
| }; |
| executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly, |
| willLaunchResolverActivity, true /* deferred */, animate); |
| } |
| |
| @Nullable |
| private ActivityLaunchAnimator.Controller wrapAnimationController( |
| ActivityLaunchAnimator.Controller animationController, boolean dismissShade) { |
| View rootView = animationController.getLaunchContainer().getRootView(); |
| |
| Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar = |
| mStatusBarWindowController.wrapAnimationControllerIfInStatusBar( |
| rootView, animationController); |
| if (controllerFromStatusBar.isPresent()) { |
| return controllerFromStatusBar.get(); |
| } |
| |
| if (dismissShade) { |
| // If the view is not in the status bar, then we are animating a view in the shade. |
| // We have to make sure that we collapse it when the animation ends or is cancelled. |
| return new StatusBarLaunchAnimatorController(animationController, this, |
| true /* isLaunchForActivity */); |
| } |
| |
| return animationController; |
| } |
| |
| @Override |
| public void readyForKeyguardDone() { |
| mStatusBarKeyguardViewManager.readyForKeyguardDone(); |
| } |
| |
| @Override |
| public void executeRunnableDismissingKeyguard(final Runnable runnable, |
| final Runnable cancelAction, |
| final boolean dismissShade, |
| final boolean afterKeyguardGone, |
| final boolean deferred) { |
| executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone, |
| deferred, false /* willAnimateOnKeyguard */); |
| } |
| |
| @Override |
| public void executeRunnableDismissingKeyguard(final Runnable runnable, |
| final Runnable cancelAction, |
| final boolean dismissShade, |
| final boolean afterKeyguardGone, |
| final boolean deferred, |
| final boolean willAnimateOnKeyguard) { |
| OnDismissAction onDismissAction = new OnDismissAction() { |
| @Override |
| public boolean onDismiss() { |
| if (runnable != null) { |
| if (mStatusBarKeyguardViewManager.isShowing() |
| && mStatusBarKeyguardViewManager.isOccluded()) { |
| mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); |
| } else { |
| mMainExecutor.execute(runnable); |
| } |
| } |
| if (dismissShade) { |
| if (mExpandedVisible && !mBouncerShowing) { |
| mShadeController.animateCollapsePanels( |
| CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, |
| true /* force */, true /* delayed*/); |
| } else { |
| |
| // Do it after DismissAction has been processed to conserve the needed |
| // ordering. |
| mMainExecutor.execute(mShadeController::runPostCollapseRunnables); |
| } |
| } else if (CentralSurfacesImpl.this.isInLaunchTransition() |
| && mNotificationPanelViewController.isLaunchTransitionFinished()) { |
| |
| // We are not dismissing the shade, but the launch transition is already |
| // finished, |
| // so nobody will call readyForKeyguardDone anymore. Post it such that |
| // keyguardDonePending gets called first. |
| mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone); |
| } |
| return deferred; |
| } |
| |
| @Override |
| public boolean willRunAnimationOnKeyguard() { |
| return willAnimateOnKeyguard; |
| } |
| }; |
| dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone); |
| } |
| |
| private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| Trace.beginSection("CentralSurfaces#onReceive"); |
| if (DEBUG) Log.v(TAG, "onReceive: " + intent); |
| String action = intent.getAction(); |
| String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); |
| if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { |
| KeyboardShortcuts.dismiss(); |
| mRemoteInputManager.closeRemoteInputs(); |
| if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) { |
| int flags = CommandQueue.FLAG_EXCLUDE_NONE; |
| if (reason != null) { |
| if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { |
| flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; |
| } |
| // Do not collapse notifications when starting dreaming if the notifications |
| // shade is used for the screen off animation. It might require expanded |
| // state for the scrims to be visible |
| if (reason.equals(SYSTEM_DIALOG_REASON_DREAM) |
| && mScreenOffAnimationController.shouldExpandNotifications()) { |
| flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL; |
| } |
| } |
| mShadeController.animateCollapsePanels(flags); |
| } |
| } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { |
| if (mNotificationShadeWindowController != null) { |
| mNotificationShadeWindowController.setNotTouchable(false); |
| } |
| finishBarAnimations(); |
| resetUserExpandedStates(); |
| } |
| Trace.endSection(); |
| } |
| }; |
| |
| private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (DEBUG) Log.v(TAG, "onReceive: " + intent); |
| String action = intent.getAction(); |
| if (ACTION_FAKE_ARTWORK.equals(action)) { |
| if (DEBUG_MEDIA_FAKE_ARTWORK) { |
| mPresenter.updateMediaMetaData(true, true); |
| } |
| } |
| } |
| }; |
| |
| @Override |
| public void resetUserExpandedStates() { |
| mNotificationsController.resetUserExpandedStates(); |
| } |
| |
| private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, |
| boolean afterKeyguardGone) { |
| if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); |
| } |
| dismissKeyguardThenExecute(action, null /* cancelAction */, |
| afterKeyguardGone /* afterKeyguardGone */); |
| } |
| |
| protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { |
| dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); |
| } |
| |
| @Override |
| public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, |
| boolean afterKeyguardGone) { |
| if (!action.willRunAnimationOnKeyguard() |
| && mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP |
| && mKeyguardStateController.canDismissLockScreen() |
| && !mStatusBarStateController.leaveOpenOnKeyguardHide() |
| && mDozeServiceHost.isPulsing()) { |
| // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse. |
| // TODO: Factor this transition out of BiometricUnlockController. |
| mBiometricUnlockController.startWakeAndUnlock( |
| BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING); |
| } |
| if (mStatusBarKeyguardViewManager.isShowing()) { |
| mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, |
| afterKeyguardGone); |
| } else { |
| // If the keyguard isn't showing but the device is dreaming, we should exit the dream. |
| if (mKeyguardUpdateMonitor.isDreaming()) { |
| awakenDreams(); |
| } |
| action.onDismiss(); |
| } |
| } |
| /** |
| * Notify the shade controller that the current user changed |
| * |
| * @param newUserId userId of the new user |
| */ |
| @Override |
| public void setLockscreenUser(int newUserId) { |
| if (mLockscreenWallpaper != null) { |
| mLockscreenWallpaper.setCurrentUser(newUserId); |
| } |
| mScrimController.setCurrentUser(newUserId); |
| if (mWallpaperSupported) { |
| mWallpaperChangedReceiver.onReceive(mContext, null); |
| } |
| } |
| |
| /** |
| * Reload some of our resources when the configuration changes. |
| * |
| * We don't reload everything when the configuration changes -- we probably |
| * should, but getting that smooth is tough. Someday we'll fix that. In the |
| * meantime, just update the things that we know change. |
| */ |
| void updateResources() { |
| // Update the quick setting tiles |
| if (mQSPanelController != null) { |
| mQSPanelController.updateResources(); |
| } |
| |
| if (mStatusBarWindowController != null) { |
| mStatusBarWindowController.refreshStatusBarHeight(); |
| } |
| |
| if (mNotificationPanelViewController != null) { |
| mNotificationPanelViewController.updateResources(); |
| } |
| if (mBrightnessMirrorController != null) { |
| mBrightnessMirrorController.updateResources(); |
| } |
| if (mStatusBarKeyguardViewManager != null) { |
| mStatusBarKeyguardViewManager.updateResources(); |
| } |
| |
| mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize( |
| com.android.systemui.R.dimen.physical_power_button_center_screen_location_y)); |
| } |
| |
| // Visibility reporting |
| protected void handleVisibleToUserChanged(boolean visibleToUser) { |
| if (visibleToUser) { |
| handleVisibleToUserChangedImpl(visibleToUser); |
| mNotificationLogger.startNotificationLogging(); |
| } else { |
| mNotificationLogger.stopNotificationLogging(); |
| handleVisibleToUserChangedImpl(visibleToUser); |
| } |
| } |
| |
| // Visibility reporting |
| void handleVisibleToUserChangedImpl(boolean visibleToUser) { |
| if (visibleToUser) { |
| /* The LEDs are turned off when the notification panel is shown, even just a little bit. |
| * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do |
| * this. |
| */ |
| boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); |
| boolean clearNotificationEffects = |
| !mPresenter.isPresenterFullyCollapsed() && |
| (mState == StatusBarState.SHADE |
| || mState == StatusBarState.SHADE_LOCKED); |
| int notificationLoad = mNotificationsController.getActiveNotificationsCount(); |
| if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) { |
| notificationLoad = 1; |
| } |
| final int finalNotificationLoad = notificationLoad; |
| mUiBgExecutor.execute(() -> { |
| try { |
| mBarService.onPanelRevealed(clearNotificationEffects, |
| finalNotificationLoad); |
| } catch (RemoteException ex) { |
| // Won't fail unless the world has ended. |
| } |
| }); |
| } else { |
| mUiBgExecutor.execute(() -> { |
| try { |
| mBarService.onPanelHidden(); |
| } catch (RemoteException ex) { |
| // Won't fail unless the world has ended. |
| } |
| }); |
| } |
| |
| } |
| |
| private void logStateToEventlog() { |
| boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); |
| boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); |
| boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); |
| boolean isSecure = mKeyguardStateController.isMethodSecure(); |
| boolean unlocked = mKeyguardStateController.canDismissLockScreen(); |
| int stateFingerprint = getLoggingFingerprint(mState, |
| isShowing, |
| isOccluded, |
| isBouncerShowing, |
| isSecure, |
| unlocked); |
| if (stateFingerprint != mLastLoggedStateFingerprint) { |
| if (mStatusBarStateLog == null) { |
| mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN); |
| } |
| mMetricsLogger.write(mStatusBarStateLog |
| .setCategory(isBouncerShowing ? MetricsEvent.BOUNCER : MetricsEvent.LOCKSCREEN) |
| .setType(isShowing ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE) |
| .setSubtype(isSecure ? 1 : 0)); |
| EventLogTags.writeSysuiStatusBarState(mState, |
| isShowing ? 1 : 0, |
| isOccluded ? 1 : 0, |
| isBouncerShowing ? 1 : 0, |
| isSecure ? 1 : 0, |
| unlocked ? 1 : 0); |
| mLastLoggedStateFingerprint = stateFingerprint; |
| |
| StringBuilder uiEventValueBuilder = new StringBuilder(); |
| uiEventValueBuilder.append(isBouncerShowing ? "BOUNCER" : "LOCKSCREEN"); |
| uiEventValueBuilder.append(isShowing ? "_OPEN" : "_CLOSE"); |
| uiEventValueBuilder.append(isSecure ? "_SECURE" : "_INSECURE"); |
| sUiEventLogger.log(StatusBarUiEvent.valueOf(uiEventValueBuilder.toString())); |
| } |
| } |
| |
| /** |
| * Returns a fingerprint of fields logged to eventlog |
| */ |
| private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing, |
| boolean keyguardOccluded, boolean bouncerShowing, boolean secure, |
| boolean currentlyInsecure) { |
| // Reserve 8 bits for statusBarState. We'll never go higher than |
| // that, right? Riiiight. |
| return (statusBarState & 0xFF) |
| | ((keyguardShowing ? 1 : 0) << 8) |
| | ((keyguardOccluded ? 1 : 0) << 9) |
| | ((bouncerShowing ? 1 : 0) << 10) |
| | ((secure ? 1 : 0) << 11) |
| | ((currentlyInsecure ? 1 : 0) << 12); |
| } |
| |
| @Override |
| public void postQSRunnableDismissingKeyguard(final Runnable runnable) { |
| mMainExecutor.execute(() -> { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); |
| executeRunnableDismissingKeyguard( |
| () -> mMainExecutor.execute(runnable), null, false, false, false); |
| }); |
| } |
| |
| @Override |
| public void postStartActivityDismissingKeyguard(PendingIntent intent) { |
| postStartActivityDismissingKeyguard(intent, null /* animationController */); |
| } |
| |
| @Override |
| public void postStartActivityDismissingKeyguard(final PendingIntent intent, |
| @Nullable ActivityLaunchAnimator.Controller animationController) { |
| mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent, |
| null /* intentSentUiThreadCallback */, animationController)); |
| } |
| |
| @Override |
| public void postStartActivityDismissingKeyguard(final Intent intent, int delay) { |
| postStartActivityDismissingKeyguard(intent, delay, null /* animationController */); |
| } |
| |
| @Override |
| public void postStartActivityDismissingKeyguard(Intent intent, int delay, |
| @Nullable ActivityLaunchAnimator.Controller animationController) { |
| mMainExecutor.executeDelayed( |
| () -> |
| startActivityDismissingKeyguard(intent, true /* onlyProvisioned */, |
| true /* dismissShade */, |
| false /* disallowEnterPictureInPictureWhileLaunching */, |
| null /* callback */, |
| 0 /* flags */, |
| animationController, |
| getActivityUserHandle(intent)), |
| delay); |
| } |
| |
| @Override |
| public void showKeyguard() { |
| mStatusBarStateController.setKeyguardRequested(true); |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); |
| updateIsKeyguard(); |
| mAssistManagerLazy.get().onLockscreenShown(); |
| } |
| |
| @Override |
| public boolean hideKeyguard() { |
| mStatusBarStateController.setKeyguardRequested(false); |
| return updateIsKeyguard(); |
| } |
| |
| @Override |
| public boolean updateIsKeyguard() { |
| return updateIsKeyguard(false /* forceStateChange */); |
| } |
| |
| @Override |
| public boolean updateIsKeyguard(boolean forceStateChange) { |
| boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock(); |
| |
| // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise |
| // there's no surface we can show to the user. Note that the device goes fully interactive |
| // late in the transition, so we also allow the device to start dozing once the screen has |
| // turned off fully. |
| boolean keyguardForDozing = mDozeServiceHost.getDozingRequested() |
| && (!mDeviceInteractive || (isGoingToSleep() |
| && (isScreenFullyOff() |
| || (mKeyguardStateController.isShowing() && !isOccluded())))); |
| boolean isWakingAndOccluded = isOccluded() && isWakingOrAwake(); |
| boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested() |
| || keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded; |
| if (keyguardForDozing) { |
| updatePanelExpansionForKeyguard(); |
| } |
| if (shouldBeKeyguard) { |
| if (mScreenOffAnimationController.isKeyguardShowDelayed() |
| || (isGoingToSleep() |
| && mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_OFF)) { |
| // Delay showing the keyguard until screen turned off. |
| } else { |
| showKeyguardImpl(); |
| } |
| } else { |
| // During folding a foldable device this might be called as a result of |
| // 'onScreenTurnedOff' call for the inner display. |
| // In this case: |
| // * When phone is locked on folding: it doesn't make sense to hide keyguard as it |
| // will be immediately locked again |
| // * When phone is unlocked: we still don't want to execute hiding of the keyguard |
| // as the animation could prepare 'fake AOD' interface (without actually |
| // transitioning to keyguard state) and this might reset the view states |
| if (!mScreenOffAnimationController.isKeyguardHideDelayed()) { |
| return hideKeyguardImpl(forceStateChange); |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void showKeyguardImpl() { |
| Trace.beginSection("CentralSurfaces#showKeyguard"); |
| if (mKeyguardStateController.isLaunchTransitionFadingAway()) { |
| mNotificationPanelViewController.cancelAnimation(); |
| onLaunchTransitionFadingEnded(); |
| } |
| mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); |
| if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) { |
| mStatusBarStateController.setState(StatusBarState.KEYGUARD); |
| } |
| updatePanelExpansionForKeyguard(); |
| Trace.endSection(); |
| } |
| |
| private void updatePanelExpansionForKeyguard() { |
| if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode() |
| != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) { |
| mShadeController.instantExpandNotificationsPanel(); |
| } |
| } |
| |
| private void onLaunchTransitionFadingEnded() { |
| mNotificationPanelViewController.resetAlpha(); |
| mNotificationPanelViewController.onAffordanceLaunchEnded(); |
| releaseGestureWakeLock(); |
| runLaunchTransitionEndRunnable(); |
| mKeyguardStateController.setLaunchTransitionFadingAway(false); |
| mPresenter.updateMediaMetaData(true /* metaDataChanged */, true); |
| } |
| |
| @Override |
| public boolean isInLaunchTransition() { |
| return mNotificationPanelViewController.isLaunchTransitionFinished(); |
| } |
| |
| /** |
| * Fades the content of the keyguard away after the launch transition is done. |
| * |
| * @param beforeFading the runnable to be run when the circle is fully expanded and the fading |
| * starts |
| * @param endRunnable the runnable to be run when the transition is done. Will not run |
| * if the transition is cancelled, instead cancelRunnable will run |
| * @param cancelRunnable the runnable to be run if the transition is cancelled |
| */ |
| @Override |
| public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, |
| Runnable endRunnable, Runnable cancelRunnable) { |
| mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); |
| mLaunchTransitionEndRunnable = endRunnable; |
| mLaunchTransitionCancelRunnable = cancelRunnable; |
| Runnable hideRunnable = () -> { |
| mKeyguardStateController.setLaunchTransitionFadingAway(true); |
| if (beforeFading != null) { |
| beforeFading.run(); |
| } |
| updateScrimController(); |
| mPresenter.updateMediaMetaData(false, true); |
| mNotificationPanelViewController.resetAlpha(); |
| mNotificationPanelViewController.fadeOut( |
| FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION, |
| this::onLaunchTransitionFadingEnded); |
| mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(), |
| LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); |
| }; |
| hideRunnable.run(); |
| } |
| |
| private void cancelAfterLaunchTransitionRunnables() { |
| if (mLaunchTransitionCancelRunnable != null) { |
| mLaunchTransitionCancelRunnable.run(); |
| } |
| mLaunchTransitionEndRunnable = null; |
| mLaunchTransitionCancelRunnable = null; |
| } |
| |
| /** |
| * Fades the content of the Keyguard while we are dozing and makes it invisible when finished |
| * fading. |
| */ |
| @Override |
| public void fadeKeyguardWhilePulsing() { |
| mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING, |
| ()-> { |
| hideKeyguard(); |
| mStatusBarKeyguardViewManager.onKeyguardFadedAway(); |
| }).start(); |
| } |
| |
| /** |
| * Plays the animation when an activity that was occluding Keyguard goes away. |
| */ |
| @Override |
| public void animateKeyguardUnoccluding() { |
| mNotificationPanelViewController.setExpandedFraction(0f); |
| mCommandQueueCallbacks.animateExpandNotificationsPanel(); |
| mScrimController.setUnocclusionAnimationRunning(true); |
| } |
| |
| /** |
| * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that |
| * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen |
| * because the launched app crashed or something else went wrong. |
| */ |
| @Override |
| public void startLaunchTransitionTimeout() { |
| mMessageRouter.sendMessageDelayed( |
| MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS); |
| } |
| |
| private void onLaunchTransitionTimeout() { |
| Log.w(TAG, "Launch transition: Timeout!"); |
| mNotificationPanelViewController.onAffordanceLaunchEnded(); |
| releaseGestureWakeLock(); |
| mNotificationPanelViewController.resetViews(false /* animate */); |
| } |
| |
| private void runLaunchTransitionEndRunnable() { |
| mLaunchTransitionCancelRunnable = null; |
| if (mLaunchTransitionEndRunnable != null) { |
| Runnable r = mLaunchTransitionEndRunnable; |
| |
| // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again, |
| // which would lead to infinite recursion. Protect against it. |
| mLaunchTransitionEndRunnable = null; |
| r.run(); |
| } |
| } |
| |
| /** |
| * @return true if we would like to stay in the shade, false if it should go away entirely |
| */ |
| @Override |
| public boolean hideKeyguardImpl(boolean forceStateChange) { |
| Trace.beginSection("CentralSurfaces#hideKeyguard"); |
| boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); |
| int previousState = mStatusBarStateController.getState(); |
| if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) { |
| //TODO: StatusBarStateController should probably know about hiding the keyguard and |
| // notify listeners. |
| |
| // If the state didn't change, we may still need to update public mode |
| mLockscreenUserManager.updatePublicMode(); |
| } |
| if (mStatusBarStateController.leaveOpenOnKeyguardHide()) { |
| if (!mStatusBarStateController.isKeyguardRequested()) { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); |
| } |
| long delay = mKeyguardStateController.calculateGoingToFullShadeDelay(); |
| mLockscreenShadeTransitionController.onHideKeyguard(delay, previousState); |
| |
| // Disable layout transitions in navbar for this transition because the load is just |
| // too heavy for the CPU and GPU on any device. |
| mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay); |
| } else if (!mNotificationPanelViewController.isCollapsing()) { |
| instantCollapseNotificationPanel(); |
| } |
| |
| // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile |
| // visibilities so next time we open the panel we know the correct height already. |
| if (mQSPanelController != null) { |
| mQSPanelController.refreshAllTiles(); |
| } |
| mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); |
| releaseGestureWakeLock(); |
| mNotificationPanelViewController.onAffordanceLaunchEnded(); |
| mNotificationPanelViewController.resetAlpha(); |
| mNotificationPanelViewController.resetTranslation(); |
| mNotificationPanelViewController.resetViewGroupFade(); |
| updateDozingState(); |
| updateScrimController(); |
| Trace.endSection(); |
| return staying; |
| } |
| |
| private void releaseGestureWakeLock() { |
| if (mGestureWakeLock.isHeld()) { |
| mGestureWakeLock.release(); |
| } |
| } |
| |
| /** |
| * Notifies the status bar that Keyguard is going away very soon. |
| */ |
| @Override |
| public void keyguardGoingAway() { |
| // Treat Keyguard exit animation as an app transition to achieve nice transition for status |
| // bar. |
| mKeyguardStateController.notifyKeyguardGoingAway(true); |
| mCommandQueue.appTransitionPending(mDisplayId, true /* forced */); |
| updateScrimController(); |
| } |
| |
| /** |
| * Notifies the status bar the Keyguard is fading away with the specified timings. |
| * @param startTime the start time of the animations in uptime millis |
| * @param delay the precalculated animation delay in milliseconds |
| * @param fadeoutDuration the duration of the exit animation, in milliseconds |
| */ |
| @Override |
| public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) { |
| mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration |
| - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, |
| LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); |
| mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */); |
| mCommandQueue.appTransitionStarting(mDisplayId, |
| startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, |
| LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); |
| mKeyguardStateController.notifyKeyguardFadingAway(delay, fadeoutDuration); |
| } |
| |
| /** |
| * Notifies that the Keyguard fading away animation is done. |
| */ |
| @Override |
| public void finishKeyguardFadingAway() { |
| mKeyguardStateController.notifyKeyguardDoneFading(); |
| mScrimController.setExpansionAffectsAlpha(true); |
| |
| // If the device was re-locked while unlocking, we might have a pending lock that was |
| // delayed because the keyguard was in the middle of going away. |
| mKeyguardViewMediator.maybeHandlePendingLock(); |
| } |
| |
| /** |
| * Switches theme from light to dark and vice-versa. |
| */ |
| protected void updateTheme() { |
| // Set additional scrim only if the lock and system wallpaper are different to prevent |
| // applying the dimming effect twice. |
| mUiBgExecutor.execute(() -> { |
| float dimAmount = 0f; |
| if (mWallpaperManager.lockScreenWallpaperExists()) { |
| dimAmount = mWallpaperManager.getWallpaperDimAmount(); |
| } |
| final float scrimDimAmount = dimAmount; |
| mMainExecutor.execute(() -> { |
| mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount); |
| mScrimController.applyCompositeAlphaOnScrimBehindKeyguard(); |
| }); |
| }); |
| |
| // Lock wallpaper defines the color of the majority of the views, hence we'll use it |
| // to set our default theme. |
| final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); |
| final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper |
| : R.style.Theme_SystemUI; |
| if (mContext.getThemeResId() != themeResId) { |
| mContext.setTheme(themeResId); |
| mConfigurationController.notifyThemeChanged(); |
| } |
| } |
| |
| private void updateDozingState() { |
| Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0); |
| Trace.beginSection("CentralSurfaces#updateDozingState"); |
| |
| boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing() |
| && !mStatusBarKeyguardViewManager.isOccluded(); |
| // If we're dozing and we'll be animating the screen off, the keyguard isn't currently |
| // visible but will be shortly for the animation, so we should proceed as if it's visible. |
| boolean visibleNotOccludedOrWillBe = |
| visibleNotOccluded || (mDozing && mDozeParameters.shouldDelayKeyguardShow()); |
| |
| boolean wakeAndUnlock = mBiometricUnlockController.getMode() |
| == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; |
| boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock) |
| || (mDozing && mDozeParameters.shouldControlScreenOff() |
| && visibleNotOccludedOrWillBe); |
| |
| mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation); |
| updateQsExpansionEnabled(); |
| Trace.endSection(); |
| } |
| |
| @Override |
| public void userActivity() { |
| if (mState == StatusBarState.KEYGUARD) { |
| mKeyguardViewMediatorCallback.userActivity(); |
| } |
| } |
| |
| @Override |
| public boolean interceptMediaKey(KeyEvent event) { |
| return mState == StatusBarState.KEYGUARD |
| && mStatusBarKeyguardViewManager.interceptMediaKey(event); |
| } |
| |
| /** |
| * While IME is active and a BACK event is detected, check with |
| * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event |
| * should be handled before routing to IME, in order to prevent the user having to hit back |
| * twice to exit bouncer. |
| */ |
| @Override |
| public boolean dispatchKeyEventPreIme(KeyEvent event) { |
| switch (event.getKeyCode()) { |
| case KeyEvent.KEYCODE_BACK: |
| if (mState == StatusBarState.KEYGUARD |
| && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) { |
| return onBackPressed(); |
| } |
| } |
| return false; |
| } |
| |
| protected boolean shouldUnlockOnMenuPressed() { |
| return mDeviceInteractive && mState != StatusBarState.SHADE |
| && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed(); |
| } |
| |
| @Override |
| public boolean onMenuPressed() { |
| if (shouldUnlockOnMenuPressed()) { |
| mShadeController.animateCollapsePanels( |
| CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void endAffordanceLaunch() { |
| releaseGestureWakeLock(); |
| mNotificationPanelViewController.onAffordanceLaunchEnded(); |
| } |
| |
| @Override |
| public boolean onBackPressed() { |
| final boolean isScrimmedBouncer = |
| mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; |
| final boolean isBouncerOverDream = isBouncerShowingOverDream(); |
| |
| if (mStatusBarKeyguardViewManager.onBackPressed( |
| isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) { |
| if (isScrimmedBouncer || isBouncerOverDream) { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); |
| } else { |
| mNotificationPanelViewController.expandWithoutQs(); |
| } |
| return true; |
| } |
| if (mNotificationPanelViewController.isQsCustomizing()) { |
| mNotificationPanelViewController.closeQsCustomizer(); |
| return true; |
| } |
| if (mNotificationPanelViewController.isQsExpanded()) { |
| if (mNotificationPanelViewController.isQsDetailShowing()) { |
| mNotificationPanelViewController.closeQsDetail(); |
| } else { |
| mNotificationPanelViewController.animateCloseQs(false /* animateAway */); |
| } |
| return true; |
| } |
| if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) { |
| return true; |
| } |
| if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED |
| && !isBouncerOverDream) { |
| if (mNotificationPanelViewController.canPanelBeCollapsed()) { |
| mShadeController.animateCollapsePanels(); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean onSpacePressed() { |
| if (mDeviceInteractive && mState != StatusBarState.SHADE) { |
| mShadeController.animateCollapsePanels( |
| CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */); |
| return true; |
| } |
| return false; |
| } |
| |
| private void showBouncerOrLockScreenIfKeyguard() { |
| // If the keyguard is animating away, we aren't really the keyguard anymore and should not |
| // show the bouncer/lockscreen. |
| if (!mKeyguardViewMediator.isHiding() |
| && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { |
| if (mState == StatusBarState.SHADE_LOCKED |
| && mKeyguardUpdateMonitor.isUdfpsEnrolled()) { |
| // shade is showing while locked on the keyguard, so go back to showing the |
| // lock screen where users can use the UDFPS affordance to enter the device |
| mStatusBarKeyguardViewManager.reset(true); |
| } else if ((mState == StatusBarState.KEYGUARD |
| && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) |
| || mState == StatusBarState.SHADE_LOCKED) { |
| mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */); |
| } |
| } |
| } |
| |
| /** |
| * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding. |
| * @param performAction the action to perform when the bouncer is dismissed. |
| * @param cancelAction the action to perform when unlock is aborted. |
| */ |
| @Override |
| public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction, |
| Runnable cancelAction) { |
| if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) |
| && !mKeyguardViewMediator.isHiding()) { |
| mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction, |
| false /* afterKeyguardGone */); |
| } else if (cancelAction != null) { |
| cancelAction.run(); |
| } |
| } |
| |
| @Override |
| public void instantCollapseNotificationPanel() { |
| mNotificationPanelViewController.instantCollapse(); |
| mShadeController.runPostCollapseRunnables(); |
| } |
| |
| /** |
| * Collapse the panel directly if we are on the main thread, post the collapsing on the main |
| * thread if we are not. |
| */ |
| @Override |
| public void collapsePanelOnMainThread() { |
| if (Looper.getMainLooper().isCurrentThread()) { |
| mShadeController.collapsePanel(); |
| } else { |
| mContext.getMainExecutor().execute(mShadeController::collapsePanel); |
| } |
| } |
| |
| /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */ |
| @Override |
| public void collapsePanelWithDuration(int duration) { |
| mNotificationPanelViewController.collapseWithDuration(duration); |
| } |
| |
| /** |
| * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example, |
| * from the power button). |
| * @param wakingUp Whether we're updating because we're waking up (true) or going to sleep |
| * (false). |
| */ |
| private void updateRevealEffect(boolean wakingUp) { |
| if (mLightRevealScrim == null) { |
| return; |
| } |
| |
| final boolean wakingUpFromPowerButton = wakingUp |
| && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal) |
| && mWakefulnessLifecycle.getLastWakeReason() |
| == PowerManager.WAKE_REASON_POWER_BUTTON; |
| final boolean sleepingFromPowerButton = !wakingUp |
| && mWakefulnessLifecycle.getLastSleepReason() |
| == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON; |
| |
| if (wakingUpFromPowerButton || sleepingFromPowerButton) { |
| mLightRevealScrim.setRevealEffect(mPowerButtonReveal); |
| mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount()); |
| } else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { |
| // If we're going to sleep, but it's not from the power button, use the default reveal. |
| // If we're waking up, only use the default reveal if the biometric controller didn't |
| // already set it to the circular reveal because we're waking up from a fingerprint/face |
| // auth. |
| mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); |
| mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount()); |
| } |
| } |
| |
| @Override |
| public LightRevealScrim getLightRevealScrim() { |
| return mLightRevealScrim; |
| } |
| |
| @Override |
| public void onTrackingStarted() { |
| mShadeController.runPostCollapseRunnables(); |
| } |
| |
| @Override |
| public void onClosingFinished() { |
| mShadeController.runPostCollapseRunnables(); |
| if (!mPresenter.isPresenterFullyCollapsed()) { |
| // if we set it not to be focusable when collapsing, we have to undo it when we aborted |
| // the closing |
| mNotificationShadeWindowController.setNotificationShadeFocusable(true); |
| } |
| } |
| |
| @Override |
| public void onUnlockHintStarted() { |
| mFalsingCollector.onUnlockHintStarted(); |
| mKeyguardIndicationController.showActionToUnlock(); |
| } |
| |
| @Override |
| public void onHintFinished() { |
| // Delay the reset a bit so the user can read the text. |
| mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); |
| } |
| |
| @Override |
| public void onTrackingStopped(boolean expand) { |
| } |
| |
| // TODO: Figure out way to remove these. |
| @Override |
| public NavigationBarView getNavigationBarView() { |
| return mNavigationBarController.getNavigationBarView(mDisplayId); |
| } |
| |
| @Override |
| public boolean isOverviewEnabled() { |
| return mNavigationBarController.isOverviewEnabled(mDisplayId); |
| } |
| |
| @Override |
| public void showPinningEnterExitToast(boolean entering) { |
| mNavigationBarController.showPinningEnterExitToast(mDisplayId, entering); |
| } |
| |
| @Override |
| public void showPinningEscapeToast() { |
| mNavigationBarController.showPinningEscapeToast(mDisplayId); |
| } |
| |
| /** |
| * TODO: Remove this method. Views should not be passed forward. Will cause theme issues. |
| * @return bottom area view |
| */ |
| @Override |
| public KeyguardBottomAreaView getKeyguardBottomAreaView() { |
| return mNotificationPanelViewController.getKeyguardBottomAreaView(); |
| } |
| |
| /** |
| * Propagation of the bouncer state, indicating that it's fully visible. |
| */ |
| @Override |
| public void setBouncerShowing(boolean bouncerShowing) { |
| mBouncerShowing = bouncerShowing; |
| mKeyguardBypassController.setBouncerShowing(bouncerShowing); |
| mPulseExpansionHandler.setBouncerShowing(bouncerShowing); |
| setBouncerShowingForStatusBarComponents(bouncerShowing); |
| mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing); |
| mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); |
| if (mBouncerShowing) { |
| wakeUpIfDozing(SystemClock.uptimeMillis(), null, "BOUNCER_VISIBLE"); |
| } |
| updateScrimController(); |
| if (!mBouncerShowing) { |
| updatePanelExpansionForKeyguard(); |
| } |
| } |
| |
| /** |
| * Sets whether the bouncer over dream is showing. Note that the bouncer over dream is handled |
| * independently of the rest of the notification panel. As a result, setting this state via |
| * {@link #setBouncerShowing(boolean)} leads to unintended side effects from states modified |
| * behind the dream. |
| */ |
| @Override |
| public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) { |
| mBouncerShowingOverDream = bouncerShowingOverDream; |
| } |
| |
| /** |
| * Propagate the bouncer state to status bar components. |
| * |
| * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and |
| * should update only the status bar components. |
| */ |
| private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) { |
| int importance = bouncerShowing |
| ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS |
| : IMPORTANT_FOR_ACCESSIBILITY_AUTO; |
| if (mPhoneStatusBarViewController != null) { |
| mPhoneStatusBarViewController.setImportantForAccessibility(importance); |
| } |
| mNotificationPanelViewController.setImportantForAccessibility(importance); |
| mNotificationPanelViewController.setBouncerShowing(bouncerShowing); |
| } |
| |
| /** |
| * Collapses the notification shade if it is tracking or expanded. |
| */ |
| @Override |
| public void collapseShade() { |
| if (mNotificationPanelViewController.isTracking()) { |
| mNotificationShadeWindowViewController.cancelCurrentTouch(); |
| } |
| if (mPanelExpanded && mState == StatusBarState.SHADE) { |
| mShadeController.animateCollapsePanels(); |
| } |
| } |
| |
| @VisibleForTesting |
| final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { |
| @Override |
| public void onFinishedGoingToSleep() { |
| mNotificationPanelViewController.onAffordanceLaunchEnded(); |
| releaseGestureWakeLock(); |
| mLaunchCameraWhenFinishedWaking = false; |
| mDeviceInteractive = false; |
| mWakeUpComingFromTouch = false; |
| mWakeUpTouchLocation = null; |
| updateVisibleToUser(); |
| |
| updateNotificationPanelTouchState(); |
| mNotificationShadeWindowViewController.cancelCurrentTouch(); |
| if (mLaunchCameraOnFinishedGoingToSleep) { |
| mLaunchCameraOnFinishedGoingToSleep = false; |
| |
| // This gets executed before we will show Keyguard, so post it in order that the state |
| // is correct. |
| mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected( |
| mLastCameraLaunchSource)); |
| } |
| |
| if (mLaunchEmergencyActionOnFinishedGoingToSleep) { |
| mLaunchEmergencyActionOnFinishedGoingToSleep = false; |
| |
| // This gets executed before we will show Keyguard, so post it in order that the |
| // state is correct. |
| mMainExecutor.execute( |
| () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected()); |
| } |
| updateIsKeyguard(); |
| } |
| |
| @Override |
| public void onStartedGoingToSleep() { |
| String tag = "CentralSurfaces#onStartedGoingToSleep"; |
| DejankUtils.startDetectingBlockingIpcs(tag); |
| |
| // cancel stale runnables that could put the device in the wrong state |
| cancelAfterLaunchTransitionRunnables(); |
| |
| updateRevealEffect(false /* wakingUp */); |
| updateNotificationPanelTouchState(); |
| maybeEscalateHeadsUp(); |
| dismissVolumeDialog(); |
| mWakeUpCoordinator.setFullyAwake(false); |
| mKeyguardBypassController.onStartedGoingToSleep(); |
| |
| // The unlocked screen off and fold to aod animations might use our LightRevealScrim - |
| // we need to be expanded for it to be visible. |
| if (mDozeParameters.shouldShowLightRevealScrim()) { |
| makeExpandedVisible(true); |
| } |
| |
| DejankUtils.stopDetectingBlockingIpcs(tag); |
| } |
| |
| @Override |
| public void onStartedWakingUp() { |
| String tag = "CentralSurfaces#onStartedWakingUp"; |
| DejankUtils.startDetectingBlockingIpcs(tag); |
| mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { |
| mDeviceInteractive = true; |
| mWakeUpCoordinator.setWakingUp(true); |
| if (!mKeyguardBypassController.getBypassEnabled()) { |
| mHeadsUpManager.releaseAllImmediately(); |
| } |
| updateVisibleToUser(); |
| updateIsKeyguard(); |
| mDozeServiceHost.stopDozing(); |
| // This is intentionally below the stopDozing call above, since it avoids that we're |
| // unnecessarily animating the wakeUp transition. Animations should only be enabled |
| // once we fully woke up. |
| updateRevealEffect(true /* wakingUp */); |
| updateNotificationPanelTouchState(); |
| |
| // If we are waking up during the screen off animation, we should undo making the |
| // expanded visible (we did that so the LightRevealScrim would be visible). |
| if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) { |
| makeExpandedInvisible(); |
| } |
| |
| }); |
| DejankUtils.stopDetectingBlockingIpcs(tag); |
| } |
| |
| @Override |
| public void onFinishedWakingUp() { |
| mWakeUpCoordinator.setFullyAwake(true); |
| mWakeUpCoordinator.setWakingUp(false); |
| if (mLaunchCameraWhenFinishedWaking) { |
| mNotificationPanelViewController.launchCamera(mLastCameraLaunchSource); |
| mLaunchCameraWhenFinishedWaking = false; |
| } |
| if (mLaunchEmergencyActionWhenFinishedWaking) { |
| mLaunchEmergencyActionWhenFinishedWaking = false; |
| Intent emergencyIntent = getEmergencyActionIntent(); |
| if (emergencyIntent != null) { |
| mContext.startActivityAsUser(emergencyIntent, |
| getActivityUserHandle(emergencyIntent)); |
| } |
| } |
| updateScrimController(); |
| } |
| }; |
| |
| /** |
| * We need to disable touch events because these might |
| * collapse the panel after we expanded it, and thus we would end up with a blank |
| * Keyguard. |
| */ |
| @Override |
| public void updateNotificationPanelTouchState() { |
| boolean goingToSleepWithoutAnimation = isGoingToSleep() |
| && !mDozeParameters.shouldControlScreenOff(); |
| boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing()) |
| || goingToSleepWithoutAnimation; |
| mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled); |
| mNotificationIconAreaController.setAnimationsEnabled(!disabled); |
| } |
| |
| final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { |
| @Override |
| public void onScreenTurningOn(Runnable onDrawn) { |
| mFalsingCollector.onScreenTurningOn(); |
| mNotificationPanelViewController.onScreenTurningOn(); |
| } |
| |
| @Override |
| public void onScreenTurnedOn() { |
| mScrimController.onScreenTurnedOn(); |
| } |
| |
| @Override |
| public void onScreenTurnedOff() { |
| Trace.beginSection("CentralSurfaces#onScreenTurnedOff"); |
| mFalsingCollector.onScreenOff(); |
| mScrimController.onScreenTurnedOff(); |
| if (mCloseQsBeforeScreenOff) { |
| mNotificationPanelViewController.closeQs(); |
| mCloseQsBeforeScreenOff = false; |
| } |
| updateIsKeyguard(); |
| Trace.endSection(); |
| } |
| }; |
| |
| @Override |
| public int getWakefulnessState() { |
| return mWakefulnessLifecycle.getWakefulness(); |
| } |
| |
| /** |
| * @return true if the screen is currently fully off, i.e. has finished turning off and has |
| * since not started turning on. |
| */ |
| @Override |
| public boolean isScreenFullyOff() { |
| return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF; |
| } |
| |
| @Override |
| public void showScreenPinningRequest(int taskId, boolean allowCancel) { |
| mScreenPinningRequest.showPrompt(taskId, allowCancel); |
| } |
| |
| @Nullable |
| @Override |
| public Intent getEmergencyActionIntent() { |
| Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY); |
| PackageManager pm = mContext.getPackageManager(); |
| List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent, |
| PackageManager.MATCH_SYSTEM_ONLY); |
| ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities); |
| if (resolveInfo == null) { |
| Log.wtf(TAG, "Couldn't find an app to process the emergency intent."); |
| return null; |
| } |
| emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, |
| resolveInfo.activityInfo.name)); |
| emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| return emergencyIntent; |
| } |
| |
| /** |
| * Select and return the "best" ResolveInfo for Emergency SOS Activity. |
| */ |
| private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) { |
| // No matched activity. |
| if (emergencyActivities == null || emergencyActivities.isEmpty()) { |
| return null; |
| } |
| |
| // Of multiple matched Activities, give preference to the pre-set package name. |
| String preferredAppPackageName = |
| mContext.getString(R.string.config_preferredEmergencySosPackage); |
| |
| // If there is no preferred app, then return first match. |
| if (TextUtils.isEmpty(preferredAppPackageName)) { |
| return emergencyActivities.get(0); |
| } |
| |
| for (ResolveInfo emergencyInfo: emergencyActivities) { |
| // If activity is from the preferred app, use it. |
| if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) { |
| return emergencyInfo; |
| } |
| } |
| // No matching activity: return first match |
| return emergencyActivities.get(0); |
| } |
| |
| @Override |
| public boolean isCameraAllowedByAdmin() { |
| if (mDevicePolicyManager.getCameraDisabled(null, |
| mLockscreenUserManager.getCurrentUserId())) { |
| return false; |
| } else if (mStatusBarKeyguardViewManager == null |
| || (isKeyguardShowing() && isKeyguardSecure())) { |
| // Check if the admin has disabled the camera specifically for the keyguard |
| return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, |
| mLockscreenUserManager.getCurrentUserId()) |
| & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isGoingToSleep() { |
| return mWakefulnessLifecycle.getWakefulness() |
| == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP; |
| } |
| |
| boolean isWakingOrAwake() { |
| return mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_WAKING |
| || mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_AWAKE; |
| } |
| |
| @Override |
| public void notifyBiometricAuthModeChanged() { |
| mDozeServiceHost.updateDozing(); |
| updateScrimController(); |
| } |
| |
| /** |
| * Set the amount of progress we are currently in if we're transitioning to the full shade. |
| * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full |
| * shade. |
| */ |
| @Override |
| public void setTransitionToFullShadeProgress(float transitionToFullShadeProgress) { |
| mTransitionToFullShadeProgress = transitionToFullShadeProgress; |
| } |
| |
| /** |
| * Sets the amount of progress to the bouncer being fully hidden/visible. 1 means the bouncer |
| * is fully hidden, while 0 means the bouncer is visible. |
| */ |
| @Override |
| public void setBouncerHiddenFraction(float expansion) { |
| mScrimController.setBouncerHiddenFraction(expansion); |
| } |
| |
| @Override |
| @VisibleForTesting |
| public void updateScrimController() { |
| Trace.beginSection("CentralSurfaces#updateScrimController"); |
| |
| boolean unlocking = mKeyguardStateController.isShowing() && ( |
| mBiometricUnlockController.isWakeAndUnlock() |
| || mKeyguardStateController.isKeyguardFadingAway() |
| || mKeyguardStateController.isKeyguardGoingAway() |
| || mKeyguardViewMediator.requestedShowSurfaceBehindKeyguard() |
| || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind()); |
| |
| mScrimController.setExpansionAffectsAlpha(!unlocking); |
| |
| boolean launchingAffordanceWithPreview = |
| mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); |
| mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); |
| |
| if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { |
| if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED |
| || mTransitionToFullShadeProgress > 0f) { |
| mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); |
| } else { |
| mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); |
| } |
| } else if (mBouncerShowing && !unlocking) { |
| // Bouncer needs the front scrim when it's on top of an activity, |
| // tapping on a notification, editing QS or being dismissed by |
| // FLAG_DISMISS_KEYGUARD_ACTIVITY. |
| ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() |
| ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; |
| mScrimController.transitionTo(state); |
| } else if (launchingAffordanceWithPreview) { |
| // We want to avoid animating when launching with a preview. |
| mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); |
| } else if (mBrightnessMirrorVisible) { |
| mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); |
| } else if (mState == StatusBarState.SHADE_LOCKED) { |
| mScrimController.transitionTo(ScrimState.SHADE_LOCKED); |
| } else if (mDozeServiceHost.isPulsing()) { |
| mScrimController.transitionTo(ScrimState.PULSING, |
| mDozeScrimController.getScrimCallback()); |
| } else if (mDozeServiceHost.hasPendingScreenOffCallback()) { |
| mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() { |
| @Override |
| public void onFinished() { |
| mDozeServiceHost.executePendingScreenOffCallback(); |
| } |
| }); |
| } else if (mDozing && !unlocking) { |
| mScrimController.transitionTo(ScrimState.AOD); |
| } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) { |
| mScrimController.transitionTo(ScrimState.KEYGUARD); |
| } else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming() |
| && !unlocking) { |
| mScrimController.transitionTo(ScrimState.DREAMING); |
| } else { |
| mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); |
| } |
| updateLightRevealScrimVisibility(); |
| |
| Trace.endSection(); |
| } |
| |
| @Override |
| public boolean isKeyguardShowing() { |
| if (mStatusBarKeyguardViewManager == null) { |
| Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true"); |
| return true; |
| } |
| return mStatusBarKeyguardViewManager.isShowing(); |
| } |
| |
| @Override |
| public boolean shouldIgnoreTouch() { |
| return (mStatusBarStateController.isDozing() |
| && mDozeServiceHost.getIgnoreTouchWhilePulsing()) |
| || mScreenOffAnimationController.shouldIgnoreKeyguardTouches(); |
| } |
| |
| // Begin Extra BaseStatusBar methods. |
| |
| protected final CommandQueue mCommandQueue; |
| protected IStatusBarService mBarService; |
| |
| // all notifications |
| protected NotificationStackScrollLayout mStackScroller; |
| |
| // handling reordering |
| private final VisualStabilityManager mVisualStabilityManager; |
| |
| protected AccessibilityManager mAccessibilityManager; |
| |
| protected boolean mDeviceInteractive; |
| |
| protected boolean mVisible; |
| |
| // mScreenOnFromKeyguard && mVisible. |
| private boolean mVisibleToUser; |
| |
| protected DevicePolicyManager mDevicePolicyManager; |
| private final PowerManager mPowerManager; |
| protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; |
| |
| protected KeyguardManager mKeyguardManager; |
| private final DeviceProvisionedController mDeviceProvisionedController; |
| |
| private final NavigationBarController mNavigationBarController; |
| private final AccessibilityFloatingMenuController mAccessibilityFloatingMenuController; |
| |
| // UI-specific methods |
| |
| protected WindowManager mWindowManager; |
| protected IWindowManager mWindowManagerService; |
| private final IDreamManager mDreamManager; |
| |
| protected Display mDisplay; |
| private int mDisplayId; |
| |
| protected NotificationShelfController mNotificationShelfController; |
| |
| private final Lazy<AssistManager> mAssistManagerLazy; |
| |
| @Override |
| public boolean isDeviceInteractive() { |
| return mDeviceInteractive; |
| } |
| |
| private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { |
| NotificationManager noMan = (NotificationManager) |
| mContext.getSystemService(Context.NOTIFICATION_SERVICE); |
| noMan.cancel(com.android.internal.messages.nano.SystemMessageProto.SystemMessage. |
| NOTE_HIDDEN_NOTIFICATIONS); |
| |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); |
| if (BANNER_ACTION_SETUP.equals(action)) { |
| mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, |
| true /* force */); |
| mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| |
| ); |
| } |
| } |
| } |
| }; |
| |
| @Override |
| public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) { |
| mNotificationsController.setNotificationSnoozed(sbn, snoozeOption); |
| } |
| |
| |
| @Override |
| public void awakenDreams() { |
| mUiBgExecutor.execute(() -> { |
| try { |
| mDreamManager.awaken(); |
| } catch (RemoteException e) { |
| e.printStackTrace(); |
| } |
| }); |
| } |
| |
| protected void toggleKeyboardShortcuts(int deviceId) { |
| KeyboardShortcuts.toggle(mContext, deviceId); |
| } |
| |
| protected void dismissKeyboardShortcuts() { |
| KeyboardShortcuts.dismiss(); |
| } |
| |
| /** |
| * Dismiss the keyguard then execute an action. |
| * |
| * @param action The action to execute after dismissing the keyguard. |
| * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard. |
| * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if |
| * we are locked. |
| */ |
| private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone, |
| boolean collapsePanel, boolean willAnimateOnKeyguard) { |
| if (!mDeviceProvisionedController.isDeviceProvisioned()) return; |
| |
| OnDismissAction onDismissAction = new OnDismissAction() { |
| @Override |
| public boolean onDismiss() { |
| new Thread(() -> { |
| try { |
| // The intent we are sending is for the application, which |
| // won't have permission to immediately start an activity after |
| // the user switches to home. We know it is safe to do at this |
| // point, so make sure new activity switches are now allowed. |
| ActivityManager.getService().resumeAppSwitches(); |
| } catch (RemoteException e) { |
| } |
| action.run(); |
| }).start(); |
| |
| return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard; |
| } |
| |
| @Override |
| public boolean willRunAnimationOnKeyguard() { |
| return willAnimateOnKeyguard; |
| } |
| }; |
| dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone); |
| } |
| |
| @Override |
| public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { |
| startPendingIntentDismissingKeyguard(intent, null); |
| } |
| |
| @Override |
| public void startPendingIntentDismissingKeyguard( |
| final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) { |
| startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, |
| (ActivityLaunchAnimator.Controller) null); |
| } |
| |
| @Override |
| public void startPendingIntentDismissingKeyguard(PendingIntent intent, |
| Runnable intentSentUiThreadCallback, View associatedView) { |
| ActivityLaunchAnimator.Controller animationController = null; |
| if (associatedView instanceof ExpandableNotificationRow) { |
| animationController = mNotificationAnimationProvider.getAnimatorController( |
| ((ExpandableNotificationRow) associatedView)); |
| } |
| |
| startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, |
| animationController); |
| } |
| |
| @Override |
| public void startPendingIntentDismissingKeyguard( |
| final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback, |
| @Nullable ActivityLaunchAnimator.Controller animationController) { |
| final boolean willLaunchResolverActivity = intent.isActivity() |
| && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), |
| mLockscreenUserManager.getCurrentUserId()); |
| |
| boolean animate = !willLaunchResolverActivity |
| && animationController != null |
| && shouldAnimateLaunch(intent.isActivity()); |
| |
| // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we run |
| // the animation on the keyguard). The animation will take care of (instantly) collapsing |
| // the shade and hiding the keyguard once it is done. |
| boolean collapse = !animate; |
| executeActionDismissingKeyguard(() -> { |
| try { |
| // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the |
| // shade is collapsed after the animation (or when it is cancelled, aborted, etc). |
| ActivityLaunchAnimator.Controller controller = |
| animationController != null ? new StatusBarLaunchAnimatorController( |
| animationController, this, intent.isActivity()) : null; |
| |
| mActivityLaunchAnimator.startPendingIntentWithAnimation( |
| controller, animate, intent.getCreatorPackage(), |
| (animationAdapter) -> { |
| ActivityOptions options = new ActivityOptions( |
| CentralSurfaces.getActivityOptions( |
| mDisplayId, animationAdapter)); |
| // TODO b/221255671: restrict this to only be set for notifications |
| options.setEligibleForLegacyPermissionPrompt(true); |
| return intent.sendAndReturnResult(null, 0, null, null, null, |
| null, options.toBundle()); |
| }); |
| } catch (PendingIntent.CanceledException e) { |
| // the stack trace isn't very helpful here. |
| // Just log the exception message. |
| Log.w(TAG, "Sending intent failed: " + e); |
| if (!collapse) { |
| // executeActionDismissingKeyguard did not collapse for us already. |
| collapsePanelOnMainThread(); |
| } |
| // TODO: Dismiss Keyguard. |
| } |
| if (intent.isActivity()) { |
| mAssistManagerLazy.get().hideAssist(); |
| } |
| if (intentSentUiThreadCallback != null) { |
| postOnUiThread(intentSentUiThreadCallback); |
| } |
| }, willLaunchResolverActivity, collapse, animate); |
| } |
| |
| private void postOnUiThread(Runnable runnable) { |
| mMainExecutor.execute(runnable); |
| } |
| |
| @Override |
| public void visibilityChanged(boolean visible) { |
| if (mVisible != visible) { |
| mVisible = visible; |
| if (!visible) { |
| mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, |
| true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */); |
| } |
| } |
| updateVisibleToUser(); |
| } |
| |
| protected void updateVisibleToUser() { |
| boolean oldVisibleToUser = mVisibleToUser; |
| mVisibleToUser = mVisible && mDeviceInteractive; |
| |
| if (oldVisibleToUser != mVisibleToUser) { |
| handleVisibleToUserChanged(mVisibleToUser); |
| } |
| } |
| |
| /** |
| * Clear Buzz/Beep/Blink. |
| */ |
| @Override |
| public void clearNotificationEffects() { |
| try { |
| mBarService.clearNotificationEffects(); |
| } catch (RemoteException e) { |
| // Won't fail unless the world has ended. |
| } |
| } |
| |
| /** |
| * @return Whether the security bouncer from Keyguard is showing. |
| */ |
| @Override |
| public boolean isBouncerShowing() { |
| return mBouncerShowing; |
| } |
| |
| /** |
| * @return Whether the security bouncer from Keyguard is showing. |
| */ |
| @Override |
| public boolean isBouncerShowingScrimmed() { |
| return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming(); |
| } |
| |
| @Override |
| public boolean isBouncerShowingOverDream() { |
| return mBouncerShowingOverDream; |
| } |
| |
| /** |
| * When {@link KeyguardBouncer} starts to be dismissed, playing its animation. |
| */ |
| @Override |
| public void onBouncerPreHideAnimation() { |
| mNotificationPanelViewController.onBouncerPreHideAnimation(); |
| |
| } |
| |
| @Override |
| public boolean isKeyguardSecure() { |
| if (mStatusBarKeyguardViewManager == null) { |
| // startKeyguard() hasn't been called yet, so we don't know. |
| // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this |
| // value onVisibilityChanged(). |
| Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false", |
| new Throwable()); |
| return false; |
| } |
| return mStatusBarKeyguardViewManager.isSecure(); |
| } |
| @Override |
| public NotificationPanelViewController getPanelController() { |
| return mNotificationPanelViewController; |
| } |
| // End Extra BaseStatusBarMethods. |
| |
| @Override |
| public NotificationGutsManager getGutsManager() { |
| return mGutsManager; |
| } |
| |
| boolean isTransientShown() { |
| return mTransientShown; |
| } |
| |
| private void updateLightRevealScrimVisibility() { |
| if (mLightRevealScrim == null) { |
| // status bar may not be inflated yet |
| return; |
| } |
| |
| mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha()); |
| } |
| |
| @Override |
| public void extendDozePulse(){ |
| mDozeScrimController.extendPulse(); |
| } |
| |
| private final KeyguardUpdateMonitorCallback mUpdateCallback = |
| new KeyguardUpdateMonitorCallback() { |
| @Override |
| public void onDreamingStateChanged(boolean dreaming) { |
| updateScrimController(); |
| if (dreaming) { |
| maybeEscalateHeadsUp(); |
| } |
| } |
| |
| // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by |
| // KeyguardCoordinator |
| @Override |
| public void onStrongAuthStateChanged(int userId) { |
| super.onStrongAuthStateChanged(userId); |
| mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged"); |
| } |
| }; |
| |
| |
| private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener = |
| new FalsingManager.FalsingBeliefListener() { |
| @Override |
| public void onFalse() { |
| // Hides quick settings, bouncer, and quick-quick settings. |
| mStatusBarKeyguardViewManager.reset(true); |
| } |
| }; |
| |
| // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, |
| // this animation is tied to the scrim for historic reasons. |
| // TODO: notify when keyguard has faded away instead of the scrim. |
| private final ScrimController.Callback mUnlockScrimCallback = new ScrimController |
| .Callback() { |
| @Override |
| public void onFinished() { |
| if (mStatusBarKeyguardViewManager == null) { |
| Log.w(TAG, "Tried to notify keyguard visibility when " |
| + "mStatusBarKeyguardViewManager was null"); |
| return; |
| } |
| if (mKeyguardStateController.isKeyguardFadingAway()) { |
| mStatusBarKeyguardViewManager.onKeyguardFadedAway(); |
| } |
| } |
| |
| @Override |
| public void onCancelled() { |
| onFinished(); |
| } |
| }; |
| |
| private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { |
| @Override |
| public void onUserSetupChanged() { |
| final boolean userSetup = mDeviceProvisionedController.isCurrentUserSetup(); |
| Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for " |
| + "current user"); |
| if (MULTIUSER_DEBUG) { |
| Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s", |
| userSetup, mUserSetup)); |
| } |
| |
| if (userSetup != mUserSetup) { |
| mUserSetup = userSetup; |
| if (!mUserSetup) { |
| animateCollapseQuickSettings(); |
| } |
| updateQsExpansionEnabled(); |
| } |
| } |
| }; |
| |
| private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (!mWallpaperSupported) { |
| // Receiver should not have been registered at all... |
| Log.wtf(TAG, "WallpaperManager not supported"); |
| return; |
| } |
| WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); |
| mWallpaperController.onWallpaperInfoUpdated(info); |
| |
| final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_dozeSupportsAodWallpaper); |
| // If WallpaperInfo is null, it must be ImageWallpaper. |
| final boolean supportsAmbientMode = deviceSupportsAodWallpaper |
| && (info != null && info.supportsAmbientMode()); |
| |
| mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); |
| mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); |
| mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode); |
| } |
| }; |
| |
| private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { |
| @Override |
| public void onConfigChanged(Configuration newConfig) { |
| updateResources(); |
| updateDisplaySize(); // populates mDisplayMetrics |
| |
| if (DEBUG) { |
| Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); |
| } |
| |
| if (!mNotifPipelineFlags.isNewPipelineEnabled()) { |
| mViewHierarchyManager.updateRowStates(); |
| } |
| mScreenPinningRequest.onConfigurationChanged(); |
| } |
| |
| @Override |
| public void onDensityOrFontScaleChanged() { |
| // TODO: Remove this. |
| if (mBrightnessMirrorController != null) { |
| mBrightnessMirrorController.onDensityOrFontScaleChanged(); |
| } |
| // TODO: Bring these out of CentralSurfaces. |
| mUserInfoControllerImpl.onDensityOrFontScaleChanged(); |
| mUserSwitcherController.onDensityOrFontScaleChanged(); |
| mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); |
| mHeadsUpManager.onDensityOrFontScaleChanged(); |
| } |
| |
| @Override |
| public void onThemeChanged() { |
| if (mBrightnessMirrorController != null) { |
| mBrightnessMirrorController.onOverlayChanged(); |
| } |
| // We need the new R.id.keyguard_indication_area before recreating |
| // mKeyguardIndicationController |
| mNotificationPanelViewController.onThemeChanged(); |
| |
| if (mStatusBarKeyguardViewManager != null) { |
| mStatusBarKeyguardViewManager.onThemeChanged(); |
| } |
| if (mAmbientIndicationContainer instanceof AutoReinflateContainer) { |
| ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout(); |
| } |
| mNotificationIconAreaController.onThemeChanged(); |
| } |
| |
| @Override |
| public void onUiModeChanged() { |
| if (mBrightnessMirrorController != null) { |
| mBrightnessMirrorController.onUiModeChanged(); |
| } |
| } |
| }; |
| |
| private StatusBarStateController.StateListener mStateListener = |
| new StatusBarStateController.StateListener() { |
| @Override |
| public void onStatePreChange(int oldState, int newState) { |
| // If we're visible and switched to SHADE_LOCKED (the user dragged |
| // down on the lockscreen), clear notification LED, vibration, |
| // ringing. |
| // Other transitions are covered in handleVisibleToUserChanged(). |
| if (mVisible && (newState == StatusBarState.SHADE_LOCKED |
| || mStatusBarStateController.goingToFullShade())) { |
| clearNotificationEffects(); |
| } |
| if (newState == StatusBarState.KEYGUARD) { |
| mRemoteInputManager.onPanelCollapsed(); |
| maybeEscalateHeadsUp(); |
| } |
| } |
| |
| @Override |
| public void onStateChanged(int newState) { |
| mState = newState; |
| updateReportRejectedTouchVisibility(); |
| mDozeServiceHost.updateDozing(); |
| updateTheme(); |
| mNavigationBarController.touchAutoDim(mDisplayId); |
| Trace.beginSection("CentralSurfaces#updateKeyguardState"); |
| if (mState == StatusBarState.KEYGUARD) { |
| mNotificationPanelViewController.cancelPendingPanelCollapse(); |
| } |
| updateDozingState(); |
| checkBarModes(); |
| updateScrimController(); |
| mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); |
| Trace.endSection(); |
| } |
| |
| @Override |
| public void onDozeAmountChanged(float linear, float eased) { |
| if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS) |
| && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { |
| mLightRevealScrim.setRevealAmount(1f - linear); |
| } |
| } |
| |
| @Override |
| public void onDozingChanged(boolean isDozing) { |
| Trace.beginSection("CentralSurfaces#updateDozing"); |
| mDozing = isDozing; |
| |
| // Collapse the notification panel if open |
| boolean dozingAnimated = mDozeServiceHost.getDozingRequested() |
| && mDozeParameters.shouldControlScreenOff(); |
| mNotificationPanelViewController.resetViews(dozingAnimated); |
| |
| updateQsExpansionEnabled(); |
| mKeyguardViewMediator.setDozing(mDozing); |
| |
| mNotificationsController.requestNotificationUpdate("onDozingChanged"); |
| updateDozingState(); |
| mDozeServiceHost.updateDozing(); |
| updateScrimController(); |
| |
| if (mBiometricUnlockController.isWakeAndUnlock()) { |
| // Usually doze changes are to/from lockscreen/AOD, but if we're wake and |
| // unlocking we should hide the keyguard ASAP if necessary. |
| updateIsKeyguard(); |
| } |
| |
| updateReportRejectedTouchVisibility(); |
| Trace.endSection(); |
| } |
| |
| @Override |
| public void onFullscreenStateChanged(boolean isFullscreen) { |
| mIsFullscreen = isFullscreen; |
| maybeUpdateBarMode(); |
| } |
| }; |
| |
| private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback = |
| new BatteryController.BatteryStateChangeCallback() { |
| @Override |
| public void onPowerSaveChanged(boolean isPowerSave) { |
| mMainExecutor.execute(mCheckBarModes); |
| if (mDozeServiceHost != null) { |
| mDozeServiceHost.firePowerSaveChanged(isPowerSave); |
| } |
| } |
| }; |
| |
| private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback = |
| new ActivityLaunchAnimator.Callback() { |
| @Override |
| public boolean isOnKeyguard() { |
| return mKeyguardStateController.isShowing(); |
| } |
| |
| @Override |
| public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) { |
| // We post to the main thread for 2 reasons: |
| // 1. KeyguardViewMediator is not thread-safe. |
| // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before |
| // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur |
| // when doing |
| // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }. |
| mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner)); |
| } |
| |
| @Override |
| public int getBackgroundColor(TaskInfo task) { |
| if (!mStartingSurfaceOptional.isPresent()) { |
| Log.w(TAG, "No starting surface, defaulting to SystemBGColor"); |
| return SplashscreenContentDrawer.getSystemBGColor(); |
| } |
| |
| return mStartingSurfaceOptional.get().getBackgroundColor(task); |
| } |
| }; |
| |
| private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener = |
| new ActivityLaunchAnimator.Listener() { |
| @Override |
| public void onLaunchAnimationStart() { |
| mKeyguardViewMediator.setBlursDisabledForAppLaunch(true); |
| } |
| |
| @Override |
| public void onLaunchAnimationEnd() { |
| mKeyguardViewMediator.setBlursDisabledForAppLaunch(false); |
| } |
| }; |
| |
| private final DemoMode mDemoModeCallback = new DemoMode() { |
| @Override |
| public void onDemoModeFinished() { |
| checkBarModes(); |
| } |
| |
| @Override |
| public void dispatchDemoCommand(String command, Bundle args) { } |
| }; |
| |
| /** |
| * Determines what UserHandle to use when launching an activity. |
| * |
| * We want to ensure that activities that are launched within the systemui process should be |
| * launched as user of the current process. |
| * @param intent |
| * @return UserHandle |
| */ |
| private UserHandle getActivityUserHandle(Intent intent) { |
| String[] packages = mContext.getResources().getStringArray(R.array.system_ui_packages); |
| for (String pkg : packages) { |
| if (intent.getComponent() == null) break; |
| if (pkg.equals(intent.getComponent().getPackageName())) { |
| return new UserHandle(UserHandle.myUserId()); |
| } |
| } |
| return UserHandle.CURRENT; |
| } |
| } |