Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ fun VolumePanelRoot( ) { val accessibilityTitle = stringResource(R.string.accessibility_volume_settings) val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle() val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null) val components by viewModel.componentsLayout.collectAsStateWithLifecycle() with(VolumePanelComposeScope(state)) { components?.let { componentsState -> Loading packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt +2 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.volume.panel.domain.model.ComponentModel import com.android.systemui.volume.panel.domain.unavailableCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import com.android.systemui.volume.panel.ui.composable.enabledComponents import com.android.systemui.volume.shared.volumePanelLogger import com.google.common.truth.Truth.assertThat import javax.inject.Provider import kotlinx.coroutines.test.runTest Loading @@ -49,6 +50,7 @@ class ComponentsInteractorImplTest : SysuiTestCase() { enabledComponents, { defaultCriteria }, testScope.backgroundScope, volumePanelLogger, criteriaByKey, ) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt +80 −1 Original line number Diff line number Diff line Loading @@ -24,21 +24,30 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.statusbar.policy.fakeConfigurationController import com.android.systemui.testKosmos import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository import com.android.systemui.volume.panel.domain.interactor.criteriaByKey import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor import com.android.systemui.volume.panel.domain.unavailableCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import com.android.systemui.volume.panel.shared.model.mockVolumePanelUiComponentProvider import com.android.systemui.volume.panel.ui.composable.componentByKey import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager import com.android.systemui.volume.panel.ui.layout.componentsLayoutManager import com.android.systemui.volume.shared.volumePanelLogger import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test Loading @@ -55,6 +64,7 @@ class VolumePanelViewModelTest : SysuiTestCase() { volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) } } private val realDumpManager = DumpManager() private val testableResources = context.orCreateTestableResources private lateinit var underTest: VolumePanelViewModel Loading Loading @@ -123,6 +133,60 @@ class VolumePanelViewModelTest : SysuiTestCase() { } } @Test fun testDumpableRegister_unregister() = with(kosmos) { testScope.runTest { val job = launch { applicationCoroutineScope = this underTest = createViewModel() runCurrent() assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }) .isTrue() } runCurrent() job.cancel() assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }).isTrue() } } @Test fun testDumpingState() = test({ componentByKey = mapOf( COMPONENT_1 to mockVolumePanelUiComponentProvider, COMPONENT_2 to mockVolumePanelUiComponentProvider, BOTTOM_BAR to mockVolumePanelUiComponentProvider, ) criteriaByKey = mapOf(COMPONENT_2 to Provider { unavailableCriteria }) }) { testScope.runTest { runCurrent() StringWriter().use { underTest.dump(PrintWriter(it), emptyArray()) assertThat(it.buffer.toString()) .isEqualTo( "volumePanelState=" + "VolumePanelState(orientation=1, isLargeScreen=false)\n" + "componentsLayout=( " + "headerComponents= " + "contentComponents=" + "test_component:1:visible=true, " + "test_component:2:visible=false " + "footerComponents= " + "bottomBarComponent=test_bottom_bar:visible=true )\n" ) } } } @Test fun dismissBroadcast_dismissesPanel() = test { testScope.runTest { Loading @@ -140,11 +204,26 @@ class VolumePanelViewModelTest : SysuiTestCase() { private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) = with(kosmos) { setup() underTest = volumePanelViewModel underTest = createViewModel() test() } private fun Kosmos.createViewModel(): VolumePanelViewModel = VolumePanelViewModel( context.orCreateTestableResources.resources, applicationCoroutineScope, volumePanelComponentFactory, configurationController, broadcastDispatcher, realDumpManager, volumePanelLogger, volumePanelGlobalStateInteractor, ) private companion object { const val DUMPABLE_NAME = "VolumePanelViewModel" const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar" const val COMPONENT_1: VolumePanelComponentKey = "test_component:1" const val COMPONENT_2: VolumePanelComponentKey = "test_component:2" Loading packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt +9 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.data.repository import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.volume.panel.shared.VolumePanelLogger import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState import java.io.PrintWriter import javax.inject.Inject Loading @@ -27,10 +28,15 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update private const val TAG = "VolumePanelGlobalState" private const val TAG = "VolumePanelGlobalStateRepository" @SysUISingleton class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable { class VolumePanelGlobalStateRepository @Inject constructor( dumpManager: DumpManager, private val logger: VolumePanelLogger, ) : Dumpable { private val mutableGlobalState = MutableStateFlow( Loading @@ -48,6 +54,7 @@ class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpMana update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState ) { mutableGlobalState.update(update) logger.onVolumePanelGlobalStateChanged(mutableGlobalState.value) } override fun dump(pw: PrintWriter, args: Array<out String>) { Loading packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt +17 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.domain.interactor import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.domain.model.ComponentModel import com.android.systemui.volume.panel.shared.VolumePanelLogger import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import javax.inject.Inject import javax.inject.Provider Loading @@ -26,8 +27,12 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn interface ComponentsInteractor { Loading @@ -45,6 +50,7 @@ constructor( enabledComponents: Collection<VolumePanelComponentKey>, defaultCriteria: Provider<ComponentAvailabilityCriteria>, @VolumePanelScope coroutineScope: CoroutineScope, private val logger: VolumePanelLogger, private val criteriaByKey: Map< VolumePanelComponentKey, Loading @@ -57,12 +63,18 @@ constructor( combine( enabledComponents.map { componentKey -> val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get() componentCriteria.isAvailable().map { isAvailable -> componentCriteria .isAvailable() .distinctUntilChanged() .conflate() .onEach { logger.onComponentAvailabilityChanged(componentKey, it) } .map { isAvailable -> ComponentModel(componentKey, isAvailable = isAvailable) } } ) { it.asList() } .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1) .stateIn(coroutineScope, SharingStarted.Eagerly, null) .filterNotNull() } Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ fun VolumePanelRoot( ) { val accessibilityTitle = stringResource(R.string.accessibility_volume_settings) val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle() val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null) val components by viewModel.componentsLayout.collectAsStateWithLifecycle() with(VolumePanelComposeScope(state)) { components?.let { componentsState -> Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt +2 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.volume.panel.domain.model.ComponentModel import com.android.systemui.volume.panel.domain.unavailableCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import com.android.systemui.volume.panel.ui.composable.enabledComponents import com.android.systemui.volume.shared.volumePanelLogger import com.google.common.truth.Truth.assertThat import javax.inject.Provider import kotlinx.coroutines.test.runTest Loading @@ -49,6 +50,7 @@ class ComponentsInteractorImplTest : SysuiTestCase() { enabledComponents, { defaultCriteria }, testScope.backgroundScope, volumePanelLogger, criteriaByKey, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt +80 −1 Original line number Diff line number Diff line Loading @@ -24,21 +24,30 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.statusbar.policy.fakeConfigurationController import com.android.systemui.testKosmos import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository import com.android.systemui.volume.panel.domain.interactor.criteriaByKey import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor import com.android.systemui.volume.panel.domain.unavailableCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import com.android.systemui.volume.panel.shared.model.mockVolumePanelUiComponentProvider import com.android.systemui.volume.panel.ui.composable.componentByKey import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager import com.android.systemui.volume.panel.ui.layout.componentsLayoutManager import com.android.systemui.volume.shared.volumePanelLogger import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test Loading @@ -55,6 +64,7 @@ class VolumePanelViewModelTest : SysuiTestCase() { volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) } } private val realDumpManager = DumpManager() private val testableResources = context.orCreateTestableResources private lateinit var underTest: VolumePanelViewModel Loading Loading @@ -123,6 +133,60 @@ class VolumePanelViewModelTest : SysuiTestCase() { } } @Test fun testDumpableRegister_unregister() = with(kosmos) { testScope.runTest { val job = launch { applicationCoroutineScope = this underTest = createViewModel() runCurrent() assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }) .isTrue() } runCurrent() job.cancel() assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }).isTrue() } } @Test fun testDumpingState() = test({ componentByKey = mapOf( COMPONENT_1 to mockVolumePanelUiComponentProvider, COMPONENT_2 to mockVolumePanelUiComponentProvider, BOTTOM_BAR to mockVolumePanelUiComponentProvider, ) criteriaByKey = mapOf(COMPONENT_2 to Provider { unavailableCriteria }) }) { testScope.runTest { runCurrent() StringWriter().use { underTest.dump(PrintWriter(it), emptyArray()) assertThat(it.buffer.toString()) .isEqualTo( "volumePanelState=" + "VolumePanelState(orientation=1, isLargeScreen=false)\n" + "componentsLayout=( " + "headerComponents= " + "contentComponents=" + "test_component:1:visible=true, " + "test_component:2:visible=false " + "footerComponents= " + "bottomBarComponent=test_bottom_bar:visible=true )\n" ) } } } @Test fun dismissBroadcast_dismissesPanel() = test { testScope.runTest { Loading @@ -140,11 +204,26 @@ class VolumePanelViewModelTest : SysuiTestCase() { private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) = with(kosmos) { setup() underTest = volumePanelViewModel underTest = createViewModel() test() } private fun Kosmos.createViewModel(): VolumePanelViewModel = VolumePanelViewModel( context.orCreateTestableResources.resources, applicationCoroutineScope, volumePanelComponentFactory, configurationController, broadcastDispatcher, realDumpManager, volumePanelLogger, volumePanelGlobalStateInteractor, ) private companion object { const val DUMPABLE_NAME = "VolumePanelViewModel" const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar" const val COMPONENT_1: VolumePanelComponentKey = "test_component:1" const val COMPONENT_2: VolumePanelComponentKey = "test_component:2" Loading
packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt +9 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.data.repository import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.volume.panel.shared.VolumePanelLogger import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState import java.io.PrintWriter import javax.inject.Inject Loading @@ -27,10 +28,15 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update private const val TAG = "VolumePanelGlobalState" private const val TAG = "VolumePanelGlobalStateRepository" @SysUISingleton class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable { class VolumePanelGlobalStateRepository @Inject constructor( dumpManager: DumpManager, private val logger: VolumePanelLogger, ) : Dumpable { private val mutableGlobalState = MutableStateFlow( Loading @@ -48,6 +54,7 @@ class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpMana update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState ) { mutableGlobalState.update(update) logger.onVolumePanelGlobalStateChanged(mutableGlobalState.value) } override fun dump(pw: PrintWriter, args: Array<out String>) { Loading
packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt +17 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.domain.interactor import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.domain.model.ComponentModel import com.android.systemui.volume.panel.shared.VolumePanelLogger import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import javax.inject.Inject import javax.inject.Provider Loading @@ -26,8 +27,12 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn interface ComponentsInteractor { Loading @@ -45,6 +50,7 @@ constructor( enabledComponents: Collection<VolumePanelComponentKey>, defaultCriteria: Provider<ComponentAvailabilityCriteria>, @VolumePanelScope coroutineScope: CoroutineScope, private val logger: VolumePanelLogger, private val criteriaByKey: Map< VolumePanelComponentKey, Loading @@ -57,12 +63,18 @@ constructor( combine( enabledComponents.map { componentKey -> val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get() componentCriteria.isAvailable().map { isAvailable -> componentCriteria .isAvailable() .distinctUntilChanged() .conflate() .onEach { logger.onComponentAvailabilityChanged(componentKey, it) } .map { isAvailable -> ComponentModel(componentKey, isAvailable = isAvailable) } } ) { it.asList() } .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1) .stateIn(coroutineScope, SharingStarted.Eagerly, null) .filterNotNull() }