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

Commit 417ff972 authored by Darrell Shi's avatar Darrell Shi
Browse files

Dynamically size and order ongoing content

- UMO is dynamically sized based on the number of ongoing content
- Ongoing cards are ordered based on creation time

Test: atest CommunalInteractorTest
Test: atest CommunalMediaRepositoryImplTest
Bug: 318423695
Bug: 311245493
Fix: 311245493
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT
Change-Id: I5d8086e77d42cde7420e17c1a77f3b6cd7c5b7c9
parent 13627b32
Loading
Loading
Loading
Loading
+29 −41
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 * 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.
@@ -25,13 +25,13 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -59,74 +59,62 @@ class CommunalMediaRepositoryImplTest : SysuiTestCase() {
    }

    @Test
    fun mediaPlaying_defaultsToFalse() =
    fun hasAnyMediaOrRecommendation_defaultsToFalse() =
        testScope.runTest {
            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)

            val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            val mediaModel = collectLastValue(mediaRepository.mediaModel)
            runCurrent()
            assertThat(isMediaPlaying()).isFalse()
            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
        }

    @Test
    fun mediaPlaying_emitsInitialValue() =
    fun mediaModel_updatesWhenMediaDataLoaded() =
        testScope.runTest {
            // Start with media available.
            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)

            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)

            val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            runCurrent()
            assertThat(isMediaPlaying()).isTrue()
        }

    @Test
    fun mediaPlaying_updatesWhenMediaDataLoaded() =
        testScope.runTest {
            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
            // Listener is added
            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())

            // Initial value is false.
            var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            val mediaModel = collectLastValue(mediaRepository.mediaModel)
            runCurrent()
            assertThat(isMediaPlaying()).isFalse()

            // Listener is added
            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()

            // Change to media available and notify the listener.
            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
            whenever(mediaData.createdTimestampMillis).thenReturn(1234L)
            mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)

            // mediaPlaying now returns true.
            isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            runCurrent()
            assertThat(isMediaPlaying()).isTrue()

            // Media active now returns true.
            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
            assertThat(mediaModel()?.createdTimestampMillis).isEqualTo(1234L)
        }

    @Test
    fun mediaPlaying_updatesWhenMediaDataRemoved() =
    fun mediaModel_updatesWhenMediaDataRemoved() =
        testScope.runTest {
            // Start with media available.
            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)

            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)

            // Initial value is true.
            var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            // Listener is added
            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())

            // Change to media available and notify the listener.
            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
            mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
            runCurrent()
            assertThat(isMediaPlaying()).isTrue()

            // Listener is added.
            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
            // Media active now returns true.
            val mediaModel = collectLastValue(mediaRepository.mediaModel)
            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()

            // Change to media unavailable and notify the listener.
            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
            mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)

            // mediaPlaying now returns false.
            isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
            mediaDataListenerCaptor.value.onMediaDataRemoved("key")
            runCurrent()
            assertThat(isMediaPlaying()).isFalse()

            // Media active now returns false.
            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
        }
}
+64 −17
Original line number Diff line number Diff line
@@ -148,25 +148,29 @@ class CommunalInteractorTest : SysuiTestCase() {
            whenever(target1.smartspaceTargetId).thenReturn("target1")
            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_WEATHER)
            whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
            whenever(target1.creationTimeMillis).thenReturn(0L)

            // Does not have RemoteViews
            val target2 = mock(SmartspaceTarget::class.java)
            whenever(target1.smartspaceTargetId).thenReturn("target2")
            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
            whenever(target1.remoteViews).thenReturn(null)
            whenever(target2.smartspaceTargetId).thenReturn("target2")
            whenever(target2.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
            whenever(target2.remoteViews).thenReturn(null)
            whenever(target2.creationTimeMillis).thenReturn(0L)

            // Timer and has RemoteViews
            val target3 = mock(SmartspaceTarget::class.java)
            whenever(target1.smartspaceTargetId).thenReturn("target3")
            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
            whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
            whenever(target3.smartspaceTargetId).thenReturn("target3")
            whenever(target3.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
            whenever(target3.remoteViews).thenReturn(mock(RemoteViews::class.java))
            whenever(target3.creationTimeMillis).thenReturn(0L)

            val targets = listOf(target1, target2, target3)
            smartspaceRepository.setCommunalSmartspaceTargets(targets)

            val smartspaceContent by collectLastValue(underTest.smartspaceContent)
            val smartspaceContent by collectLastValue(underTest.ongoingContent)
            assertThat(smartspaceContent?.size).isEqualTo(1)
            assertThat(smartspaceContent?.get(0)?.key).isEqualTo("smartspace_target3")
            assertThat(smartspaceContent?.get(0)?.key)
                .isEqualTo(CommunalContentModel.KEY.smartspace("target3"))
        }

    @Test
@@ -256,16 +260,12 @@ class CommunalInteractorTest : SysuiTestCase() {

            val targets = mutableListOf<SmartspaceTarget>()
            for (index in 0 until totalTargets) {
                val target = mock(SmartspaceTarget::class.java)
                whenever(target.smartspaceTargetId).thenReturn("target$index")
                whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
                whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
                targets.add(target)
                targets.add(smartspaceTimer(index.toString()))
            }

            smartspaceRepository.setCommunalSmartspaceTargets(targets)

            val smartspaceContent by collectLastValue(underTest.smartspaceContent)
            val smartspaceContent by collectLastValue(underTest.ongoingContent)
            assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
            for (index in 0 until totalTargets) {
                assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index])
@@ -279,13 +279,51 @@ class CommunalInteractorTest : SysuiTestCase() {
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)

            // Media is playing.
            mediaRepository.mediaPlaying.value = true
            mediaRepository.mediaActive()

            val umoContent by collectLastValue(underTest.umoContent)
            val umoContent by collectLastValue(underTest.ongoingContent)

            assertThat(umoContent?.size).isEqualTo(1)
            assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
            assertThat(umoContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
            assertThat(umoContent?.get(0)?.key).isEqualTo(CommunalContentModel.KEY.umo())
        }

    @Test
    fun ongoing_shouldOrderAndSizeByTimestamp() =
        testScope.runTest {
            // Keyguard showing, and tutorial completed.
            keyguardRepository.setKeyguardShowing(true)
            keyguardRepository.setKeyguardOccluded(false)
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)

            // Timer1 started
            val timer1 = smartspaceTimer("timer1", timestamp = 1L)
            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1))

            // Umo started
            mediaRepository.mediaActive(timestamp = 2L)

            // Timer2 started
            val timer2 = smartspaceTimer("timer2", timestamp = 3L)
            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1, timer2))

            // Timer3 started
            val timer3 = smartspaceTimer("timer3", timestamp = 4L)
            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1, timer2, timer3))

            val ongoingContent by collectLastValue(underTest.ongoingContent)
            assertThat(ongoingContent?.size).isEqualTo(4)
            assertThat(ongoingContent?.get(0)?.key)
                .isEqualTo(CommunalContentModel.KEY.smartspace("timer3"))
            assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.FULL)
            assertThat(ongoingContent?.get(1)?.key)
                .isEqualTo(CommunalContentModel.KEY.smartspace("timer2"))
            assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.THIRD)
            assertThat(ongoingContent?.get(2)?.key).isEqualTo(CommunalContentModel.KEY.umo())
            assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.THIRD)
            assertThat(ongoingContent?.get(3)?.key)
                .isEqualTo(CommunalContentModel.KEY.smartspace("timer1"))
            assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.THIRD)
        }

    @Test
@@ -334,4 +372,13 @@ class CommunalInteractorTest : SysuiTestCase() {
            underTest.showWidgetEditor()
            verify(editWidgetsActivityStarter).startActivity()
        }

    private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
        val timer = mock(SmartspaceTarget::class.java)
        whenever(timer.smartspaceTargetId).thenReturn(id)
        whenever(timer.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
        whenever(timer.remoteViews).thenReturn(mock(RemoteViews::class.java))
        whenever(timer.creationTimeMillis).thenReturn(timestamp)
        return timer
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))

            // Media playing.
            mediaRepository.mediaPlaying.value = true
            mediaRepository.mediaActive()

            val communalContent by collectLastValue(underTest.communalContent)

+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ class CommunalViewModelTest : SysuiTestCase() {
            smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))

            // Media playing.
            mediaRepository.mediaPlaying.value = true
            mediaRepository.mediaActive()

            val communalContent by collectLastValue(underTest.communalContent)

+30 −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.communal.data.model

/** Data model of media on the communal hub. */
data class CommunalMediaModel(
    val hasAnyMediaOrRecommendation: Boolean,
    val createdTimestampMillis: Long = 0L,
) {
    companion object {
        val INACTIVE =
            CommunalMediaModel(
                hasAnyMediaOrRecommendation = false,
            )
    }
}
Loading