1 /* 2 * Copyright (C) 2018 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; 18 19 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; 20 21 import android.annotation.NonNull; 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.util.ArraySet; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.systemui.R; 28 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 29 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; 30 31 import javax.inject.Inject; 32 import javax.inject.Singleton; 33 34 /** 35 * Manager which handles high priority notifications that should "pulse" in when the device is 36 * dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly 37 * before automatically dismissing the alert. 38 */ 39 @Singleton 40 public class AmbientPulseManager extends AlertingNotificationManager { 41 42 protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>(); 43 @VisibleForTesting 44 protected long mExtensionTime; 45 46 @Inject AmbientPulseManager(@onNull final Context context)47 public AmbientPulseManager(@NonNull final Context context) { 48 Resources resources = context.getResources(); 49 mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay); 50 mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time); 51 mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); 52 } 53 54 /** 55 * Adds an OnAmbientChangedListener to observe events. 56 */ addListener(@onNull OnAmbientChangedListener listener)57 public void addListener(@NonNull OnAmbientChangedListener listener) { 58 mListeners.add(listener); 59 } 60 61 /** 62 * Removes the OnAmbientChangedListener from the observer list. 63 */ removeListener(@onNull OnAmbientChangedListener listener)64 public void removeListener(@NonNull OnAmbientChangedListener listener) { 65 mListeners.remove(listener); 66 } 67 68 /** 69 * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts 70 * longer. 71 */ extendPulse()72 public void extendPulse() { 73 AmbientEntry topEntry = getTopEntry(); 74 if (topEntry == null) { 75 return; 76 } 77 topEntry.extendPulse(); 78 } 79 getContentFlag()80 public @InflationFlag int getContentFlag() { 81 return FLAG_CONTENT_VIEW_AMBIENT; 82 } 83 84 @Override onAlertEntryAdded(AlertEntry alertEntry)85 protected void onAlertEntryAdded(AlertEntry alertEntry) { 86 NotificationEntry entry = alertEntry.mEntry; 87 entry.setAmbientPulsing(true); 88 for (OnAmbientChangedListener listener : mListeners) { 89 listener.onAmbientStateChanged(entry, true); 90 } 91 } 92 93 @Override onAlertEntryRemoved(AlertEntry alertEntry)94 protected void onAlertEntryRemoved(AlertEntry alertEntry) { 95 NotificationEntry entry = alertEntry.mEntry; 96 entry.setAmbientPulsing(false); 97 for (OnAmbientChangedListener listener : mListeners) { 98 listener.onAmbientStateChanged(entry, false); 99 } 100 entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT); 101 } 102 103 @Override createAlertEntry()104 protected AlertEntry createAlertEntry() { 105 return new AmbientEntry(); 106 } 107 108 /** 109 * Get the top pulsing entry. This should be the currently showing one if there are multiple. 110 * @return the currently showing entry 111 */ getTopEntry()112 private AmbientEntry getTopEntry() { 113 if (mAlertEntries.isEmpty()) { 114 return null; 115 } 116 AlertEntry topEntry = null; 117 for (AlertEntry entry : mAlertEntries.values()) { 118 if (topEntry == null || entry.compareTo(topEntry) < 0) { 119 topEntry = entry; 120 } 121 } 122 return (AmbientEntry) topEntry; 123 } 124 125 /** 126 * Observer interface for any changes in the ambient entries. 127 */ 128 public interface OnAmbientChangedListener { 129 /** 130 * Called when an entry starts or stops pulsing. 131 * @param entry the entry that changed 132 * @param isPulsing true if the entry is now pulsing, false otherwise 133 */ onAmbientStateChanged(@onNull NotificationEntry entry, boolean isPulsing)134 void onAmbientStateChanged(@NonNull NotificationEntry entry, boolean isPulsing); 135 } 136 137 private final class AmbientEntry extends AlertEntry { 138 private boolean extended; 139 140 /** 141 * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be 142 * extended once. 143 */ extendPulse()144 private void extendPulse() { 145 if (!extended) { 146 extended = true; 147 updateEntry(false); 148 } 149 } 150 151 @Override reset()152 public void reset() { 153 super.reset(); 154 extended = false; 155 } 156 157 @Override calculateFinishTime()158 protected long calculateFinishTime() { 159 return super.calculateFinishTime() + (extended ? mExtensionTime : 0); 160 } 161 } 162 } 163