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