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

Commit 8a13d66d authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[RONs] Drawable loading for AOD Promoted Notification" into main

parents eb762903 715a49b7
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.app.Notification.ProgressStyle.Segment
import android.app.PendingIntent
import android.app.Person
import android.content.Intent
import android.graphics.drawable.Icon
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,6 +39,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
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.row.RowImageInflater
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -49,6 +51,8 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private val underTest = kosmos.promotedNotificationContentExtractor
    private val rowImageInflater = RowImageInflater.newInstance(previousIndex = null)
    private val imageModelProvider by lazy { rowImageInflater.useForContentModel() }

    @Test
    @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
@@ -294,14 +298,18 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {

    private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? {
        val recoveredBuilder = Notification.Builder(context, entry.sbn.notification)
        return underTest.extractContent(entry, recoveredBuilder)
        return underTest.extractContent(entry, recoveredBuilder, imageModelProvider)
    }

    private fun createEntry(
        promoted: Boolean = true,
        builderBlock: Notification.Builder.() -> Unit = {},
    ): NotificationEntry {
        val notif = Notification.Builder(context, "channel").also(builderBlock).build()
        val notif =
            Notification.Builder(context, "channel")
                .setSmallIcon(Icon.createWithContentUri("content://foo/bar"))
                .also(builderBlock)
                .build()
        if (promoted) {
            notif.flags = FLAG_PROMOTED_ONGOING
        }
+1 −0
Original line number Diff line number Diff line
@@ -203,6 +203,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
        val result =
            NotificationRowContentBinderImpl.InflationProgress(
                packageContext = mContext,
                rowImageInflater = RowImageInflater.newInstance(null),
                remoteViews = NewRemoteViews(),
                contentModel = NotificationContentModel(headsUpStatusBarModel),
                promotedContent = null,
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.row

import android.graphics.drawable.Icon
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
import com.android.systemui.statusbar.notification.row.shared.ImageModel
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.SmallSquare
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(PromotedNotificationUiAod.FLAG_NAME)
class RowImageInflaterTest : SysuiTestCase() {
    private lateinit var rowImageInflater: RowImageInflater

    private val resIcon1 = Icon.createWithResource(context, android.R.drawable.ic_info)
    private val resIcon2 = Icon.createWithResource(context, android.R.drawable.ic_delete)
    private val badUriIcon = Icon.createWithContentUri("content://com.test/does_not_exist")

    @Before
    fun setUp() {
        rowImageInflater = RowImageInflater.newInstance(null)
    }

    @Test
    fun getNewImageIndex_returnsNullWhenUnused() {
        assertThat(rowImageInflater.getNewImageIndex()).isNull()
    }

    @Test
    fun getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() {
        assertThat(getImageModelsForIcons()).isEmpty()
        val result = rowImageInflater.getNewImageIndex()
        assertThat(result).isNotNull()
        assertThat(result?.contentsForTesting).isEmpty()
    }

    @Test
    fun getNewImageIndex_returnsSingleImageWhenOneImageLoaded() {
        assertThat(getImageModelsForIcons(resIcon1)).hasSize(1)
        val result = rowImageInflater.getNewImageIndex()
        assertThat(result).isNotNull()
        assertThat(result?.contentsForTesting).hasSize(1)
    }

    @Test
    fun exampleFirstGeneration() {
        // GIVEN various models are required
        val providedModels = getImageModelsForIcons(resIcon1, badUriIcon, resIcon1, resIcon2)

        // VERIFY that we get 4 models, and the two with the same value are shared
        assertThat(providedModels).hasSize(4)
        assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[1])
        assertThat(providedModels[0]).isSameInstanceAs(providedModels[2])
        assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[3])

        // THEN load images
        rowImageInflater.loadImagesSynchronously(context)

        // VERIFY that the valid drawables are loaded
        assertThat(providedModels[0].drawable).isNotNull()
        assertThat(providedModels[1].drawable).isNull()
        assertThat(providedModels[2].drawable).isNotNull()
        assertThat(providedModels[3].drawable).isNotNull()

        // VERIFY the returned index has all 3 entries, 2 of which have drawables
        val indexGen1 = rowImageInflater.getNewImageIndex()
        assertThat(indexGen1).isNotNull()
        assertThat(indexGen1?.contentsForTesting).hasSize(3)
        assertThat(indexGen1?.contentsForTesting?.mapNotNull { it.drawable }).hasSize(2)
    }

    @Test
    fun exampleSecondGeneration_whichLoadsNothing() {
        exampleFirstGeneration()

        // THEN start a new generation of the inflation
        rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex())

        getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded()
    }

    @Test
    fun exampleSecondGeneration_whichLoadsOneImage() {
        exampleFirstGeneration()

        // THEN start a new generation of the inflation
        rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex())

        getNewImageIndex_returnsSingleImageWhenOneImageLoaded()
    }

    private fun getImageModelsForIcons(vararg icons: Icon): List<ImageModel> {
        val provider = rowImageInflater.useForContentModel()
        return icons.map { icon ->
            requireNotNull(provider.getImageModel(icon, SmallSquare)) { "null model for $icon" }
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -322,7 +322,7 @@ public interface NotificationsModule {
        if (PromotedNotificationContentModel.featureFlagEnabled()) {
            return implProvider.get();
        } else {
            return (entry, recoveredBuilder) -> null;
            return (entry, recoveredBuilder, imageModelProvider) -> null;
        }
    }

+24 −9
Original line number Diff line number Diff line
@@ -55,12 +55,15 @@ import com.android.internal.widget.NotificationProgressModel
import com.android.internal.widget.NotificationRowIconView
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R as systemuiR
import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.Background
import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.PrimaryText
import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.SecondaryText
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 com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel
import com.android.systemui.statusbar.notification.row.shared.ImageModel
import com.android.systemui.statusbar.notification.row.shared.isNullOrEmpty

@Composable
fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.Factory) {
@@ -171,6 +174,7 @@ private class AODPromotedNotificationViewUpdater(root: View) {
        root.findViewById(R.id.header_text_secondary_divider)
    private val icon: NotificationRowIconView? = root.findViewById(R.id.icon)
    private val leftIcon: ImageView? = root.findViewById(R.id.left_icon)
    private val rightIcon: ImageView? = root.findViewById(R.id.right_icon)
    private val notificationProgressEndIcon: CachingIconView? =
        root.findViewById(R.id.notification_progress_end_icon)
    private val notificationProgressStartIcon: CachingIconView? =
@@ -224,10 +228,16 @@ private class AODPromotedNotificationViewUpdater(root: View) {
        textView: ImageFloatingTextView? = null,
        showOldProgress: Boolean = true,
    ) {
        // Icon binding must be called in this order
        updateImageView(icon, content.smallIcon)
        icon?.setBackgroundColor(Background.colorInt)
        icon?.originalIconColor = PrimaryText.colorInt

        updateHeader(content, hideTitle = true)

        updateTitle(title, content)
        updateText(textView ?: text, content)
        updateImageView(rightIcon, content.skeletonLargeIcon)

        if (showOldProgress) {
            updateOldProgressBar(content)
@@ -327,6 +337,7 @@ private class AODPromotedNotificationViewUpdater(root: View) {
        updateTimeAndChronometer(content)
        updateConversationHeaderDividers(content, hideTitle = true)

        updateImageView(verificationIcon, content.verificationIcon)
        updateTextView(verificationText, content.verificationText)
    }

@@ -337,7 +348,8 @@ private class AODPromotedNotificationViewUpdater(root: View) {
        val hasTitle = content.title != null && !hideTitle
        val hasAppName = content.appName != null
        val hasTimeOrChronometer = content.time != null
        val hasVerification = content.verificationIcon != null || content.verificationText != null
        val hasVerification =
            !content.verificationIcon.isNullOrEmpty() || !content.verificationText.isNullOrEmpty()

        val hasTextBeforeAppName = hasTitle
        val hasTextBeforeTime = hasTitle || hasAppName
@@ -415,15 +427,18 @@ private class AODPromotedNotificationViewUpdater(root: View) {
        text: CharSequence?,
        color: AodPromotedNotificationColor = SecondaryText,
    ) {
        if (view == null) return
        setTextViewColor(view, color)

        if (text != null && text.isNotEmpty()) {
            view?.text = text.toSkeleton()
            view?.visibility = VISIBLE
        } else {
            view?.text = ""
            view?.visibility = GONE
        view.text = text?.toSkeleton() ?: ""
        view.isVisible = !text.isNullOrEmpty()
    }

    private fun updateImageView(view: ImageView?, model: ImageModel?) {
        if (view == null) return
        val drawable = model?.drawable
        view.setImageDrawable(drawable)
        view.isVisible = drawable != null
    }

    private fun setTextViewColor(view: TextView?, color: AodPromotedNotificationColor) {
@@ -464,7 +479,7 @@ private fun Notification.ProgressStyle.Point.toSkeleton(): Notification.Progress
}

private enum class AodPromotedNotificationColor(colorUInt: UInt) {
    Background(0x00000000u),
    Background(0xFF000000u),
    PrimaryText(0xFFFFFFFFu),
    SecondaryText(0xFFCCCCCCu);

Loading