Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +26 −13 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Flags import android.app.Notification import android.graphics.PorterDuff import android.view.LayoutInflater import android.view.View import android.view.View.GONE Loading Loading @@ -70,6 +71,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F val viewModel = rememberViewModel(traceName = "$TAG.viewModel") { viewModelFactory.create() } val content = viewModel.content ?: return val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible key(content.identity) { val layoutResource = content.layoutResource ?: return Loading @@ -88,6 +90,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F AODPromotedNotificationView( layoutResource = layoutResource, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = Modifier.border(borderStroke, borderShape), ) } Loading @@ -98,6 +101,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F fun AODPromotedNotificationView( layoutResource: Int, content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean, modifier: Modifier = Modifier, ) { AndroidView( Loading @@ -117,7 +121,7 @@ fun AODPromotedNotificationView( update = { view -> val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater traceSection("$TAG.update") { updater.update(content) } traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) } }, modifier = modifier, ) Loading Loading @@ -147,7 +151,7 @@ private val PromotedNotificationContentModel.layoutResource: Int? } private class AODPromotedNotificationViewUpdater(root: View) { private val alertedIcon: View? = root.findViewById(R.id.alerted_icon) private val alertedIcon: ImageView? = root.findViewById(R.id.alerted_icon) private val alternateExpandTarget: View? = root.findViewById(R.id.alternate_expand_target) private val appNameDivider: View? = root.findViewById(R.id.app_name_divider) private val appNameText: TextView? = root.findViewById(R.id.app_name_text) Loading Loading @@ -182,7 +186,24 @@ private class AODPromotedNotificationViewUpdater(root: View) { private var oldProgressBar: ProgressBar? = null private val newProgressBar = root.findViewById<View>(R.id.progress) as? NotificationProgressBar fun update(content: PromotedNotificationContentModel) { init { // Hide views that are never visible in the skeleton promoted notification. alternateExpandTarget?.visibility = GONE bigPicture?.visibility = GONE closeButton?.visibility = GONE expandButton?.visibility = GONE leftIcon?.visibility = GONE notificationProgressEndIcon?.visibility = GONE notificationProgressStartIcon?.visibility = GONE // Make one-time changes needed for the skeleton promoted notification. alertedIcon ?.drawable ?.mutate() ?.setColorFilter(SecondaryText.colorInt, PorterDuff.Mode.SRC_IN) } fun update(content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean) { when (content.style) { Style.Base -> updateBase(content) Style.BigPicture -> updateBigPictureStyle(content) Loading @@ -191,6 +212,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { Style.Progress -> updateProgressStyle(content) Style.Ineligible -> {} } alertedIcon?.isVisible = audiblyAlertedIconVisible } private fun updateBase( Loading @@ -210,8 +233,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { private fun updateBigPictureStyle(content: PromotedNotificationContentModel) { updateBase(content) bigPicture?.visibility = GONE } private fun updateBigTextStyle(content: PromotedNotificationContentModel) { Loading Loading @@ -251,9 +272,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { } private fun updateNewProgressBar(content: PromotedNotificationContentModel) { notificationProgressStartIcon?.visibility = GONE notificationProgressEndIcon?.visibility = GONE val newProgressBar = newProgressBar ?: return if (content.newProgress != null && !content.newProgress.isIndeterminate) { Loading @@ -271,11 +289,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTimeAndChronometer(content) updateHeaderDividers(content) leftIcon?.visibility = GONE alternateExpandTarget?.visibility = GONE expandButton?.visibility = GONE closeButton?.visibility = GONE } private fun updateHeaderDividers(content: PromotedNotificationContentModel) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt +66 −2 Original line number Diff line number Diff line Loading @@ -17,16 +17,40 @@ package com.android.systemui.statusbar.notification.promoted.ui.viewmodel import androidx.compose.runtime.getValue import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.util.kotlin.ActivatableFlowDumper import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl import com.android.systemui.util.time.SystemClock import com.android.systemui.utils.coroutines.flow.transformLatestConflated import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class AODPromotedNotificationViewModel @AssistedInject constructor(interactor: AODPromotedNotificationInteractor) : ExclusiveActivatable() { constructor( interactor: AODPromotedNotificationInteractor, systemClock: SystemClock, dumpManager: DumpManager, ) : ExclusiveActivatable(), ActivatableFlowDumper by ActivatableFlowDumperImpl( dumpManager, "AODPromotedNotificationViewModel", ) { private val hydrator = Hydrator("AODPromotedNotificationViewModel.hydrator") val content: PromotedNotificationContentModel? by Loading @@ -36,12 +60,52 @@ constructor(interactor: AODPromotedNotificationInteractor) : ExclusiveActivatabl source = interactor.content, ) private val audiblyAlertedIconVisibleUntil: Flow<Duration?> = interactor.content .map { when (it) { null -> null else -> it.lastAudiblyAlertedMs.milliseconds + RECENTLY_ALERTED_THRESHOLD } } .distinctUntilChanged() .dumpWhileCollecting("audiblyAlertedIconVisibleUntil") private val audiblyAlertedIconVisibleFlow: Flow<Boolean> = audiblyAlertedIconVisibleUntil .transformLatestConflated { until -> val now = systemClock.currentTimeMillis().milliseconds if (until != null && until > now) { emit(true) delay(until - now) } emit(false) } .distinctUntilChanged() .dumpWhileCollecting("audiblyAlertedIconVisible") val audiblyAlertedIconVisible: Boolean by hydrator.hydratedStateOf( traceName = "audiblyAlertedIconVisible", initialValue = false, source = audiblyAlertedIconVisibleFlow, ) override suspend fun onActivated(): Nothing { hydrator.activate() coroutineScope { launch { activateFlowDumper() } launch { hydrator.activate() } awaitCancellation() } } @AssistedFactory interface Factory { fun create(): AODPromotedNotificationViewModel } companion object { private val RECENTLY_ALERTED_THRESHOLD = 30.seconds } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +26 −13 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Flags import android.app.Notification import android.graphics.PorterDuff import android.view.LayoutInflater import android.view.View import android.view.View.GONE Loading Loading @@ -70,6 +71,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F val viewModel = rememberViewModel(traceName = "$TAG.viewModel") { viewModelFactory.create() } val content = viewModel.content ?: return val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible key(content.identity) { val layoutResource = content.layoutResource ?: return Loading @@ -88,6 +90,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F AODPromotedNotificationView( layoutResource = layoutResource, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = Modifier.border(borderStroke, borderShape), ) } Loading @@ -98,6 +101,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F fun AODPromotedNotificationView( layoutResource: Int, content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean, modifier: Modifier = Modifier, ) { AndroidView( Loading @@ -117,7 +121,7 @@ fun AODPromotedNotificationView( update = { view -> val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater traceSection("$TAG.update") { updater.update(content) } traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) } }, modifier = modifier, ) Loading Loading @@ -147,7 +151,7 @@ private val PromotedNotificationContentModel.layoutResource: Int? } private class AODPromotedNotificationViewUpdater(root: View) { private val alertedIcon: View? = root.findViewById(R.id.alerted_icon) private val alertedIcon: ImageView? = root.findViewById(R.id.alerted_icon) private val alternateExpandTarget: View? = root.findViewById(R.id.alternate_expand_target) private val appNameDivider: View? = root.findViewById(R.id.app_name_divider) private val appNameText: TextView? = root.findViewById(R.id.app_name_text) Loading Loading @@ -182,7 +186,24 @@ private class AODPromotedNotificationViewUpdater(root: View) { private var oldProgressBar: ProgressBar? = null private val newProgressBar = root.findViewById<View>(R.id.progress) as? NotificationProgressBar fun update(content: PromotedNotificationContentModel) { init { // Hide views that are never visible in the skeleton promoted notification. alternateExpandTarget?.visibility = GONE bigPicture?.visibility = GONE closeButton?.visibility = GONE expandButton?.visibility = GONE leftIcon?.visibility = GONE notificationProgressEndIcon?.visibility = GONE notificationProgressStartIcon?.visibility = GONE // Make one-time changes needed for the skeleton promoted notification. alertedIcon ?.drawable ?.mutate() ?.setColorFilter(SecondaryText.colorInt, PorterDuff.Mode.SRC_IN) } fun update(content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean) { when (content.style) { Style.Base -> updateBase(content) Style.BigPicture -> updateBigPictureStyle(content) Loading @@ -191,6 +212,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { Style.Progress -> updateProgressStyle(content) Style.Ineligible -> {} } alertedIcon?.isVisible = audiblyAlertedIconVisible } private fun updateBase( Loading @@ -210,8 +233,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { private fun updateBigPictureStyle(content: PromotedNotificationContentModel) { updateBase(content) bigPicture?.visibility = GONE } private fun updateBigTextStyle(content: PromotedNotificationContentModel) { Loading Loading @@ -251,9 +272,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { } private fun updateNewProgressBar(content: PromotedNotificationContentModel) { notificationProgressStartIcon?.visibility = GONE notificationProgressEndIcon?.visibility = GONE val newProgressBar = newProgressBar ?: return if (content.newProgress != null && !content.newProgress.isIndeterminate) { Loading @@ -271,11 +289,6 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTimeAndChronometer(content) updateHeaderDividers(content) leftIcon?.visibility = GONE alternateExpandTarget?.visibility = GONE expandButton?.visibility = GONE closeButton?.visibility = GONE } private fun updateHeaderDividers(content: PromotedNotificationContentModel) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt +66 −2 Original line number Diff line number Diff line Loading @@ -17,16 +17,40 @@ package com.android.systemui.statusbar.notification.promoted.ui.viewmodel import androidx.compose.runtime.getValue import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.util.kotlin.ActivatableFlowDumper import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl import com.android.systemui.util.time.SystemClock import com.android.systemui.utils.coroutines.flow.transformLatestConflated import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class AODPromotedNotificationViewModel @AssistedInject constructor(interactor: AODPromotedNotificationInteractor) : ExclusiveActivatable() { constructor( interactor: AODPromotedNotificationInteractor, systemClock: SystemClock, dumpManager: DumpManager, ) : ExclusiveActivatable(), ActivatableFlowDumper by ActivatableFlowDumperImpl( dumpManager, "AODPromotedNotificationViewModel", ) { private val hydrator = Hydrator("AODPromotedNotificationViewModel.hydrator") val content: PromotedNotificationContentModel? by Loading @@ -36,12 +60,52 @@ constructor(interactor: AODPromotedNotificationInteractor) : ExclusiveActivatabl source = interactor.content, ) private val audiblyAlertedIconVisibleUntil: Flow<Duration?> = interactor.content .map { when (it) { null -> null else -> it.lastAudiblyAlertedMs.milliseconds + RECENTLY_ALERTED_THRESHOLD } } .distinctUntilChanged() .dumpWhileCollecting("audiblyAlertedIconVisibleUntil") private val audiblyAlertedIconVisibleFlow: Flow<Boolean> = audiblyAlertedIconVisibleUntil .transformLatestConflated { until -> val now = systemClock.currentTimeMillis().milliseconds if (until != null && until > now) { emit(true) delay(until - now) } emit(false) } .distinctUntilChanged() .dumpWhileCollecting("audiblyAlertedIconVisible") val audiblyAlertedIconVisible: Boolean by hydrator.hydratedStateOf( traceName = "audiblyAlertedIconVisible", initialValue = false, source = audiblyAlertedIconVisibleFlow, ) override suspend fun onActivated(): Nothing { hydrator.activate() coroutineScope { launch { activateFlowDumper() } launch { hydrator.activate() } awaitCancellation() } } @AssistedFactory interface Factory { fun create(): AODPromotedNotificationViewModel } companion object { private val RECENTLY_ALERTED_THRESHOLD = 30.seconds } }