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.provider.SectionStyleProvider; 31 import com.android.systemui.statusbar.notification.collection.render.NodeController; 32 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; 33 import com.android.systemui.statusbar.notification.dagger.AlertingHeader; 34 import com.android.systemui.statusbar.notification.dagger.SilentHeader; 35 import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt; 36 37 import java.util.Collections; 38 import java.util.List; 39 40 import javax.inject.Inject; 41 42 /** 43 * Filters out NotificationEntries based on its Ranking and dozing state. 44 * Assigns alerting / silent section based on the importance of the notification entry. 45 * We check the NotificationEntry's Ranking for: 46 * - whether the notification's app is suspended or hiding its notifications 47 * - whether DND settings are hiding notifications from ambient display or the notification list 48 */ 49 @CoordinatorScope 50 public class RankingCoordinator implements Coordinator { 51 public static final boolean SHOW_ALL_SECTIONS = false; 52 private final StatusBarStateController mStatusBarStateController; 53 private final HighPriorityProvider mHighPriorityProvider; 54 private final SectionStyleProvider mSectionStyleProvider; 55 private final NodeController mSilentNodeController; 56 private final SectionHeaderController mSilentHeaderController; 57 private final NodeController mAlertingHeaderController; 58 private boolean mHasSilentEntries; 59 private boolean mHasMinimizedEntries; 60 61 @Inject RankingCoordinator( StatusBarStateController statusBarStateController, HighPriorityProvider highPriorityProvider, SectionStyleProvider sectionStyleProvider, @AlertingHeader NodeController alertingHeaderController, @SilentHeader SectionHeaderController silentHeaderController, @SilentHeader NodeController silentNodeController)62 public RankingCoordinator( 63 StatusBarStateController statusBarStateController, 64 HighPriorityProvider highPriorityProvider, 65 SectionStyleProvider sectionStyleProvider, 66 @AlertingHeader NodeController alertingHeaderController, 67 @SilentHeader SectionHeaderController silentHeaderController, 68 @SilentHeader NodeController silentNodeController) { 69 mStatusBarStateController = statusBarStateController; 70 mHighPriorityProvider = highPriorityProvider; 71 mSectionStyleProvider = sectionStyleProvider; 72 mAlertingHeaderController = alertingHeaderController; 73 mSilentNodeController = silentNodeController; 74 mSilentHeaderController = silentHeaderController; 75 } 76 77 @Override attach(NotifPipeline pipeline)78 public void attach(NotifPipeline pipeline) { 79 mStatusBarStateController.addCallback(mStatusBarStateCallback); 80 mSectionStyleProvider.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner)); 81 82 pipeline.addPreGroupFilter(mSuspendedFilter); 83 pipeline.addPreGroupFilter(mDndVisualEffectsFilter); 84 } 85 getAlertingSectioner()86 public NotifSectioner getAlertingSectioner() { 87 return mAlertingNotifSectioner; 88 } 89 getSilentSectioner()90 public NotifSectioner getSilentSectioner() { 91 return mSilentNotifSectioner; 92 } 93 getMinimizedSectioner()94 public NotifSectioner getMinimizedSectioner() { 95 return mMinimizedNotifSectioner; 96 } 97 98 private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting", 99 NotificationPriorityBucketKt.BUCKET_ALERTING) { 100 @Override 101 public boolean isInSection(ListEntry entry) { 102 return mHighPriorityProvider.isHighPriority(entry); 103 } 104 105 @Nullable 106 @Override 107 public NodeController getHeaderNodeController() { 108 // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController 109 if (SHOW_ALL_SECTIONS) { 110 return mAlertingHeaderController; 111 } 112 return null; 113 } 114 }; 115 116 private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent", 117 NotificationPriorityBucketKt.BUCKET_SILENT) { 118 @Override 119 public boolean isInSection(ListEntry entry) { 120 return !mHighPriorityProvider.isHighPriority(entry) 121 && !entry.getRepresentativeEntry().isAmbient(); 122 } 123 124 @Nullable 125 @Override 126 public NodeController getHeaderNodeController() { 127 return mSilentNodeController; 128 } 129 130 @Nullable 131 @Override 132 public void onEntriesUpdated(@NonNull List<ListEntry> entries) { 133 mHasSilentEntries = false; 134 for (int i = 0; i < entries.size(); i++) { 135 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { 136 mHasSilentEntries = true; 137 break; 138 } 139 } 140 mSilentHeaderController.setClearSectionEnabled( 141 mHasSilentEntries | mHasMinimizedEntries); 142 } 143 }; 144 145 private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized", 146 NotificationPriorityBucketKt.BUCKET_SILENT) { 147 @Override 148 public boolean isInSection(ListEntry entry) { 149 return !mHighPriorityProvider.isHighPriority(entry) 150 && entry.getRepresentativeEntry().isAmbient(); 151 } 152 153 @Nullable 154 @Override 155 public NodeController getHeaderNodeController() { 156 return mSilentNodeController; 157 } 158 159 @Nullable 160 @Override 161 public void onEntriesUpdated(@NonNull List<ListEntry> entries) { 162 mHasMinimizedEntries = false; 163 for (int i = 0; i < entries.size(); i++) { 164 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { 165 mHasMinimizedEntries = true; 166 break; 167 } 168 } 169 mSilentHeaderController.setClearSectionEnabled( 170 mHasSilentEntries | mHasMinimizedEntries); 171 } 172 }; 173 174 /** 175 * Checks whether to filter out the given notification based the notification's Ranking object. 176 * NotifListBuilder invalidates the notification list each time the ranking is updated, 177 * so we don't need to explicitly invalidate this filter on ranking update. 178 */ 179 private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") { 180 @Override 181 public boolean shouldFilterOut(NotificationEntry entry, long now) { 182 return entry.getRanking().isSuspended(); 183 } 184 }; 185 186 private final NotifFilter mDndVisualEffectsFilter = new NotifFilter( 187 "DndSuppressingVisualEffects") { 188 @Override 189 public boolean shouldFilterOut(NotificationEntry entry, long now) { 190 if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) { 191 return true; 192 } 193 194 return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList(); 195 } 196 }; 197 198 private final StatusBarStateController.StateListener mStatusBarStateCallback = 199 new StatusBarStateController.StateListener() { 200 @Override 201 public void onDozingChanged(boolean isDozing) { 202 mDndVisualEffectsFilter.invalidateList("onDozingChanged to " + isDozing); 203 } 204 }; 205 } 206