• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 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.row
18 
19 import com.android.app.tracing.FlowTracing.traceEach
20 import com.android.app.tracing.TraceUtils.traceAsyncClosable
21 import com.android.app.tracing.TrackGroupUtils.trackGroup
22 import com.android.systemui.CoreStartable
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.dagger.qualifiers.Background
26 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.MutableStateFlow
31 import kotlinx.coroutines.flow.SharingStarted
32 import kotlinx.coroutines.flow.StateFlow
33 import kotlinx.coroutines.flow.map
34 import kotlinx.coroutines.flow.stateIn
35 import kotlinx.coroutines.flow.update
36 import kotlinx.coroutines.launch
37 
38 /**
39  * Tracks notification rebindings in progress as a result of a configuration change (such as density
40  * or font size)
41  */
42 @SysUISingleton
43 class NotificationRebindingTracker
44 @Inject
45 constructor(
46     activeNotificationsInteractor: ActiveNotificationsInteractor,
47     @Background private val bgScope: CoroutineScope,
48     @Application private val appScope: CoroutineScope,
49 ) : CoreStartable {
50 
51     private val rebindingKeys = MutableStateFlow(emptySet<String>())
52     private val activeKeys: Flow<Set<String>> =
53         activeNotificationsInteractor.allRepresentativeNotifications
54             .map { notifications: Map<String, *> ->
55                 notifications.map { (notifKey, _) -> notifKey }.toSet()
56             }
57             .traceEach(trackGroup("shade", "activeKeys"))
58 
59     /**
60      * Emits the current number of active notification rebinding in progress.
61      *
62      * Note the usaged of the [appScope] instead of the bg one is intentional, as we need the value
63      * immediately also in the same frame if it changes.
64      */
65     val rebindingInProgressCount: StateFlow<Int> =
66         rebindingKeys
67             .map { it.size }
68             .traceEach(trackGroup("shade", "rebindingInProgressCount"), traceEmissionCount = true)
69             .stateIn(appScope, started = SharingStarted.Eagerly, initialValue = 0)
70 
71     override fun start() {
72         syncRebindingKeysWithActiveKeys()
73     }
74 
75     private fun syncRebindingKeysWithActiveKeys() {
76         // Let's make sure that the "rebindingKeys" set doesn't contain entries that are not active
77         // anymore.
78         bgScope.launch {
79             activeKeys.collect { activeKeys ->
80                 rebindingKeys.update { currentlyBeingInflated ->
81                     currentlyBeingInflated.intersect(activeKeys)
82                 }
83             }
84         }
85     }
86 
87     /** Should be called when the inflation begins */
88     fun trackRebinding(key: String): RebindFinishedCallback {
89         val endTrace =
90             traceAsyncClosable(
91                 trackGroupName = "Notifications",
92                 trackName = "Rebinding",
93                 sliceName = "Rebinding in progress for $key",
94             )
95         rebindingKeys.value += key
96         return RebindFinishedCallback {
97             endTrace()
98             rebindingKeys.value -= key
99         }
100     }
101 
102     /**
103      * Callback to notify the end of a rebiding. Views are expected to be in the hierarchy when this
104      * is called.
105      */
106     fun interface RebindFinishedCallback {
107         fun onFinished()
108     }
109 }
110