OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / statusbar / phone / StatusBarNotificationPresenter.java
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.SysUiServiceProvider.getComponent;
18 import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
19 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
20 import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
21 import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
22
23 import android.annotation.Nullable;
24 import android.app.KeyguardManager;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.service.notification.StatusBarNotification;
30 import android.service.vr.IVrManager;
31 import android.service.vr.IVrStateCallbacks;
32 import android.util.Log;
33 import android.util.Slog;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.accessibility.AccessibilityManager;
37 import android.widget.TextView;
38
39 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
40 import com.android.internal.statusbar.IStatusBarService;
41 import com.android.internal.statusbar.NotificationVisibility;
42 import com.android.internal.widget.MessagingGroup;
43 import com.android.internal.widget.MessagingMessage;
44 import com.android.keyguard.KeyguardUpdateMonitor;
45 import com.android.systemui.Dependency;
46 import com.android.systemui.ForegroundServiceNotificationListener;
47 import com.android.systemui.InitController;
48 import com.android.systemui.R;
49 import com.android.systemui.plugins.ActivityStarter;
50 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
51 import com.android.systemui.plugins.statusbar.StatusBarStateController;
52 import com.android.systemui.statusbar.AmbientPulseManager;
53 import com.android.systemui.statusbar.CommandQueue;
54 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
55 import com.android.systemui.statusbar.NotificationMediaManager;
56 import com.android.systemui.statusbar.NotificationPresenter;
57 import com.android.systemui.statusbar.NotificationRemoteInputManager;
58 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
59 import com.android.systemui.statusbar.StatusBarState;
60 import com.android.systemui.statusbar.SysuiStatusBarStateController;
61 import com.android.systemui.statusbar.notification.AboveShelfObserver;
62 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
63 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
64 import com.android.systemui.statusbar.notification.NotificationEntryListener;
65 import com.android.systemui.statusbar.notification.NotificationEntryManager;
66 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
67 import com.android.systemui.statusbar.notification.VisualStabilityManager;
68 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
69 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
70 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
71 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
72 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
73 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
74 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
75 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
76 import com.android.systemui.statusbar.policy.ConfigurationController;
77 import com.android.systemui.statusbar.policy.KeyguardMonitor;
78
79 import java.util.ArrayList;
80
81 public class StatusBarNotificationPresenter implements NotificationPresenter,
82         ConfigurationController.ConfigurationListener,
83         NotificationRowBinderImpl.BindRowCallback {
84
85     private final LockscreenGestureLogger mLockscreenGestureLogger =
86             Dependency.get(LockscreenGestureLogger.class);
87
88     private static final String TAG = "StatusBarNotificationPresenter";
89
90     private final ShadeController mShadeController = Dependency.get(ShadeController.class);
91     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
92     private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
93     private final NotificationViewHierarchyManager mViewHierarchyManager =
94             Dependency.get(NotificationViewHierarchyManager.class);
95     private final NotificationLockscreenUserManager mLockscreenUserManager =
96             Dependency.get(NotificationLockscreenUserManager.class);
97     private final SysuiStatusBarStateController mStatusBarStateController =
98             (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
99     private final NotificationEntryManager mEntryManager =
100             Dependency.get(NotificationEntryManager.class);
101     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
102             Dependency.get(NotificationInterruptionStateProvider.class);
103     private final NotificationMediaManager mMediaManager =
104             Dependency.get(NotificationMediaManager.class);
105     private final VisualStabilityManager mVisualStabilityManager =
106             Dependency.get(VisualStabilityManager.class);
107     private final NotificationGutsManager mGutsManager =
108             Dependency.get(NotificationGutsManager.class);
109     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
110
111     private final NotificationPanelView mNotificationPanel;
112     private final HeadsUpManagerPhone mHeadsUpManager;
113     private final AboveShelfObserver mAboveShelfObserver;
114     private final DozeScrimController mDozeScrimController;
115     private final ScrimController mScrimController;
116     private final Context mContext;
117     private final CommandQueue mCommandQueue;
118
119     private final AccessibilityManager mAccessibilityManager;
120     private final KeyguardManager mKeyguardManager;
121     private final ActivityLaunchAnimator mActivityLaunchAnimator;
122     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
123     private final int mMaxAllowedKeyguardNotifications;
124     private final IStatusBarService mBarService;
125     private boolean mReinflateNotificationsOnUserSwitched;
126     private boolean mDispatchUiModeChangeOnUserSwitched;
127     private final UnlockMethodCache mUnlockMethodCache;
128     private TextView mNotificationPanelDebugText;
129
130     protected boolean mVrMode;
131     private int mMaxKeyguardNotifications;
132
133     public StatusBarNotificationPresenter(Context context,
134             NotificationPanelView panel,
135             HeadsUpManagerPhone headsUp,
136             StatusBarWindowView statusBarWindow,
137             ViewGroup stackScroller,
138             DozeScrimController dozeScrimController,
139             ScrimController scrimController,
140             ActivityLaunchAnimator activityLaunchAnimator,
141             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
142             NotificationAlertingManager notificationAlertingManager,
143             NotificationRowBinderImpl notificationRowBinder) {
144         mContext = context;
145         mNotificationPanel = panel;
146         mHeadsUpManager = headsUp;
147         mCommandQueue = getComponent(context, CommandQueue.class);
148         mAboveShelfObserver = new AboveShelfObserver(stackScroller);
149         mActivityLaunchAnimator = activityLaunchAnimator;
150         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
151         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
152                 R.id.notification_container_parent));
153         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
154         mDozeScrimController = dozeScrimController;
155         mScrimController = scrimController;
156         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
157         mKeyguardManager = context.getSystemService(KeyguardManager.class);
158         mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
159                 R.integer.keyguard_max_notification_count);
160         mBarService = IStatusBarService.Stub.asInterface(
161                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
162
163         if (MULTIUSER_DEBUG) {
164             mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
165             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
166         }
167
168         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
169                 Context.VR_SERVICE));
170         if (vrManager != null) {
171             try {
172                 vrManager.registerListener(mVrStateCallbacks);
173             } catch (RemoteException e) {
174                 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
175             }
176         }
177         NotificationRemoteInputManager remoteInputManager =
178                 Dependency.get(NotificationRemoteInputManager.class);
179         remoteInputManager.setUpWithCallback(
180                 Dependency.get(NotificationRemoteInputManager.Callback.class),
181                 mNotificationPanel.createRemoteInputDelegate());
182         remoteInputManager.getController().addCallback(
183                 Dependency.get(StatusBarWindowController.class));
184
185         NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
186         Dependency.get(InitController.class).addPostInitTask(() -> {
187             NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
188                 @Override
189                 public void onNotificationAdded(NotificationEntry entry) {
190                     // Recalculate the position of the sliding windows and the titles.
191                     mShadeController.updateAreThereNotifications();
192                 }
193
194                 @Override
195                 public void onPostEntryUpdated(NotificationEntry entry) {
196                     mShadeController.updateAreThereNotifications();
197                 }
198
199                 @Override
200                 public void onEntryRemoved(
201                         @Nullable NotificationEntry entry,
202                         NotificationVisibility visibility,
203                         boolean removedByUser) {
204                     StatusBarNotificationPresenter.this.onNotificationRemoved(
205                             entry.key, entry.notification);
206                     if (removedByUser) {
207                         maybeEndAmbientPulse();
208                     }
209                 }
210             };
211
212             mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
213             mEntryManager.setUpWithPresenter(this, notifListContainer, mHeadsUpManager);
214             mEntryManager.addNotificationEntryListener(notificationEntryListener);
215             mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
216             mEntryManager.addNotificationLifetimeExtender(mAmbientPulseManager);
217             mEntryManager.addNotificationLifetimeExtender(mGutsManager);
218             mEntryManager.addNotificationLifetimeExtenders(
219                     remoteInputManager.getLifetimeExtenders());
220             notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
221                     mEntryManager, this);
222             mNotificationInterruptionStateProvider.setUpWithPresenter(
223                     this, mHeadsUpManager, this::canHeadsUp);
224             mLockscreenUserManager.setUpWithPresenter(this);
225             mMediaManager.setUpWithPresenter(this);
226             mVisualStabilityManager.setUpWithPresenter(this);
227             mGutsManager.setUpWithPresenter(this,
228                     notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
229             // ForegroundServiceNotificationListener adds its listener in its constructor
230             // but we need to request it here in order for it to be instantiated.
231             // TODO: figure out how to do this correctly once Dependency.get() is gone.
232             Dependency.get(ForegroundServiceNotificationListener.class);
233
234             onUserSwitched(mLockscreenUserManager.getCurrentUserId());
235         });
236         Dependency.get(ConfigurationController.class).addCallback(this);
237
238         notificationAlertingManager.setHeadsUpManager(mHeadsUpManager);
239     }
240
241     @Override
242     public void onDensityOrFontScaleChanged() {
243         MessagingMessage.dropCache();
244         MessagingGroup.dropCache();
245         if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
246             updateNotificationsOnDensityOrFontScaleChanged();
247         } else {
248             mReinflateNotificationsOnUserSwitched = true;
249         }
250     }
251
252     @Override
253     public void onUiModeChanged() {
254         if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
255             updateNotificationOnUiModeChanged();
256         } else {
257             mDispatchUiModeChangeOnUserSwitched = true;
258         }
259     }
260
261     @Override
262     public void onOverlayChanged() {
263         onDensityOrFontScaleChanged();
264     }
265
266     private void updateNotificationOnUiModeChanged() {
267         ArrayList<NotificationEntry> userNotifications
268                 = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
269         for (int i = 0; i < userNotifications.size(); i++) {
270             NotificationEntry entry = userNotifications.get(i);
271             ExpandableNotificationRow row = entry.getRow();
272             if (row != null) {
273                 row.onUiModeChanged();
274             }
275         }
276     }
277
278     private void updateNotificationsOnDensityOrFontScaleChanged() {
279         ArrayList<NotificationEntry> userNotifications =
280                 mEntryManager.getNotificationData().getNotificationsForCurrentUser();
281         for (int i = 0; i < userNotifications.size(); i++) {
282             NotificationEntry entry = userNotifications.get(i);
283             entry.onDensityOrFontScaleChanged();
284             boolean exposedGuts = entry.areGutsExposed();
285             if (exposedGuts) {
286                 mGutsManager.onDensityOrFontScaleChanged(entry);
287             }
288         }
289     }
290
291     @Override
292     public boolean isCollapsing() {
293         return mNotificationPanel.isCollapsing()
294                 || mActivityLaunchAnimator.isAnimationPending()
295                 || mActivityLaunchAnimator.isAnimationRunning();
296     }
297
298     private void maybeEndAmbientPulse() {
299         if (mNotificationPanel.hasPulsingNotifications() &&
300                 !mAmbientPulseManager.hasNotifications()) {
301             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
302             // Finish the pulse.
303             mDozeScrimController.pulseOutNow();
304         }
305     }
306
307     @Override
308     public void updateNotificationViews() {
309         // The function updateRowStates depends on both of these being non-null, so check them here.
310         // We may be called before they are set from DeviceProvisionedController's callback.
311         if (mScrimController == null) return;
312
313         // Do not modify the notifications during collapse.
314         if (isCollapsing()) {
315             mShadeController.addPostCollapseAction(this::updateNotificationViews);
316             return;
317         }
318
319         mViewHierarchyManager.updateNotificationViews();
320
321         mNotificationPanel.updateNotificationViews();
322     }
323
324     public void onNotificationRemoved(String key, StatusBarNotification old) {
325         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
326
327         if (old != null) {
328             if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
329                     && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
330                 if (mStatusBarStateController.getState() == StatusBarState.SHADE) {
331                     mCommandQueue.animateCollapsePanels();
332                 } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
333                         && !isCollapsing()) {
334                     mShadeController.goToKeyguard();
335                 }
336             }
337         }
338         mShadeController.updateAreThereNotifications();
339     }
340
341     public boolean hasActiveNotifications() {
342         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
343     }
344
345     public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
346         if (mShadeController.isDozing()) {
347             return false;
348         }
349
350         if (mShadeController.isOccluded()) {
351             boolean devicePublic = mLockscreenUserManager.
352                     isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
353             boolean userPublic = devicePublic
354                     || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
355             boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
356             if (userPublic && needsRedaction) {
357                 return false;
358             }
359         }
360
361         if (!mCommandQueue.panelsEnabled()) {
362             if (DEBUG) {
363                 Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
364             }
365             return false;
366         }
367
368         if (sbn.getNotification().fullScreenIntent != null) {
369             if (mAccessibilityManager.isTouchExplorationEnabled()) {
370                 if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
371                 return false;
372             } else {
373                 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
374                 return !mKeyguardMonitor.isShowing()
375                         || mShadeController.isOccluded();
376             }
377         }
378         return true;
379     }
380
381     @Override
382     public void onUserSwitched(int newUserId) {
383         // Begin old BaseStatusBar.userSwitched
384         mHeadsUpManager.setUser(newUserId);
385         // End old BaseStatusBar.userSwitched
386         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
387         mCommandQueue.animateCollapsePanels();
388         if (mReinflateNotificationsOnUserSwitched) {
389             updateNotificationsOnDensityOrFontScaleChanged();
390             mReinflateNotificationsOnUserSwitched = false;
391         }
392         if (mDispatchUiModeChangeOnUserSwitched) {
393             updateNotificationOnUiModeChanged();
394             mDispatchUiModeChangeOnUserSwitched = false;
395         }
396         updateNotificationViews();
397         mMediaManager.clearCurrentMediaNotification();
398         mShadeController.setLockscreenUser(newUserId);
399         updateMediaMetaData(true, false);
400     }
401
402     @Override
403     public void onBindRow(NotificationEntry entry, PackageManager pmUser,
404             StatusBarNotification sbn, ExpandableNotificationRow row) {
405         row.setAboveShelfChangedListener(mAboveShelfObserver);
406         row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
407     }
408
409     @Override
410     public boolean isPresenterFullyCollapsed() {
411         return mNotificationPanel.isFullyCollapsed();
412     }
413
414     @Override
415     public void onActivated(ActivatableNotificationView view) {
416         onActivated();
417         if (view != null) mNotificationPanel.setActivatedChild(view);
418     }
419
420     public void onActivated() {
421         mLockscreenGestureLogger.write(
422                 MetricsEvent.ACTION_LS_NOTE,
423                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
424         mNotificationPanel.showTransientIndication(R.string.notification_tap_again);
425         ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild();
426         if (previousView != null) {
427             previousView.makeInactive(true /* animate */);
428         }
429     }
430
431     @Override
432     public void onActivationReset(ActivatableNotificationView view) {
433         if (view == mNotificationPanel.getActivatedChild()) {
434             mNotificationPanel.setActivatedChild(null);
435             mShadeController.onActivationReset();
436         }
437     }
438
439     @Override
440     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
441         mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
442     }
443
444     @Override
445     public int getMaxNotificationsWhileLocked(boolean recompute) {
446         if (recompute) {
447             mMaxKeyguardNotifications = Math.max(1,
448                     mNotificationPanel.computeMaxKeyguardNotifications(
449                             mMaxAllowedKeyguardNotifications));
450             return mMaxKeyguardNotifications;
451         }
452         return mMaxKeyguardNotifications;
453     }
454
455     @Override
456     public void onUpdateRowStates() {
457         mNotificationPanel.onUpdateRowStates();
458     }
459
460     @Override
461     public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
462         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
463         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
464             mShadeController.goToLockedShade(clickedEntry.getRow());
465         }
466     }
467
468     @Override
469     public boolean isDeviceInVrMode() {
470         return mVrMode;
471     }
472
473     @Override
474     public boolean isPresenterLocked() {
475         return mStatusBarKeyguardViewManager.isShowing()
476                 && mStatusBarKeyguardViewManager.isSecure();
477     }
478
479     private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
480         mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
481         mActivityStarter.dismissKeyguardThenExecute(dismissAction, null,
482                 true /* afterKeyguardGone */);
483     }
484
485     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
486         @Override
487         public void onVrStateChanged(boolean enabled) {
488             mVrMode = enabled;
489         }
490     };
491
492     private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() {
493         @Override
494         public void checkSave(Runnable saveImportance, StatusBarNotification sbn) {
495             // If the user has security enabled, show challenge if the setting is changed.
496             if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
497                     && mKeyguardManager.isKeyguardLocked()) {
498                 onLockedNotificationImportanceChange(() -> {
499                     saveImportance.run();
500                     return true;
501                 });
502             } else {
503                 saveImportance.run();
504             }
505         }
506     };
507
508     private final OnSettingsClickListener mOnSettingsClickListener = new OnSettingsClickListener() {
509         @Override
510         public void onSettingsClick(String key) {
511             try {
512                 mBarService.onNotificationSettingsViewed(key);
513             } catch (RemoteException e) {
514                 // if we're here we're dead
515             }
516         }
517     };
518 }