1 /*
2  * Copyright (C) 2021 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 package com.android.systemui.statusbar.notification.collection.coordinator
17 
18 import android.util.ArraySet
19 import com.android.systemui.Dumpable
20 import com.android.systemui.dump.DumpManager
21 import com.android.systemui.statusbar.notification.collection.ListEntry
22 import com.android.systemui.statusbar.notification.collection.NotifPipeline
23 import com.android.systemui.statusbar.notification.collection.NotificationEntry
24 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
25 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
26 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
27 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
28 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
29 import com.android.systemui.statusbar.notification.row.NotificationGuts
30 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
31 import com.android.systemui.util.asIndenting
32 import com.android.systemui.util.printCollection
33 import com.android.systemui.util.println
34 import com.android.systemui.util.withIncreasedIndent
35 import java.io.PrintWriter
36 import javax.inject.Inject
37 
38 private const val TAG = "GutsCoordinator"
39 
40 /**
41  * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline.
42  * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing.
43  */
44 @CoordinatorScope
45 class GutsCoordinator @Inject constructor(
46     private val notifGutsViewManager: NotifGutsViewManager,
47     private val logger: GutsCoordinatorLogger,
48     dumpManager: DumpManager
49 ) : Coordinator, Dumpable {
50 
51     /** Keys of any Notifications for which we've been told the guts are open  */
52     private val notifsWithOpenGuts = ArraySet<String>()
53 
54     /** Keys of any Notifications we've extended the lifetime for, based on guts  */
55     private val notifsExtendingLifetime = ArraySet<String>()
56 
57     /** Callback for ending lifetime extension  */
58     private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null
59 
60     init {
61         dumpManager.registerDumpable(this)
62     }
63 
attachnull64     override fun attach(pipeline: NotifPipeline) {
65         notifGutsViewManager.setGutsListener(mGutsListener)
66         pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
67     }
68 
<lambda>null69     override fun dump(pw: PrintWriter, args: Array<String>) = pw.asIndenting().run {
70         withIncreasedIndent {
71             printCollection("notifsWithOpenGuts", notifsWithOpenGuts)
72             printCollection("notifsExtendingLifetime", notifsExtendingLifetime)
73             println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback)
74         }
75     }
76 
77     private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender {
getNamenull78         override fun getName(): String {
79             return TAG
80         }
81 
setCallbacknull82         override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
83             onEndLifetimeExtensionCallback = callback
84         }
85 
maybeExtendLifetimenull86         override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
87             val isShowingGuts = isCurrentlyShowingGuts(entry)
88             if (isShowingGuts) {
89                 notifsExtendingLifetime.add(entry.key)
90             }
91             return isShowingGuts
92         }
93 
cancelLifetimeExtensionnull94         override fun cancelLifetimeExtension(entry: NotificationEntry) {
95             notifsExtendingLifetime.remove(entry.key)
96         }
97     }
98 
99     private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener {
onGutsOpennull100         override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) {
101             logger.logGutsOpened(entry.key, guts)
102             if (guts.isLeavebehind) {
103                 // leave-behind guts should not extend the lifetime of the notification
104                 closeGutsAndEndLifetimeExtension(entry)
105             } else {
106                 notifsWithOpenGuts.add(entry.key)
107             }
108         }
109 
onGutsClosenull110         override fun onGutsClose(entry: NotificationEntry) {
111             logger.logGutsClosed(entry.key)
112             closeGutsAndEndLifetimeExtension(entry)
113         }
114     }
115 
isCurrentlyShowingGutsnull116     private fun isCurrentlyShowingGuts(entry: ListEntry) =
117             notifsWithOpenGuts.contains(entry.key)
118 
119     private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
120         notifsWithOpenGuts.remove(entry.key)
121         if (notifsExtendingLifetime.remove(entry.key)) {
122             onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry)
123         }
124     }
125 }
126