1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone; 16 17 import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED; 18 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; 19 20 import android.app.KeyguardManager; 21 import android.content.Context; 22 import android.os.PowerManager; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.service.notification.StatusBarNotification; 26 import android.service.vr.IVrManager; 27 import android.service.vr.IVrStateCallbacks; 28 import android.util.Log; 29 import android.util.Slog; 30 import android.view.View; 31 32 import com.android.internal.statusbar.IStatusBarService; 33 import com.android.systemui.InitController; 34 import com.android.systemui.R; 35 import com.android.systemui.dagger.SysUISingleton; 36 import com.android.systemui.plugins.ActivityStarter; 37 import com.android.systemui.plugins.ActivityStarter.OnDismissAction; 38 import com.android.systemui.power.domain.interactor.PowerInteractor; 39 import com.android.systemui.shade.NotificationShadeWindowView; 40 import com.android.systemui.shade.QuickSettingsController; 41 import com.android.systemui.shade.ShadeViewController; 42 import com.android.systemui.statusbar.CommandQueue; 43 import com.android.systemui.statusbar.LockscreenShadeTransitionController; 44 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 45 import com.android.systemui.statusbar.NotificationMediaManager; 46 import com.android.systemui.statusbar.NotificationPresenter; 47 import com.android.systemui.statusbar.NotificationRemoteInputManager; 48 import com.android.systemui.statusbar.NotificationShadeWindowController; 49 import com.android.systemui.statusbar.StatusBarState; 50 import com.android.systemui.statusbar.SysuiStatusBarStateController; 51 import com.android.systemui.statusbar.notification.AboveShelfObserver; 52 import com.android.systemui.statusbar.notification.DynamicPrivacyController; 53 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 54 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; 55 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; 56 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; 57 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; 58 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 59 import com.android.systemui.statusbar.notification.row.NotificationGutsManager; 60 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; 61 import com.android.systemui.statusbar.notification.stack.NotificationListContainer; 62 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 63 import com.android.systemui.statusbar.policy.KeyguardStateController; 64 65 import javax.inject.Inject; 66 67 @SysUISingleton 68 class StatusBarNotificationPresenter implements NotificationPresenter, CommandQueue.Callbacks { 69 private static final String TAG = "StatusBarNotificationPresenter"; 70 71 private final ActivityStarter mActivityStarter; 72 private final KeyguardStateController mKeyguardStateController; 73 private final NotificationLockscreenUserManager mLockscreenUserManager; 74 private final SysuiStatusBarStateController mStatusBarStateController; 75 private final NotifShadeEventSource mNotifShadeEventSource; 76 private final NotificationMediaManager mMediaManager; 77 private final NotificationGutsManager mGutsManager; 78 private final ShadeViewController mNotificationPanel; 79 private final HeadsUpManagerPhone mHeadsUpManager; 80 private final AboveShelfObserver mAboveShelfObserver; 81 private final DozeScrimController mDozeScrimController; 82 private final NotificationsInteractor mNotificationsInteractor; 83 private final NotificationStackScrollLayoutController mNsslController; 84 private final LockscreenShadeTransitionController mShadeTransitionController; 85 private final PowerInteractor mPowerInteractor; 86 private final CommandQueue mCommandQueue; 87 private final KeyguardManager mKeyguardManager; 88 private final NotificationShadeWindowController mNotificationShadeWindowController; 89 private final IStatusBarService mBarService; 90 private final DynamicPrivacyController mDynamicPrivacyController; 91 private final NotificationListContainer mNotifListContainer; 92 private final QuickSettingsController mQsController; 93 94 protected boolean mVrMode; 95 96 @Inject StatusBarNotificationPresenter( Context context, ShadeViewController panel, QuickSettingsController quickSettingsController, HeadsUpManagerPhone headsUp, NotificationShadeWindowView statusBarWindow, ActivityStarter activityStarter, NotificationStackScrollLayoutController stackScrollerController, DozeScrimController dozeScrimController, NotificationShadeWindowController notificationShadeWindowController, DynamicPrivacyController dynamicPrivacyController, KeyguardStateController keyguardStateController, NotificationsInteractor notificationsInteractor, LockscreenShadeTransitionController shadeTransitionController, PowerInteractor powerInteractor, CommandQueue commandQueue, NotificationLockscreenUserManager lockscreenUserManager, SysuiStatusBarStateController sysuiStatusBarStateController, NotifShadeEventSource notifShadeEventSource, NotificationMediaManager notificationMediaManager, NotificationGutsManager notificationGutsManager, InitController initController, NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationRemoteInputManager remoteInputManager, NotificationRemoteInputManager.Callback remoteInputManagerCallback, NotificationListContainer notificationListContainer)97 StatusBarNotificationPresenter( 98 Context context, 99 ShadeViewController panel, 100 QuickSettingsController quickSettingsController, 101 HeadsUpManagerPhone headsUp, 102 NotificationShadeWindowView statusBarWindow, 103 ActivityStarter activityStarter, 104 NotificationStackScrollLayoutController stackScrollerController, 105 DozeScrimController dozeScrimController, 106 NotificationShadeWindowController notificationShadeWindowController, 107 DynamicPrivacyController dynamicPrivacyController, 108 KeyguardStateController keyguardStateController, 109 NotificationsInteractor notificationsInteractor, 110 LockscreenShadeTransitionController shadeTransitionController, 111 PowerInteractor powerInteractor, 112 CommandQueue commandQueue, 113 NotificationLockscreenUserManager lockscreenUserManager, 114 SysuiStatusBarStateController sysuiStatusBarStateController, 115 NotifShadeEventSource notifShadeEventSource, 116 NotificationMediaManager notificationMediaManager, 117 NotificationGutsManager notificationGutsManager, 118 InitController initController, 119 NotificationInterruptStateProvider notificationInterruptStateProvider, 120 NotificationRemoteInputManager remoteInputManager, 121 NotificationRemoteInputManager.Callback remoteInputManagerCallback, 122 NotificationListContainer notificationListContainer) { 123 mActivityStarter = activityStarter; 124 mKeyguardStateController = keyguardStateController; 125 mNotificationPanel = panel; 126 mQsController = quickSettingsController; 127 mHeadsUpManager = headsUp; 128 mDynamicPrivacyController = dynamicPrivacyController; 129 mNotificationsInteractor = notificationsInteractor; 130 mNsslController = stackScrollerController; 131 mShadeTransitionController = shadeTransitionController; 132 mPowerInteractor = powerInteractor; 133 mCommandQueue = commandQueue; 134 mLockscreenUserManager = lockscreenUserManager; 135 mStatusBarStateController = sysuiStatusBarStateController; 136 mNotifShadeEventSource = notifShadeEventSource; 137 mMediaManager = notificationMediaManager; 138 mGutsManager = notificationGutsManager; 139 mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); 140 mNotificationShadeWindowController = notificationShadeWindowController; 141 mAboveShelfObserver.setListener(statusBarWindow.findViewById( 142 R.id.notification_container_parent)); 143 mDozeScrimController = dozeScrimController; 144 mKeyguardManager = context.getSystemService(KeyguardManager.class); 145 mBarService = IStatusBarService.Stub.asInterface( 146 ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 147 mNotifListContainer = notificationListContainer; 148 149 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 150 Context.VR_SERVICE)); 151 if (vrManager != null) { 152 try { 153 vrManager.registerListener(mVrStateCallbacks); 154 } catch (RemoteException e) { 155 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 156 } 157 } 158 remoteInputManager.setUpWithCallback( 159 remoteInputManagerCallback, 160 mNsslController.createDelegate()); 161 162 initController.addPostInitTask(() -> { 163 mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); 164 mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); 165 notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); 166 mLockscreenUserManager.setUpWithPresenter(this); 167 mGutsManager.setUpWithPresenter( 168 this, mNotifListContainer, mOnSettingsClickListener); 169 170 onUserSwitched(mLockscreenUserManager.getCurrentUserId()); 171 }); 172 } 173 174 /** Called when the shade has been emptied to attempt to close the shade */ maybeClosePanelForShadeEmptied()175 private void maybeClosePanelForShadeEmptied() { 176 if (CLOSE_PANEL_WHEN_EMPTIED 177 && !mNotificationPanel.isTracking() 178 && !mQsController.getExpanded() 179 && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED 180 && !isCollapsing()) { 181 mStatusBarStateController.setState(StatusBarState.KEYGUARD); 182 } 183 } 184 185 @Override isCollapsing()186 public boolean isCollapsing() { 187 return mNotificationPanel.isCollapsing() 188 || mNotificationShadeWindowController.isLaunchingActivity(); 189 } 190 maybeEndAmbientPulse()191 private void maybeEndAmbientPulse() { 192 if (mNsslController.getNotificationListContainer().hasPulsingNotifications() 193 && !mHeadsUpManager.hasNotifications()) { 194 // We were showing a pulse for a notification, but no notifications are pulsing anymore. 195 // Finish the pulse. 196 mDozeScrimController.pulseOutNow(); 197 } 198 } 199 200 @Override onUserSwitched(int newUserId)201 public void onUserSwitched(int newUserId) { 202 // Begin old BaseStatusBar.userSwitched 203 mHeadsUpManager.setUser(newUserId); 204 // End old BaseStatusBar.userSwitched 205 mCommandQueue.animateCollapsePanels(); 206 mMediaManager.clearCurrentMediaNotification(); 207 updateMediaMetaData(true, false); 208 } 209 210 @Override onBindRow(ExpandableNotificationRow row)211 public void onBindRow(ExpandableNotificationRow row) { 212 row.setAboveShelfChangedListener(mAboveShelfObserver); 213 row.setSecureStateProvider(mKeyguardStateController::canDismissLockScreen); 214 } 215 216 @Override isPresenterFullyCollapsed()217 public boolean isPresenterFullyCollapsed() { 218 return mNotificationPanel.isFullyCollapsed(); 219 } 220 221 @Override updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation)222 public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { 223 mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation); 224 } 225 226 @Override onExpandClicked(NotificationEntry clickedEntry, View clickedView, boolean nowExpanded)227 public void onExpandClicked(NotificationEntry clickedEntry, View clickedView, 228 boolean nowExpanded) { 229 mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); 230 mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); 231 if (nowExpanded) { 232 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { 233 mShadeTransitionController.goToLockedShade(clickedEntry.getRow()); 234 } else if (clickedEntry.isSensitive() 235 && mDynamicPrivacyController.isInLockedDownShade()) { 236 mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); 237 mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */ 238 , null /* cancelRunnable */, false /* afterKeyguardGone */); 239 } 240 } 241 } 242 243 @Override isDeviceInVrMode()244 public boolean isDeviceInVrMode() { 245 return mVrMode; 246 } 247 onLockedNotificationImportanceChange(OnDismissAction dismissAction)248 private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) { 249 mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); 250 mActivityStarter.dismissKeyguardThenExecute(dismissAction, null, 251 true /* afterKeyguardGone */); 252 } 253 254 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 255 @Override 256 public void onVrStateChanged(boolean enabled) { 257 mVrMode = enabled; 258 } 259 }; 260 261 private final OnSettingsClickListener mOnSettingsClickListener = new OnSettingsClickListener() { 262 @Override 263 public void onSettingsClick(String key) { 264 try { 265 mBarService.onNotificationSettingsViewed(key); 266 } catch (RemoteException e) { 267 // if we're here we're dead 268 } 269 } 270 }; 271 272 private final NotificationInterruptSuppressor mInterruptSuppressor = 273 new NotificationInterruptSuppressor() { 274 @Override 275 public String getName() { 276 return TAG; 277 } 278 279 @Override 280 public boolean suppressAwakeHeadsUp(NotificationEntry entry) { 281 final StatusBarNotification sbn = entry.getSbn(); 282 if (mKeyguardStateController.isOccluded()) { 283 boolean devicePublic = mLockscreenUserManager 284 .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); 285 boolean userPublic = devicePublic 286 || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); 287 boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); 288 if (userPublic && needsRedaction) { 289 // TODO(b/135046837): we can probably relax this with dynamic privacy 290 return true; 291 } 292 } 293 294 if (!mCommandQueue.panelsEnabled()) { 295 if (DEBUG) { 296 Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); 297 } 298 return true; 299 } 300 301 return false; 302 } 303 304 @Override 305 public boolean suppressAwakeInterruptions(NotificationEntry entry) { 306 return isDeviceInVrMode(); 307 } 308 309 @Override 310 public boolean suppressInterruptions(NotificationEntry entry) { 311 return !mNotificationsInteractor.areNotificationAlertsEnabled(); 312 } 313 }; 314 } 315