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 
17 package com.android.systemui.statusbar.notification.collection.coordinator
18 
19 import android.service.notification.NotificationListenerService
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.dagger.qualifiers.Main
22 import com.android.systemui.statusbar.notification.collection.ListEntry
23 import com.android.systemui.statusbar.notification.collection.NotifPipeline
24 import com.android.systemui.statusbar.notification.collection.NotificationEntry
25 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
26 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource
27 import java.util.concurrent.Executor
28 import javax.inject.Inject
29 
30 /**
31  * A coordinator which provides callbacks to a view surfaces for various events relevant to the
32  * shade, such as when the user removes a notification, or when the shade is emptied.
33  */
34 // TODO(b/204468557): Move to @CoordinatorScope
35 @SysUISingleton
36 class ShadeEventCoordinator @Inject internal constructor(
37     @Main private val mMainExecutor: Executor,
38     private val mLogger: ShadeEventCoordinatorLogger
39 ) : Coordinator, NotifShadeEventSource {
40     private var mNotifRemovedByUserCallback: Runnable? = null
41     private var mShadeEmptiedCallback: Runnable? = null
42     private var mEntryRemoved = false
43     private var mEntryRemovedByUser = false
44 
attachnull45     override fun attach(pipeline: NotifPipeline) {
46         pipeline.addCollectionListener(mNotifCollectionListener)
47         pipeline.addOnBeforeRenderListListener(this::onBeforeRenderList)
48     }
49 
50     private val mNotifCollectionListener = object : NotifCollectionListener {
onEntryRemovednull51         override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
52             mEntryRemoved = true
53             mEntryRemovedByUser =
54                     reason == NotificationListenerService.REASON_CLICK ||
55                     reason == NotificationListenerService.REASON_CANCEL_ALL ||
56                     reason == NotificationListenerService.REASON_CANCEL
57         }
58     }
59 
setNotifRemovedByUserCallbacknull60     override fun setNotifRemovedByUserCallback(callback: Runnable) {
61         check(mNotifRemovedByUserCallback == null) { "mNotifRemovedByUserCallback already set" }
62         mNotifRemovedByUserCallback = callback
63     }
64 
setShadeEmptiedCallbacknull65     override fun setShadeEmptiedCallback(callback: Runnable) {
66         check(mShadeEmptiedCallback == null) { "mShadeEmptiedCallback already set" }
67         mShadeEmptiedCallback = callback
68     }
69 
onBeforeRenderListnull70     private fun onBeforeRenderList(entries: List<ListEntry>) {
71         if (mEntryRemoved && entries.isEmpty()) {
72             mLogger.logShadeEmptied()
73             // TODO(b/206023518): This was bad. Do not copy this.
74             mShadeEmptiedCallback?.let { mMainExecutor.execute(it) }
75         }
76         if (mEntryRemoved && mEntryRemovedByUser) {
77             mLogger.logNotifRemovedByUser()
78             // TODO(b/206023518): This was bad. Do not copy this.
79             mNotifRemovedByUserCallback?.let { mMainExecutor.execute(it) }
80         }
81         mEntryRemoved = false
82         mEntryRemovedByUser = false
83     }
84 }