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

Commit 34103146 authored by Julia Tuttle's avatar Julia Tuttle
Browse files

Add promoted notification ViewModel and (AOD) Interactor

Bug: 369151941
Test: TBD
Flag: android.app.ui_rich_ongoing
Change-Id: Id6070d92bf20a188a4ddf56feaa106a4540f388b
parent 13e2e235
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -27,7 +26,7 @@ import javax.inject.Inject

class PromotedNotificationLogger
@Inject
constructor(@NotificationLog private val buffer: LogBuffer) {
constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
    fun logExtractionSkipped(entry: NotificationEntry, reason: String) {
        buffer.log(
            EXTRACTION_TAG,
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.notification.promoted.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

@SysUISingleton
class AODPromotedNotificationInteractor
@Inject
constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
    val content: Flow<PromotedNotificationContentModel?> =
        activeNotificationsInteractor.topLevelRepresentativeNotifications.map { notifs ->
            notifs.firstNotNullOfOrNull { it.promotedContent }
        }
}
+4 −2
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.notification.promoted.PromotedNotification
 * like the skeleton view on AOD or the status bar chip.
 */
data class PromotedNotificationContentModel(
    val key: String,
    val identity: Identity,

    // for all styles:
    val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
@@ -82,7 +82,7 @@ data class PromotedNotificationContentModel(

        fun build() =
            PromotedNotificationContentModel(
                key = key,
                identity = Identity(key, style),
                skeletonSmallIcon = skeletonSmallIcon,
                appName = appName,
                subText = subText,
@@ -103,6 +103,8 @@ data class PromotedNotificationContentModel(
            )
    }

    data class Identity(val key: String, val style: Style)

    /** The timestamp associated with a notification, along with the mode used to display it. */
    data class When(val time: Long, val mode: Mode) {
        /** The mode used to display a notification's `when` value. */
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.notification.promoted.ui.viewmodel

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Identity
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map

@SysUISingleton
class AODPromotedNotificationViewModel
@Inject
constructor(interactor: AODPromotedNotificationInteractor) {
    private val content: Flow<PromotedNotificationContentModel?> = interactor.content
    private val identity: Flow<Identity?> = content.mapNonNullsKeepingNulls { it.identity }

    val notification: Flow<PromotedNotificationViewModel?> =
        identity.distinctUntilChanged().mapNonNullsKeepingNulls { identity ->
            val updates = interactor.content.filterNotNull().filter { it.identity == identity }
            PromotedNotificationViewModel(identity, updates)
        }
}

private fun <T, R> Flow<T?>.mapNonNullsKeepingNulls(block: (T) -> R): Flow<R?> = map {
    it?.let(block)
}
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.notification.promoted.ui.viewmodel

import android.graphics.drawable.Icon
import com.android.internal.widget.NotificationProgressModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class PromotedNotificationViewModel(
    identity: PromotedNotificationContentModel.Identity,
    content: Flow<PromotedNotificationContentModel>,
) {
    // for all styles:

    val key: String = identity.key
    val style: Style = identity.style

    val skeletonSmallIcon: Flow<Icon?> = content.map { it.skeletonSmallIcon }
    val appName: Flow<CharSequence?> = content.map { it.appName }
    val subText: Flow<CharSequence?> = content.map { it.subText }

    private val time: Flow<When?> = content.map { it.time }
    val whenTime: Flow<Long?> = time.map { it?.time }
    val whenMode: Flow<When.Mode?> = time.map { it?.mode }

    val lastAudiblyAlertedMs: Flow<Long> = content.map { it.lastAudiblyAlertedMs }
    val profileBadgeResId: Flow<Int?> = content.map { it.profileBadgeResId }
    val title: Flow<CharSequence?> = content.map { it.title }
    val text: Flow<CharSequence?> = content.map { it.text }
    val skeletonLargeIcon: Flow<Icon?> = content.map { it.skeletonLargeIcon }

    // for CallStyle:
    val personIcon: Flow<Icon?> = content.map { it.personIcon }
    val personName: Flow<CharSequence?> = content.map { it.personName }
    val verificationIcon: Flow<Icon?> = content.map { it.verificationIcon }
    val verificationText: Flow<CharSequence?> = content.map { it.verificationText }

    // for ProgressStyle:
    val progress: Flow<NotificationProgressModel?> = content.map { it.progress }
}