1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.notification.collection.coordinator;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 import com.android.systemui.plugins.statusbar.StatusBarStateController;
23 import com.android.systemui.statusbar.notification.collection.ListEntry;
24 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
25 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
26 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
27 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
28 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
29 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
30 import com.android.systemui.statusbar.notification.collection.render.NodeController;
31 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
32 import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
33 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
34 import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
35 
36 import java.util.List;
37 
38 import javax.inject.Inject;
39 
40 /**
41  * Filters out NotificationEntries based on its Ranking and dozing state.
42  * Assigns alerting / silent section based on the importance of the notification entry.
43  * We check the NotificationEntry's Ranking for:
44  *  - whether the notification's app is suspended or hiding its notifications
45  *  - whether DND settings are hiding notifications from ambient display or the notification list
46  */
47 @CoordinatorScope
48 public class RankingCoordinator implements Coordinator {
49     public static final boolean SHOW_ALL_SECTIONS = false;
50     private final StatusBarStateController mStatusBarStateController;
51     private final HighPriorityProvider mHighPriorityProvider;
52     private final NodeController mSilentNodeController;
53     private final SectionHeaderController mSilentHeaderController;
54     private final NodeController mAlertingHeaderController;
55 
56     @Inject
RankingCoordinator( StatusBarStateController statusBarStateController, HighPriorityProvider highPriorityProvider, @AlertingHeader NodeController alertingHeaderController, @SilentHeader SectionHeaderController silentHeaderController, @SilentHeader NodeController silentNodeController)57     public RankingCoordinator(
58             StatusBarStateController statusBarStateController,
59             HighPriorityProvider highPriorityProvider,
60             @AlertingHeader NodeController alertingHeaderController,
61             @SilentHeader SectionHeaderController silentHeaderController,
62             @SilentHeader NodeController silentNodeController) {
63         mStatusBarStateController = statusBarStateController;
64         mHighPriorityProvider = highPriorityProvider;
65         mAlertingHeaderController = alertingHeaderController;
66         mSilentNodeController = silentNodeController;
67         mSilentHeaderController = silentHeaderController;
68     }
69 
70     @Override
attach(NotifPipeline pipeline)71     public void attach(NotifPipeline pipeline) {
72         mStatusBarStateController.addCallback(mStatusBarStateCallback);
73 
74         pipeline.addPreGroupFilter(mSuspendedFilter);
75         pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
76     }
77 
getAlertingSectioner()78     public NotifSectioner getAlertingSectioner() {
79         return mAlertingNotifSectioner;
80     }
81 
getSilentSectioner()82     public NotifSectioner getSilentSectioner() {
83         return mSilentNotifSectioner;
84     }
85 
86     private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting",
87             NotificationPriorityBucketKt.BUCKET_ALERTING) {
88         @Override
89         public boolean isInSection(ListEntry entry) {
90             return mHighPriorityProvider.isHighPriority(entry);
91         }
92 
93         @Nullable
94         @Override
95         public NodeController getHeaderNodeController() {
96             // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController
97             if (SHOW_ALL_SECTIONS) {
98                 return mAlertingHeaderController;
99             }
100             return null;
101         }
102     };
103 
104     private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent",
105             NotificationPriorityBucketKt.BUCKET_SILENT) {
106         @Override
107         public boolean isInSection(ListEntry entry) {
108             return !mHighPriorityProvider.isHighPriority(entry);
109         }
110 
111         @Nullable
112         @Override
113         public NodeController getHeaderNodeController() {
114             return mSilentNodeController;
115         }
116 
117         @Nullable
118         @Override
119         public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
120             for (int i = 0; i < entries.size(); i++) {
121                 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
122                     mSilentHeaderController.setClearSectionEnabled(true);
123                     return;
124                 }
125             }
126             mSilentHeaderController.setClearSectionEnabled(false);
127         }
128     };
129 
130     /**
131      * Checks whether to filter out the given notification based the notification's Ranking object.
132      * NotifListBuilder invalidates the notification list each time the ranking is updated,
133      * so we don't need to explicitly invalidate this filter on ranking update.
134      */
135     private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") {
136         @Override
137         public boolean shouldFilterOut(NotificationEntry entry, long now) {
138             return entry.getRanking().isSuspended();
139         }
140     };
141 
142     private final NotifFilter mDndVisualEffectsFilter = new NotifFilter(
143             "DndSuppressingVisualEffects") {
144         @Override
145         public boolean shouldFilterOut(NotificationEntry entry, long now) {
146             if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
147                 return true;
148             }
149 
150             return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList();
151         }
152     };
153 
154     private final StatusBarStateController.StateListener mStatusBarStateCallback =
155             new StatusBarStateController.StateListener() {
156                 @Override
157                 public void onDozingChanged(boolean isDozing) {
158                     mDndVisualEffectsFilter.invalidateList();
159                 }
160             };
161 }
162