1 /*
<lambda>null2  * 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
18 
19 import androidx.lifecycle.Observer
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.dagger.qualifiers.Main
22 import com.android.systemui.util.Assert
23 import com.android.systemui.util.ListenerSet
24 import com.android.app.tracing.traceSection
25 import java.util.Collections.unmodifiableList
26 import java.util.concurrent.Executor
27 import java.util.concurrent.atomic.AtomicReference
28 import javax.inject.Inject
29 
30 /** Writeable implementation of [NotifLiveDataStore] */
31 @SysUISingleton
32 class NotifLiveDataStoreImpl @Inject constructor(
33     @Main private val mainExecutor: Executor
34 ) : NotifLiveDataStore, PipelineDumpable {
35     private val hasActiveNotifsPrivate = NotifLiveDataImpl(
36         name = "hasActiveNotifs",
37         initialValue = false,
38         mainExecutor
39     )
40     private val activeNotifCountPrivate = NotifLiveDataImpl(
41         name = "activeNotifCount",
42         initialValue = 0,
43         mainExecutor
44     )
45     private val activeNotifListPrivate = NotifLiveDataImpl(
46         name = "activeNotifList",
47         initialValue = listOf<NotificationEntry>(),
48         mainExecutor
49     )
50 
51     override val hasActiveNotifs: NotifLiveData<Boolean> = hasActiveNotifsPrivate
52     override val activeNotifCount: NotifLiveData<Int> = activeNotifCountPrivate
53     override val activeNotifList: NotifLiveData<List<NotificationEntry>> = activeNotifListPrivate
54 
55     /** Set the latest flattened list of notification entries. */
56     fun setActiveNotifList(flatEntryList: List<NotificationEntry>) {
57         traceSection("NotifLiveDataStore.setActiveNotifList") {
58             Assert.isMainThread()
59             val unmodifiableCopy = unmodifiableList(flatEntryList.toList())
60             // This ensures we set all values before dispatching to any observers
61             listOf(
62                 activeNotifListPrivate.setValueAndProvideDispatcher(unmodifiableCopy),
63                 activeNotifCountPrivate.setValueAndProvideDispatcher(unmodifiableCopy.size),
64                 hasActiveNotifsPrivate.setValueAndProvideDispatcher(unmodifiableCopy.isNotEmpty())
65             ).forEach { dispatcher -> dispatcher.invoke() }
66         }
67     }
68 
69     override fun dumpPipeline(d: PipelineDumper) {
70         d.dump("activeNotifListPrivate", activeNotifListPrivate)
71         d.dump("activeNotifCountPrivate", activeNotifCountPrivate)
72         d.dump("hasActiveNotifsPrivate", hasActiveNotifsPrivate)
73     }
74 }
75 
76 /** Read-write implementation of [NotifLiveData] */
77 class NotifLiveDataImpl<T>(
78     private val name: String,
79     initialValue: T,
80     @Main private val mainExecutor: Executor
81 ) : NotifLiveData<T>, PipelineDumpable {
82     private val syncObservers = ListenerSet<Observer<T>>()
83     private val asyncObservers = ListenerSet<Observer<T>>()
84     private val atomicValue = AtomicReference(initialValue)
85     private var lastAsyncValue: T? = null
86 
dispatchToAsyncObserversnull87     private fun dispatchToAsyncObservers() {
88         val value = atomicValue.get()
89         if (lastAsyncValue != value) {
90             lastAsyncValue = value
91             traceSection("NotifLiveData($name).dispatchToAsyncObservers") {
92                 asyncObservers.forEach { it.onChanged(value) }
93             }
94         }
95     }
96 
97     /**
98      * Access or set the current value.
99      *
100      * When setting, sync observers will be dispatched synchronously, and a task will be posted to
101      * dispatch the value to async observers.
102      */
103     override var value: T
104         get() = atomicValue.get()
105         set(value) = setValueAndProvideDispatcher(value).invoke()
106 
107     /**
108      * Set the value, and return a function that when invoked will dispatch to the observers.
109      *
110      * This is intended to allow multiple instances with related data to be updated together and
111      * have their dispatchers invoked after all data has been updated.
112      */
setValueAndProvideDispatchernull113     fun setValueAndProvideDispatcher(value: T): () -> Unit {
114         val oldValue = atomicValue.getAndSet(value)
115         if (oldValue != value) {
116             return {
117                 if (syncObservers.isNotEmpty()) {
118                     traceSection("NotifLiveData($name).dispatchToSyncObservers") {
119                         syncObservers.forEach { it.onChanged(value) }
120                     }
121                 }
122                 if (asyncObservers.isNotEmpty()) {
123                     mainExecutor.execute(::dispatchToAsyncObservers)
124                 }
125             }
126         }
127         return {}
128     }
129 
addSyncObservernull130     override fun addSyncObserver(observer: Observer<T>) {
131         syncObservers.addIfAbsent(observer)
132     }
133 
addAsyncObservernull134     override fun addAsyncObserver(observer: Observer<T>) {
135         asyncObservers.addIfAbsent(observer)
136     }
137 
removeObservernull138     override fun removeObserver(observer: Observer<T>) {
139         syncObservers.remove(observer)
140         asyncObservers.remove(observer)
141     }
142 
dumpPipelinenull143     override fun dumpPipeline(d: PipelineDumper) {
144         d.dump("syncObservers", syncObservers)
145         d.dump("asyncObservers", asyncObservers)
146     }
147 }