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