• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.promoted.ui.viewmodel
18 
19 import androidx.compose.runtime.getValue
20 import com.android.systemui.dump.DumpManager
21 import com.android.systemui.lifecycle.ExclusiveActivatable
22 import com.android.systemui.lifecycle.Hydrator
23 import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor
24 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
25 import com.android.systemui.util.kotlin.ActivatableFlowDumper
26 import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
27 import com.android.systemui.util.time.SystemClock
28 import com.android.systemui.utils.coroutines.flow.transformLatestConflated
29 import dagger.assisted.AssistedFactory
30 import dagger.assisted.AssistedInject
31 import kotlin.time.Duration
32 import kotlin.time.Duration.Companion.milliseconds
33 import kotlin.time.Duration.Companion.seconds
34 import kotlinx.coroutines.awaitCancellation
35 import kotlinx.coroutines.coroutineScope
36 import kotlinx.coroutines.delay
37 import kotlinx.coroutines.flow.Flow
38 import kotlinx.coroutines.flow.distinctUntilChanged
39 import kotlinx.coroutines.flow.map
40 import kotlinx.coroutines.launch
41 
42 class AODPromotedNotificationViewModel
43 @AssistedInject
44 constructor(
45     interactor: AODPromotedNotificationInteractor,
46     systemClock: SystemClock,
47     dumpManager: DumpManager,
48 ) :
49     ExclusiveActivatable(),
50     ActivatableFlowDumper by ActivatableFlowDumperImpl(
51         dumpManager,
52         "AODPromotedNotificationViewModel",
53     ) {
54     private val hydrator = Hydrator("AODPromotedNotificationViewModel.hydrator")
55 
56     private val contentFlow =
57         interactor.content.let {
58             if (DUMP_CONTENT) {
59                 it.dumpWhileCollecting("content")
60             } else {
61                 it
62             }
63         }
64 
65     val content: PromotedNotificationContentModel? by
66         hydrator.hydratedStateOf(traceName = "content", initialValue = null, source = contentFlow)
67 
68     private val audiblyAlertedIconVisibleUntil: Flow<Duration?> =
69         interactor.content
70             .map {
71                 when (it) {
72                     null -> null
73                     else -> it.lastAudiblyAlertedMs.milliseconds + RECENTLY_ALERTED_THRESHOLD
74                 }
75             }
76             .distinctUntilChanged()
77             .dumpWhileCollecting("audiblyAlertedIconVisibleUntil")
78 
79     private val audiblyAlertedIconVisibleFlow: Flow<Boolean> =
80         audiblyAlertedIconVisibleUntil
81             .transformLatestConflated { until ->
82                 val now = systemClock.currentTimeMillis().milliseconds
83 
84                 if (until != null && until > now) {
85                     emit(true)
86                     delay(until - now)
87                 }
88                 emit(false)
89             }
90             .distinctUntilChanged()
91             .dumpWhileCollecting("audiblyAlertedIconVisible")
92 
93     val audiblyAlertedIconVisible: Boolean by
94         hydrator.hydratedStateOf(
95             traceName = "audiblyAlertedIconVisible",
96             initialValue = false,
97             source = audiblyAlertedIconVisibleFlow,
98         )
99 
100     override suspend fun onActivated(): Nothing {
101         coroutineScope {
102             launch { activateFlowDumper() }
103             launch { hydrator.activate() }
104             awaitCancellation()
105         }
106     }
107 
108     @AssistedFactory
109     interface Factory {
110         fun create(): AODPromotedNotificationViewModel
111     }
112 
113     companion object {
114         private val RECENTLY_ALERTED_THRESHOLD = 30.seconds
115 
116         // For local debugging only!
117         private const val DUMP_CONTENT = false
118     }
119 }
120