• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.headsup
18 
19 import android.graphics.Region
20 import com.android.systemui.Dumpable
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.statusbar.notification.collection.NotificationEntry
23 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
24 import dagger.Binds
25 import dagger.Module
26 import java.io.PrintWriter
27 import java.util.stream.Stream
28 import javax.inject.Inject
29 import kotlinx.coroutines.flow.MutableStateFlow
30 import kotlinx.coroutines.flow.StateFlow
31 
32 /**
33  * A manager which handles heads up notifications which is a special mode where they simply peek
34  * from the top of the screen.
35  */
36 interface HeadsUpManager : Dumpable {
37     /** The stream of all current notifications managed by this manager. */
38     val allEntries: Stream<NotificationEntry>
39 
40     /** Add a listener to receive callbacks onHeadsUpGoingAway. */
addHeadsUpPhoneListenernull41     fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange)
42 
43     /** Adds an OnHeadUpChangedListener to observe events. */
44     fun addListener(listener: OnHeadsUpChangedListener)
45 
46     fun addSwipedOutNotification(key: String)
47 
48     /**
49      * Whether or not the alert can be removed currently. If it hasn't been on screen long enough it
50      * should not be removed unless forced
51      *
52      * @param key the key to check if removable
53      * @return true if the alert entry can be removed
54      */
55     fun canRemoveImmediately(key: String): Boolean
56 
57     /**
58      * Compare two entries and decide how they should be ranked.
59      *
60      * @return -1 if the first argument should be ranked higher than the second, 1 if the second one
61      *   should be ranked higher and 0 if they are equal.
62      */
63     fun compare(a: NotificationEntry?, b: NotificationEntry?): Int
64 
65     /**
66      * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
67      * longer.
68      */
69     fun extendHeadsUp()
70 
71     /** Returns when a HUN entry should be removed in milliseconds from now. */
72     fun getEarliestRemovalTime(key: String?): Long
73 
74     /** Returns the top Heads Up Notification, which appears to show at first. */
75     fun getTopEntry(): NotificationEntry?
76 
77     /**
78      * Gets the touchable region needed for heads up notifications. Returns null if no touchable
79      * region is required (ie: no heads up notification currently exists).
80      */
81     // TODO(b/347007367): With scene container enabled this method may report outdated regions
82     fun getTouchableRegion(): Region?
83 
84     /**
85      * Whether or not there are any entries managed by HeadsUpManager.
86      *
87      * @return true if there is a heads up entry, false otherwise
88      */
89     fun hasNotifications(): Boolean = false
90 
91     /** Returns whether there are any pinned Heads Up Notifications or not. */
92     fun hasPinnedHeadsUp(): Boolean
93 
94     /**
95      * Returns the status of the top Heads Up Notification, or returns [PinnedStatus.NotPinned] if
96      * there is no pinned HUN.
97      */
98     fun pinnedHeadsUpStatus(): PinnedStatus
99 
100     /** Returns whether or not the given notification is managed by this manager. */
101     fun isHeadsUpEntry(key: String): Boolean
102 
103     /** @see setHeadsUpAnimatingAway */
104     fun isHeadsUpAnimatingAwayValue(): Boolean
105 
106     /** Returns if the given notification is snoozed or not. */
107     fun isSnoozed(packageName: String): Boolean
108 
109     /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */
110     fun isSticky(key: String?): Boolean
111 
112     /**
113      * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
114      * well.
115      */
116     fun isTrackingHeadsUp(): StateFlow<Boolean>
117 
118     fun onExpandingFinished()
119 
120     /** Removes the OnHeadUpChangedListener from the observer list. */
121     fun removeListener(listener: OnHeadsUpChangedListener)
122 
123     /**
124      * Try to remove the notification. May not succeed if the notification has not been shown long
125      * enough and needs to be kept around.
126      *
127      * @param key the key of the notification to remove
128      * @param releaseImmediately force a remove regardless of earliest removal time
129      * @param reason reason for removing the notification
130      * @return true if notification is removed, false otherwise
131      */
132     fun removeNotification(key: String, releaseImmediately: Boolean, reason: String): Boolean
133 
134     /**
135      * Try to remove the notification. May not succeed if the notification has not been shown long
136      * enough and needs to be kept around.
137      *
138      * @param key the key of the notification to remove
139      * @param releaseImmediately force a remove regardless of earliest removal time
140      * @param animate if true, animate the removal
141      * @param reason reason for removing the notification
142      * @return true if notification is removed, false otherwise
143      */
144     fun removeNotification(
145         key: String,
146         releaseImmediately: Boolean,
147         animate: Boolean,
148         reason: String,
149     ): Boolean
150 
151     /** Clears all managed notifications. */
152     fun releaseAllImmediately()
153 
154     fun setAnimationStateHandler(handler: AnimationStateHandler)
155 
156     /**
157      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
158      * it's collapsed again.
159      */
160     fun setExpanded(key: String, row: ExpandableNotificationRow, expanded: Boolean)
161 
162     /**
163      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
164      * it's collapsed again.
165      */
166     fun setExpanded(entry: NotificationEntry, expanded: Boolean)
167 
168     /**
169      * Sets whether an entry's guts are exposed and therefore it should stick in the heads up area
170      * if it's pinned until it's hidden again.
171      */
172     fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean)
173 
174     /**
175      * Set that we are exiting the headsUp pinned mode, but some notifications might still be
176      * animating out. This is used to keep the touchable regions in a reasonable state.
177      */
178     fun setHeadsUpAnimatingAway(headsUpAnimatingAway: Boolean)
179 
180     /**
181      * Notifies that a remote input textbox in notification gets active or inactive.
182      *
183      * @param entry The entry of the target notification.
184      * @param remoteInputActive True to notify active, False to notify inactive.
185      */
186     fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean)
187 
188     /**
189      * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
190      * from the list even after a Heads Up Notification is gone.
191      */
192     fun setTrackingHeadsUp(tracking: Boolean)
193 
194     /** Sets the current user. */
195     fun setUser(user: Int)
196 
197     /**
198      * Notes that the user took an action on an entry that might indirectly cause the system or the
199      * app to remove the notification.
200      *
201      * @param entry the key of the entry that might be indirectly removed by the user's action
202      * @see
203      *   com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener
204      * @see .canRemoveImmediately
205      */
206     fun setUserActionMayIndirectlyRemove(entryKey: String)
207 
208     /**
209      * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough
210      * that a user might have consciously clicked on it.
211      *
212      * @param key the key of the touched notification
213      * @return whether the touch is invalid and should be discarded
214      */
215     fun shouldSwallowClick(key: String): Boolean
216 
217     /**
218      * Called when posting a new notification that should alert the user and appear on screen. Adds
219      * the notification to be managed.
220      *
221      * @param entry entry to show
222      * @param isPinnedByUser true if the notification was pinned by the user and false if the
223      *   notification was pinned by the system.
224      */
225     fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean = false)
226 
227     fun snooze()
228 
229     /**
230      * Unpins all pinned Heads Up Notifications.
231      *
232      * @param userUnPinned The unpinned action is trigger by user real operation.
233      */
234     fun unpinAll(userUnPinned: Boolean)
235 
236     /**
237      * Called when the notification state has been updated.
238      *
239      * @param key the key of the entry that was updated
240      * @param requestedPinnedStatus whether and how the notification should be pinned. If equal to
241      *   [PinnedStatus.NotPinned], the notification won't show again. Otherwise, the notification
242      *   should show again and will force reevaluation of removal time.
243      */
244     fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus)
245 
246     fun onEntryAnimatingAwayEnded(entry: NotificationEntry)
247 }
248 
249 /** Sets the animation state of the HeadsUpManager. */
250 interface AnimationStateHandler {
251     fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean)
252 }
253 
254 /** Listener to register for HeadsUpNotification Phone changes. */
255 interface OnHeadsUpPhoneListenerChange {
256     /**
257      * Called when a heads up notification is 'going away' or no longer 'going away'. See
258      * [HeadsUpManager.setHeadsUpAnimatingAway].
259      */
260     // TODO(b/325936094) delete this callback, and listen to the flow instead
onHeadsUpAnimatingAwayStateChangednull261     fun onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway: Boolean)
262 }
263 
264 /* No op impl of HeadsUpManager. */
265 class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
266     override val allEntries = Stream.empty<NotificationEntry>()
267 
268     override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {}
269 
270     override fun addListener(listener: OnHeadsUpChangedListener) {}
271 
272     override fun addSwipedOutNotification(key: String) {}
273 
274     override fun canRemoveImmediately(key: String) = false
275 
276     override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0
277 
278     override fun dump(pw: PrintWriter, args: Array<out String>) {}
279 
280     override fun extendHeadsUp() {}
281 
282     override fun getEarliestRemovalTime(key: String?) = 0L
283 
284     override fun getTouchableRegion(): Region? = null
285 
286     override fun getTopEntry() = null
287 
288     override fun hasPinnedHeadsUp() = false
289 
290     override fun pinnedHeadsUpStatus() = PinnedStatus.NotPinned
291 
292     override fun isHeadsUpEntry(key: String) = false
293 
294     override fun isHeadsUpAnimatingAwayValue() = false
295 
296     override fun isTrackingHeadsUp(): StateFlow<Boolean> = MutableStateFlow(false)
297 
298     override fun isSnoozed(packageName: String) = false
299 
300     override fun isSticky(key: String?) = false
301 
302     override fun onExpandingFinished() {}
303 
304     override fun releaseAllImmediately() {}
305 
306     override fun removeListener(listener: OnHeadsUpChangedListener) {}
307 
308     override fun removeNotification(key: String, releaseImmediately: Boolean, reason: String) =
309         false
310 
311     override fun removeNotification(
312         key: String,
313         releaseImmediately: Boolean,
314         animate: Boolean,
315         reason: String,
316     ) = false
317 
318     override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
319 
320     override fun setExpanded(key: String, row: ExpandableNotificationRow, expanded: Boolean) {}
321 
322     override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
323 
324     override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
325 
326     override fun setHeadsUpAnimatingAway(headsUpAnimatingAway: Boolean) {}
327 
328     override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {}
329 
330     override fun setTrackingHeadsUp(tracking: Boolean) {}
331 
332     override fun setUser(user: Int) {}
333 
334     override fun setUserActionMayIndirectlyRemove(entryKey: String) {}
335 
336     override fun shouldSwallowClick(key: String): Boolean = false
337 
338     override fun showNotification(entry: NotificationEntry, isPinnedByUser: Boolean) {}
339 
340     override fun snooze() {}
341 
342     override fun unpinAll(userUnPinned: Boolean) {}
343 
344     override fun updateNotification(key: String, requestedPinnedStatus: PinnedStatus) {}
345 
346     override fun onEntryAnimatingAwayEnded(entry: NotificationEntry) {}
347 }
348 
349 @Module
350 interface HeadsUpEmptyImplModule {
bindsHeadsUpManagernull351     @Binds @SysUISingleton fun bindsHeadsUpManager(noOpHum: HeadsUpManagerEmptyImpl): HeadsUpManager
352 }
353