Merge "Show the UMO in the hub mode UI" into main
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 77b844d..1429782 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,6 +25,7 @@
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
@@ -48,6 +49,8 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.res.R
@Composable
@@ -73,6 +76,7 @@
CommunalContent(
modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
model = communalContent[index],
+ viewModel = viewModel,
deleteOnClick = viewModel::onDeleteWidget,
size =
SizeF(
@@ -94,6 +98,7 @@
@Composable
private fun CommunalContent(
model: CommunalContentModel,
+ viewModel: CommunalViewModel,
size: SizeF,
deleteOnClick: (id: Int) -> Unit,
modifier: Modifier = Modifier,
@@ -102,6 +107,7 @@
is CommunalContentModel.Widget -> WidgetContent(model, size, deleteOnClick, modifier)
is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
+ is CommunalContentModel.Umo -> Umo(viewModel, modifier)
}
}
@@ -155,6 +161,31 @@
Card(modifier = modifier, content = {})
}
+@Composable
+private fun Umo(viewModel: CommunalViewModel, modifier: Modifier = Modifier) {
+ AndroidView(
+ modifier =
+ modifier
+ .width(Dimensions.CardWidth)
+ .height(Dimensions.CardHeightThird)
+ .padding(Dimensions.Spacing),
+ factory = {
+ viewModel.mediaHost.expansion = MediaHostState.EXPANDED
+ viewModel.mediaHost.showsOnlyActiveMedia = false
+ viewModel.mediaHost.falsingProtectionNeeded = false
+ viewModel.mediaHost.init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
+ viewModel.mediaHost.hostView.layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ viewModel.mediaHost.hostView
+ },
+ // For reusing composition in lazy lists.
+ onReset = {},
+ )
+}
+
private fun CommunalContentSize.dp(): Dp {
return when (this) {
CommunalContentSize.FULL -> Dimensions.CardHeightFull
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index c3421de..273adcf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.dagger
import com.android.systemui.communal.data.db.CommunalDatabaseModule
+import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
@@ -26,6 +27,7 @@
includes =
[
CommunalRepositoryModule::class,
+ CommunalMediaRepositoryModule::class,
CommunalTutorialRepositoryModule::class,
CommunalWidgetRepositoryModule::class,
CommunalDatabaseModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
new file mode 100644
index 0000000..e41c322
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+
+/** Encapsulates the state of smartspace in communal. */
+interface CommunalMediaRepository {
+ val mediaPlaying: Flow<Boolean>
+}
+
+@SysUISingleton
+class CommunalMediaRepositoryImpl
+@Inject
+constructor(
+ private val mediaDataManager: MediaDataManager,
+) : CommunalMediaRepository {
+
+ private val mediaDataListener =
+ object : MediaDataManager.Listener {
+ override fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData,
+ immediately: Boolean,
+ receivedSmartspaceCardLatency: Int,
+ isSsReactivated: Boolean
+ ) {
+ if (!mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = true
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ if (mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = false
+ }
+ }
+
+ private val _mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val mediaPlaying: Flow<Boolean> =
+ _mediaPlaying
+ .onStart {
+ mediaDataManager.addListener(mediaDataListener)
+ _mediaPlaying.value = mediaDataManager.hasAnyMediaOrRecommendation()
+ }
+ .onCompletion { mediaDataManager.removeListener(mediaDataListener) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
new file mode 100644
index 0000000..2c6d9e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 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.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalMediaRepositoryModule {
+ @Binds fun communalMediaRepository(impl: CommunalMediaRepositoryImpl): CommunalMediaRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 524cccf..eb36b19 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -19,6 +19,7 @@
import android.app.smartspace.SmartspaceTarget
import android.appwidget.AppWidgetHost
import android.content.ComponentName
+import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -37,12 +38,14 @@
import kotlinx.coroutines.flow.map
/** Encapsulates business-logic related to communal mode. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalInteractor
@Inject
constructor(
private val communalRepository: CommunalRepository,
private val widgetRepository: CommunalWidgetRepository,
+ mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
tutorialInteractor: CommunalTutorialInteractor,
private val appWidgetHost: AppWidgetHost,
@@ -87,8 +90,8 @@
if (isTutorialMode) {
return@flatMapLatest flowOf(tutorialContent)
}
- combine(smartspaceContent, widgetContent) { smartspace, widgets ->
- smartspace + widgets
+ combine(smartspaceContent, umoContent, widgetContent) { smartspace, umo, widgets ->
+ smartspace + umo + widgets
}
}
@@ -138,4 +141,13 @@
CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
)
+
+ private val umoContent: Flow<List<CommunalContentModel.Umo>> =
+ mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
+ if (mediaPlaying) {
+ flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
+ } else {
+ flowOf(emptyList())
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 69382a5..bb9b4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -53,4 +53,15 @@
) : CommunalContentModel {
override val key = "smartspace_$smartspaceTargetId"
}
+
+ class Umo(
+ override val size: CommunalContentSize,
+ ) : CommunalContentModel {
+ override val key = UMO_KEY
+ }
+
+ companion object {
+ /** Key for the [Umo] in CommunalContentModel. There should only ever be one UMO. */
+ const val UMO_KEY = "umo"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 3df6e7e..5efe6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,7 +21,10 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -30,6 +33,7 @@
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ @Named(MediaModule.COMMUNAL_HUB) val mediaHost: MediaHost,
) {
val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
fun onSceneChanged(scene: CommunalSceneKey) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
new file mode 100644
index 0000000..455f986
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 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.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.media.controls.models.player.MediaData
+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.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalMediaRepositoryImplTest : SysuiTestCase() {
+ @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var mediaData: MediaData
+
+ private val mediaDataListenerCaptor: KotlinArgumentCaptor<MediaDataManager.Listener> by lazy {
+ KotlinArgumentCaptor(MediaDataManager.Listener::class.java)
+ }
+
+ private lateinit var mediaRepository: CommunalMediaRepository
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun mediaPlaying_defaultsToFalse() =
+ testScope.runTest {
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+
+ @Test
+ fun mediaPlaying_emitsInitialValue() =
+ 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)
+
+ // Initial value is false.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+
+ // 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)
+
+ // mediaPlaying now returns true.
+ isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+ }
+
+ @Test
+ fun mediaPlaying_updatesWhenMediaDataRemoved() =
+ testScope.runTest {
+ // Start with media available.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ // Initial value is true.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+
+ // Listener is added.
+ verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
+ // 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)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 5460a1b..08d54c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -24,6 +24,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -58,6 +59,7 @@
private lateinit var tutorialRepository: FakeCommunalTutorialRepository
private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var mediaRepository: FakeCommunalMediaRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var smartspaceRepository: FakeSmartspaceRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
@@ -74,6 +76,7 @@
tutorialRepository = withDeps.tutorialRepository
communalRepository = withDeps.communalRepository
+ mediaRepository = withDeps.mediaRepository
widgetRepository = withDeps.widgetRepository
smartspaceRepository = withDeps.smartspaceRepository
keyguardRepository = withDeps.keyguardRepository
@@ -237,6 +240,66 @@
}
@Test
+ fun umo_mediaPlaying_showsUmo() =
+ testScope.runTest {
+ // Tutorial completed.
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Media is playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent?.size).isEqualTo(1)
+ assertThat(communalContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(communalContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
+ }
+
+ @Test
+ fun contentOrdering() =
+ testScope.runTest {
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Smartspace available.
+ val target = mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
+ smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+ // Media playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ // Order is smart space, then UMO, then widget content.
+ assertThat(communalContent?.size).isEqualTo(4)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.Smartspace::class.java)
+ assertThat(communalContent?.get(1)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(communalContent?.get(2))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ assertThat(communalContent?.get(3))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ }
+
+ @Test
fun listensToSceneChange() =
testScope.runTest {
var desiredScene = collectLastValue(underTest.desiredScene)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
new file mode 100644
index 0000000..3ab1b6c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 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.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeCommunalMediaRepository(
+ override val mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+) : CommunalMediaRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 6c3882f..0c821ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -18,6 +18,7 @@
package com.android.systemui.communal.domain.interactor
import android.appwidget.AppWidgetHost
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -35,6 +36,7 @@
testScope: TestScope = TestScope(),
communalRepository: FakeCommunalRepository = FakeCommunalRepository(),
widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(),
+ mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(),
smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
appWidgetHost: AppWidgetHost = mock(),
@@ -48,6 +50,7 @@
return WithDependencies(
communalRepository,
widgetRepository,
+ mediaRepository,
smartspaceRepository,
tutorialRepository,
withDeps.keyguardRepository,
@@ -57,6 +60,7 @@
CommunalInteractor(
communalRepository,
widgetRepository,
+ mediaRepository,
smartspaceRepository,
withDeps.communalTutorialInteractor,
appWidgetHost,
@@ -67,6 +71,7 @@
data class WithDependencies(
val communalRepository: FakeCommunalRepository,
val widgetRepository: FakeCommunalWidgetRepository,
+ val mediaRepository: FakeCommunalMediaRepository,
val smartspaceRepository: FakeSmartspaceRepository,
val tutorialRepository: FakeCommunalTutorialRepository,
val keyguardRepository: FakeKeyguardRepository,