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 private boolean mHasSilentEntries; 56 private boolean mHasMinimizedEntries; 57 58 @Inject RankingCoordinator( StatusBarStateController statusBarStateController, HighPriorityProvider highPriorityProvider, @AlertingHeader NodeController alertingHeaderController, @SilentHeader SectionHeaderController silentHeaderController, @SilentHeader NodeController silentNodeController)59 public RankingCoordinator( 60 StatusBarStateController statusBarStateController, 61 HighPriorityProvider highPriorityProvider, 62 @AlertingHeader NodeController alertingHeaderController, 63 @SilentHeader SectionHeaderController silentHeaderController, 64 @SilentHeader NodeController silentNodeController) { 65 mStatusBarStateController = statusBarStateController; 66 mHighPriorityProvider = highPriorityProvider; 67 mAlertingHeaderController = alertingHeaderController; 68 mSilentNodeController = silentNodeController; 69 mSilentHeaderController = silentHeaderController; 70 } 71 72 @Override attach(NotifPipeline pipeline)73 public void attach(NotifPipeline pipeline) { 74 mStatusBarStateController.addCallback(mStatusBarStateCallback); 75 76 pipeline.addPreGroupFilter(mSuspendedFilter); 77 pipeline.addPreGroupFilter(mDndVisualEffectsFilter); 78 } 79 getAlertingSectioner()80 public NotifSectioner getAlertingSectioner() { 81 return mAlertingNotifSectioner; 82 } 83 getSilentSectioner()84 public NotifSectioner getSilentSectioner() { 85 return mSilentNotifSectioner; 86 } 87 getMinimizedSectioner()88 public NotifSectioner getMinimizedSectioner() { 89 return mMinimizedNotifSectioner; 90 } 91 92 private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting", 93 NotificationPriorityBucketKt.BUCKET_ALERTING) { 94 @Override 95 public boolean isInSection(ListEntry entry) { 96 return mHighPriorityProvider.isHighPriority(entry); 97 } 98 99 @Nullable 100 @Override 101 public NodeController getHeaderNodeController() { 102 // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController 103 if (SHOW_ALL_SECTIONS) { 104 return mAlertingHeaderController; 105 } 106 return null; 107 } 108 }; 109 110 private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent", 111 NotificationPriorityBucketKt.BUCKET_SILENT) { 112 @Override 113 public boolean isInSection(ListEntry entry) { 114 return !mHighPriorityProvider.isHighPriority(entry) 115 && !entry.getRepresentativeEntry().isAmbient(); 116 } 117 118 @Nullable 119 @Override 120 public NodeController getHeaderNodeController() { 121 return mSilentNodeController; 122 } 123 124 @Nullable 125 @Override 126 public void onEntriesUpdated(@NonNull List<ListEntry> entries) { 127 mHasSilentEntries = false; 128 for (int i = 0; i < entries.size(); i++) { 129 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { 130 mHasSilentEntries = true; 131 break; 132 } 133 } 134 mSilentHeaderController.setClearSectionEnabled( 135 mHasSilentEntries | mHasMinimizedEntries); 136 } 137 }; 138 139 private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized", 140 NotificationPriorityBucketKt.BUCKET_SILENT) { 141 @Override 142 public boolean isInSection(ListEntry entry) { 143 return !mHighPriorityProvider.isHighPriority(entry) 144 && entry.getRepresentativeEntry().isAmbient(); 145 } 146 147 @Nullable 148 @Override 149 public NodeController getHeaderNodeController() { 150 return mSilentNodeController; 151 } 152 153 @Nullable 154 @Override 155 public void onEntriesUpdated(@NonNull List<ListEntry> entries) { 156 mHasMinimizedEntries = false; 157 for (int i = 0; i < entries.size(); i++) { 158 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { 159 mHasMinimizedEntries = true; 160 break; 161 } 162 } 163 mSilentHeaderController.setClearSectionEnabled( 164 mHasSilentEntries | mHasMinimizedEntries); 165 } 166 }; 167 168 /** 169 * Checks whether to filter out the given notification based the notification's Ranking object. 170 * NotifListBuilder invalidates the notification list each time the ranking is updated, 171 * so we don't need to explicitly invalidate this filter on ranking update. 172 */ 173 private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") { 174 @Override 175 public boolean shouldFilterOut(NotificationEntry entry, long now) { 176 return entry.getRanking().isSuspended(); 177 } 178 }; 179 180 private final NotifFilter mDndVisualEffectsFilter = new NotifFilter( 181 "DndSuppressingVisualEffects") { 182 @Override 183 public boolean shouldFilterOut(NotificationEntry entry, long now) { 184 if ((mStatusBarStateController.isDozing() 185 || mStatusBarStateController.getDozeAmount() == 1f) 186 && entry.shouldSuppressAmbient()) { 187 return true; 188 } 189 190 return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList(); 191 } 192 }; 193 194 private final StatusBarStateController.StateListener mStatusBarStateCallback = 195 new StatusBarStateController.StateListener() { 196 private boolean mPrevDozeAmountIsOne = false; 197 198 @Override 199 public void onDozeAmountChanged(float linear, float eased) { 200 StatusBarStateController.StateListener.super.onDozeAmountChanged(linear, eased); 201 202 boolean dozeAmountIsOne = linear == 1f; 203 if (mPrevDozeAmountIsOne != dozeAmountIsOne) { 204 mDndVisualEffectsFilter.invalidateList("dozeAmount changed to " 205 + (dozeAmountIsOne ? "one" : "not one")); 206 mPrevDozeAmountIsOne = dozeAmountIsOne; 207 } 208 } 209 210 @Override 211 public void onDozingChanged(boolean isDozing) { 212 mDndVisualEffectsFilter.invalidateList("onDozingChanged to " + isDozing); 213 } 214 }; 215 } 216