summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt18
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt6
7 files changed, 157 insertions, 53 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
index d36dbbe8d36f..d4518e7299da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
@@ -21,9 +21,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.parameterizeSceneContainerFlag
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.shared.model.MediaData
@@ -47,7 +48,8 @@ import platform.test.runner.parameterized.Parameters
class MediaControlChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val mediaControlChipInteractor by lazy { kosmos.mediaControlChipInteractor }
- private val Kosmos.underTest by Kosmos.Fixture { kosmos.mediaControlChipViewModel }
+ private val Kosmos.underTest by
+ Kosmos.Fixture { kosmos.mediaControlChipViewModelFactory.create() }
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
companion object {
@@ -62,6 +64,7 @@ class MediaControlChipViewModelTest(flags: FlagsParameterization) : SysuiTestCas
fun setUp() {
MockitoAnnotations.initMocks(this)
mediaControlChipInteractor.initialize()
+ kosmos.underTest.activateIn(kosmos.testScope)
}
init {
@@ -71,7 +74,7 @@ class MediaControlChipViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun chip_noActiveMedia_IsHidden() =
kosmos.runTest {
- val chip by collectLastValue(underTest.chip)
+ val chip = underTest.chip
assertThat(chip).isInstanceOf(PopupChipModel.Hidden::class.java)
}
@@ -79,30 +82,26 @@ class MediaControlChipViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun chip_activeMedia_IsShown() =
kosmos.runTest {
- val chip by collectLastValue(underTest.chip)
-
val userMedia = MediaData(active = true, song = "test")
updateMedia(userMedia)
- assertThat(chip).isInstanceOf(PopupChipModel.Shown::class.java)
+ assertThat(underTest.chip).isInstanceOf(PopupChipModel.Shown::class.java)
}
@Test
fun chip_songNameChanges_chipTextUpdated() =
kosmos.runTest {
- val chip by collectLastValue(underTest.chip)
-
val initialSongName = "Initial Song"
val newSongName = "New Song"
val userMedia = MediaData(active = true, song = initialSongName)
updateMedia(userMedia)
- assertThat(chip).isInstanceOf(PopupChipModel.Shown::class.java)
- assertThat((chip as PopupChipModel.Shown).chipText).isEqualTo(initialSongName)
+ assertThat(underTest.chip).isInstanceOf(PopupChipModel.Shown::class.java)
+ assertThat((underTest.chip as PopupChipModel.Shown).chipText).isEqualTo(initialSongName)
val updatedUserMedia = userMedia.copy(song = newSongName)
updateMedia(updatedUserMedia)
- assertThat((chip as PopupChipModel.Shown).chipText).isEqualTo(newSongName)
+ assertThat((underTest.chip as PopupChipModel.Shown).chipText).isEqualTo(newSongName)
}
private fun updateMedia(mediaData: MediaData) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
new file mode 100644
index 000000000000..134ab9322df0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.featurepods.media.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModelFactory
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnableFlags(StatusBarPopupChips.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val underTest = kosmos.statusBarPopupChipsViewModelFactory.create()
+
+ @Before
+ fun setUp() {
+ underTest.activateIn(kosmos.testScope)
+ }
+
+ @Test
+ fun shownPopupChips_allHidden_empty() =
+ kosmos.runTest {
+ val shownPopupChips = underTest.shownPopupChips
+ assertThat(shownPopupChips).isEmpty()
+ }
+
+ @Test
+ fun shownPopupChips_activeMedia_restHidden_mediaControlChipShown() =
+ kosmos.runTest {
+ val shownPopupChips = underTest.shownPopupChips
+ val userMedia = MediaData(active = true, song = "test")
+ val instanceId = userMedia.instanceId
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ Snapshot.takeSnapshot {
+ assertThat(shownPopupChips).hasSize(1)
+ assertThat(shownPopupChips.first().chipId).isEqualTo(PopupChipId.MediaControl)
+ }
+ }
+
+ @Test
+ fun shownPopupChips_mediaChipToggled_popupShown() =
+ kosmos.runTest {
+ val shownPopupChips = underTest.shownPopupChips
+
+ val userMedia = MediaData(active = true, song = "test")
+ val instanceId = userMedia.instanceId
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ Snapshot.takeSnapshot {
+ assertThat(shownPopupChips).hasSize(1)
+ val mediaChip = shownPopupChips.first()
+ assertThat(mediaChip.isPopupShown).isFalse()
+
+ mediaChip.showPopup.invoke()
+ assertThat(shownPopupChips.first().isPopupShown).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
index 90f97df295f5..ec6508717e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
@@ -17,51 +17,51 @@
package com.android.systemui.statusbar.featurepods.media.ui.viewmodel
import android.content.Context
+import androidx.compose.runtime.getValue
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.statusbar.featurepods.media.domain.interactor.MediaControlChipInteractor
import com.android.systemui.statusbar.featurepods.media.shared.model.MediaControlChipModel
import com.android.systemui.statusbar.featurepods.popups.shared.model.HoverBehavior
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/**
* [StatusBarPopupChipViewModel] for a media control chip in the status bar. This view model is
* responsible for converting the [MediaControlChipModel] to a [PopupChipModel] that can be used to
* display a media control chip.
*/
-@SysUISingleton
class MediaControlChipViewModel
-@Inject
+@AssistedInject
constructor(
- @Background private val backgroundScope: CoroutineScope,
@Application private val applicationContext: Context,
mediaControlChipInteractor: MediaControlChipInteractor,
-) : StatusBarPopupChipViewModel {
-
+) : StatusBarPopupChipViewModel, ExclusiveActivatable() {
+ private val hydrator: Hydrator = Hydrator("MediaControlChipViewModel.hydrator")
/**
- * A [StateFlow] of the current [PopupChipModel]. This flow emits a new [PopupChipModel]
+ * A snapshot [State] of the current [PopupChipModel]. This emits a new [PopupChipModel]
* whenever the underlying [MediaControlChipModel] changes.
*/
- override val chip: StateFlow<PopupChipModel> =
- mediaControlChipInteractor.mediaControlChipModel
- .map { mediaControlChipModel -> toPopupChipModel(mediaControlChipModel) }
- .stateIn(
- backgroundScope,
- SharingStarted.WhileSubscribed(),
- PopupChipModel.Hidden(PopupChipId.MediaControl),
- )
+ override val chip: PopupChipModel by
+ hydrator.hydratedStateOf(
+ traceName = "chip",
+ initialValue = PopupChipModel.Hidden(PopupChipId.MediaControl),
+ source =
+ mediaControlChipInteractor.mediaControlChipModel.map { model ->
+ toPopupChipModel(model)
+ },
+ )
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
private fun toPopupChipModel(model: MediaControlChipModel?): PopupChipModel {
if (model == null || model.songName.isNullOrEmpty()) {
@@ -96,7 +96,12 @@ constructor(
return HoverBehavior.Button(
icon = Icon.Loaded(drawable = icon, contentDescription = contentDescription),
- onIconPressed = { backgroundScope.launch { action.run() } },
+ onIconPressed = { action.run() },
)
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): MediaControlChipViewModel
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
index 5712be30ccd6..38f24137d355 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
@@ -16,14 +16,14 @@
package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+import com.android.systemui.lifecycle.Activatable
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
-import kotlinx.coroutines.flow.StateFlow
/**
* Interface for a view model that knows the display requirements for a single type of status bar
* popup chip.
*/
-interface StatusBarPopupChipViewModel {
- /** A flow modeling the popup chip that should be shown (or not shown). */
- val chip: StateFlow<PopupChipModel>
+interface StatusBarPopupChipViewModel : Activatable {
+ /** A snapshot [State] modeling the popup chip that should be shown (or not shown). */
+ val chip: PopupChipModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
index 33bf90defb48..35f1a9981691 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -21,7 +21,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.MediaControlChipViewModel
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
@@ -36,18 +35,17 @@ import kotlinx.coroutines.flow.map
*/
class StatusBarPopupChipsViewModel
@AssistedInject
-constructor(mediaControlChip: MediaControlChipViewModel) : ExclusiveActivatable() {
- private val hydrator: Hydrator = Hydrator("StatusBarPopupChipsViewModel.hydrator")
+constructor(mediaControlChipFactory: MediaControlChipViewModel.Factory) : ExclusiveActivatable() {
+
+ private val mediaControlChip by lazy { mediaControlChipFactory.create() }
/** The ID of the current chip that is showing its popup, or `null` if no chip is shown. */
private var currentShownPopupChipId by mutableStateOf<PopupChipId?>(null)
- private val incomingPopupChipBundle: PopupChipBundle by
- hydrator.hydratedStateOf(
- traceName = "incomingPopupChipBundle",
- initialValue = PopupChipBundle(),
- source = mediaControlChip.chip.map { chip -> PopupChipBundle(media = chip) },
- )
+ private val incomingPopupChipBundle: PopupChipBundle by derivedStateOf {
+ val mediaChip = mediaControlChip.chip
+ PopupChipBundle(media = mediaChip)
+ }
val shownPopupChips: List<PopupChipModel.Shown> by derivedStateOf {
if (StatusBarPopupChips.isEnabled) {
@@ -66,7 +64,7 @@ constructor(mediaControlChip: MediaControlChipViewModel) : ExclusiveActivatable(
}
override suspend fun onActivated(): Nothing {
- hydrator.activate()
+ mediaControlChip.activate()
}
private data class PopupChipBundle(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt
index 7145907a14a8..39391d03a44b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt
@@ -18,14 +18,19 @@ package com.android.systemui.statusbar.featurepods.media.ui.viewmodel
import android.content.testableContext
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.featurepods.media.domain.interactor.mediaControlChipInteractor
-val Kosmos.mediaControlChipViewModel: MediaControlChipViewModel by
+private val Kosmos.mediaControlChipViewModel: MediaControlChipViewModel by
Kosmos.Fixture {
MediaControlChipViewModel(
- backgroundScope = applicationCoroutineScope,
applicationContext = testableContext,
mediaControlChipInteractor = mediaControlChipInteractor,
)
}
+
+val Kosmos.mediaControlChipViewModelFactory by
+ Kosmos.Fixture {
+ object : MediaControlChipViewModel.Factory {
+ override fun create(): MediaControlChipViewModel = mediaControlChipViewModel
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
index b876095fefe5..2a3167cb66f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -17,10 +17,12 @@
package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.mediaControlChipViewModel
+import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.mediaControlChipViewModelFactory
private val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
- Kosmos.Fixture { StatusBarPopupChipsViewModel(mediaControlChip = mediaControlChipViewModel) }
+ Kosmos.Fixture {
+ StatusBarPopupChipsViewModel(mediaControlChipFactory = mediaControlChipViewModelFactory)
+ }
val Kosmos.statusBarPopupChipsViewModelFactory by
Kosmos.Fixture {