Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7e01165 authored by Julia Tuttle's avatar Julia Tuttle Committed by Android (Google) Code Review
Browse files

Merge "Plumb recently alerted icon on AOD RONs" into main

parents 0ec9c2a2 fdb2f705
Loading
Loading
Loading
Loading
+26 −13
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -88,6 +90,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F
            AODPromotedNotificationView(
                layoutResource = layoutResource,
                content = content,
                audiblyAlertedIconVisible = audiblyAlertedIconVisible,
                modifier = Modifier.border(borderStroke, borderShape),
            )
        }
@@ -98,6 +101,7 @@ fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.F
fun AODPromotedNotificationView(
    layoutResource: Int,
    content: PromotedNotificationContentModel,
    audiblyAlertedIconVisible: Boolean,
    modifier: Modifier = Modifier,
) {
    AndroidView(
@@ -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,
    )
@@ -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)
@@ -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)
@@ -191,6 +212,8 @@ private class AODPromotedNotificationViewUpdater(root: View) {
            Style.Progress -> updateProgressStyle(content)
            Style.Ineligible -> {}
        }

        alertedIcon?.isVisible = audiblyAlertedIconVisible
    }

    private fun updateBase(
@@ -210,8 +233,6 @@ private class AODPromotedNotificationViewUpdater(root: View) {

    private fun updateBigPictureStyle(content: PromotedNotificationContentModel) {
        updateBase(content)

        bigPicture?.visibility = GONE
    }

    private fun updateBigTextStyle(content: PromotedNotificationContentModel) {
@@ -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) {
@@ -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) {
+66 −2
Original line number Diff line number Diff line
@@ -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
@@ -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
    }
}