diff options
| author | 2024-05-01 15:54:10 +0100 | |
|---|---|---|
| committer | 2024-05-01 16:48:57 +0100 | |
| commit | 3ab98d39ea19ea18f64b3d457e11aeeaa87aaf4f (patch) | |
| tree | 24d72a1ab19bec82c4dc2ec80fc676f1f62c6757 | |
| parent | 17ba0c73f7e6eb8aed92f2711a75ef45c4785a03 (diff) | |
Show media output header when calling
Flag: aconfig new_volume_panel NEXTFOOD
Test: atest AudioOutputInteractorTest
Test: atest MediaOutputViewModelTest
Test: atest MediaDeviceSessionInteractorTest
Test: manual on the phone. Start call and change the device
Fixes: 330107223
Change-Id: I227f5e66d830a0aaf8aecdf03ace688c865e5318
12 files changed, 80 insertions, 157 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt index c73656eb1ec5..f1cc71bc59af 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt @@ -16,9 +16,9 @@ package com.android.systemui.volume.panel.component.mediaoutput -import com.android.systemui.volume.panel.component.mediaoutput.domain.MediaOutputAvailabilityCriteria import com.android.systemui.volume.panel.component.mediaoutput.ui.composable.MediaOutputComponent import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents +import com.android.systemui.volume.panel.domain.AlwaysAvailableCriteria import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent import dagger.Binds @@ -39,6 +39,6 @@ interface MediaOutputModule { @IntoMap @StringKey(VolumePanelComponents.MEDIA_OUTPUT) fun bindComponentAvailabilityCriteria( - criteria: MediaOutputAvailabilityCriteria + criteria: AlwaysAvailableCriteria ): ComponentAvailabilityCriteria } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt deleted file mode 100644 index 96b4dae7bb87..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.volume.panel.component.mediaoutput.domain - -import android.media.AudioManager -import android.testing.TestableLooper.RunWithLooper -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.kosmos.testScope -import com.android.systemui.testKosmos -import com.android.systemui.volume.data.repository.audioRepository -import com.android.systemui.volume.domain.interactor.audioModeInteractor -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(AndroidJUnit4::class) -@RunWithLooper(setAsMainLooper = true) -class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() { - - private val kosmos = testKosmos() - - private lateinit var underTest: MediaOutputAvailabilityCriteria - - @Before - fun setup() { - underTest = - MediaOutputAvailabilityCriteria( - kosmos.audioModeInteractor, - ) - } - - @Test - fun notInCall_isAvailable_true() { - with(kosmos) { - testScope.runTest { - audioRepository.setMode(AudioManager.MODE_NORMAL) - - val isAvailable by collectLastValue(underTest.isAvailable()) - runCurrent() - - assertThat(isAvailable).isTrue() - } - } - } - - @Test - fun inCall_isAvailable_false() { - with(kosmos) { - testScope.runTest { - audioRepository.setMode(AudioManager.MODE_IN_CALL) - - val isAvailable by collectLastValue(underTest.isAvailable()) - runCurrent() - - assertThat(isAvailable).isFalse() - } - } - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt index b5c580978737..64c9429cbe25 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.testKosmos import com.android.systemui.volume.localMediaController import com.android.systemui.volume.mediaControllerRepository import com.android.systemui.volume.mediaOutputInteractor +import com.android.systemui.volume.panel.shared.model.filterData import com.android.systemui.volume.remoteMediaController import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -67,7 +68,8 @@ class MediaDeviceSessionInteractorTest : SysuiTestCase() { fun playbackInfo_returnsPlaybackInfo() { with(kosmos) { testScope.runTest { - val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession) + val session by + collectLastValue(mediaOutputInteractor.defaultActiveMediaSession.filterData()) runCurrent() val info by collectLastValue(underTest.playbackInfo(session!!)) runCurrent() @@ -81,7 +83,8 @@ class MediaDeviceSessionInteractorTest : SysuiTestCase() { fun playbackState_returnsPlaybackState() { with(kosmos) { testScope.runTest { - val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession) + val session by + collectLastValue(mediaOutputInteractor.defaultActiveMediaSession.filterData()) runCurrent() val state by collectLastValue(underTest.playbackState(session!!)) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt index 30524d93dc02..49f82d4adf60 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt @@ -30,6 +30,8 @@ import com.android.systemui.res.R import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.volume.domain.interactor.audioModeInteractor +import com.android.systemui.volume.domain.interactor.audioOutputInteractor import com.android.systemui.volume.localMediaController import com.android.systemui.volume.localMediaRepository import com.android.systemui.volume.mediaControllerRepository @@ -64,6 +66,8 @@ class MediaOutputViewModelTest : SysuiTestCase() { testScope.backgroundScope, mediaOutputActionsInteractor, mediaDeviceSessionInteractor, + audioOutputInteractor, + audioModeInteractor, mediaOutputInteractor, uiEventLogger, ) diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b8e78a44a8ed..9c6e5f8621b5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1630,12 +1630,15 @@ <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to unmute media [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_unmute">unmute %s</string> - <!-- Title with application label for media output settings. [CHAR LIMIT=20] --> + <!-- Title with application label for media output settings when there is media playing. [CHAR LIMIT=20] --> <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string> <!-- Title for media output settings without media is playing. [CHAR LIMIT=20] --> <string name="media_output_title_without_playing">Audio will play on</string> + <!-- Title for media output settings when there is an ongoing call in progress. [CHAR LIMIT=20] --> + <string name="media_output_title_ongoing_call">Calling on</string> + <!-- Name of special SystemUI debug settings --> <string name="system_ui_tuner">System UI Tuner</string> diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt index ed446996a5f4..19d9c3f125b7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt @@ -32,6 +32,7 @@ import com.android.systemui.volume.domain.model.AudioOutputDevice import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.shared.model.filterData import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -69,6 +70,7 @@ constructor( } } else { mediaOutputInteractor.defaultActiveMediaSession + .filterData() .flatMapLatest { localMediaRepositoryFactory .create(it?.packageName) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt deleted file mode 100644 index bac7d15235d0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.volume.panel.component.mediaoutput.domain - -import com.android.settingslib.volume.domain.interactor.AudioModeInteractor -import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope -import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -/** Determines if the Media Output Volume Panel component is available. */ -@VolumePanelScope -class MediaOutputAvailabilityCriteria -@Inject -constructor( - private val audioModeInteractor: AudioModeInteractor, -) : ComponentAvailabilityCriteria { - - override fun isAvailable(): Flow<Boolean> = audioModeInteractor.isOngoingCall.map { !it } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt index 83b80294cfb6..b974f90191e9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt @@ -28,6 +28,9 @@ import com.android.systemui.volume.panel.component.mediaoutput.data.repository.L import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.shared.model.Result +import com.android.systemui.volume.panel.shared.model.filterData +import com.android.systemui.volume.panel.shared.model.wrapInResult import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -72,7 +75,7 @@ constructor( } /** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */ - val defaultActiveMediaSession: StateFlow<MediaDeviceSession?> = + val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> = activeMediaControllers .map { when { @@ -82,11 +85,13 @@ constructor( else -> null } } + .wrapInResult() .flowOn(backgroundCoroutineContext) - .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading()) private val localMediaRepository: SharedFlow<LocalMediaRepository> = defaultActiveMediaSession + .filterData() .map { it?.packageName } .distinctUntilChanged() .map { localMediaRepositoryFactory.create(it) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt index d60d981a3be1..192e0ec76132 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt @@ -18,16 +18,21 @@ package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel import android.content.Context import com.android.internal.logging.UiEventLogger +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Color import com.android.systemui.common.shared.model.Icon import com.android.systemui.res.R +import com.android.systemui.volume.domain.interactor.AudioOutputInteractor +import com.android.systemui.volume.domain.model.AudioOutputDevice import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.shared.model.Result +import com.android.systemui.volume.panel.shared.model.filterData +import com.android.systemui.volume.panel.shared.model.wrapInResult import com.android.systemui.volume.panel.ui.VolumePanelUiEvent import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -35,6 +40,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.mapNotNull @@ -50,25 +56,25 @@ constructor( @VolumePanelScope private val coroutineScope: CoroutineScope, private val actionsInteractor: MediaOutputActionsInteractor, private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor, + audioOutputInteractor: AudioOutputInteractor, + audioModeInteractor: AudioModeInteractor, interactor: MediaOutputInteractor, private val uiEventLogger: UiEventLogger, ) { private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> = interactor.defaultActiveMediaSession + .filterData() .flatMapLatest { session -> if (session == null) { - flowOf(Result.Data<SessionWithPlaybackState?>(null)) + flowOf(null) } else { mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback -> - playback?.let { - Result.Data<SessionWithPlaybackState?>( - SessionWithPlaybackState(session, playback.isActive()) - ) - } + playback?.let { SessionWithPlaybackState(session, playback.isActive) } } } } + .wrapInResult() .stateIn( coroutineScope, SharingStarted.Eagerly, @@ -76,23 +82,24 @@ constructor( ) val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> = - combine(sessionWithPlaybackState, interactor.currentConnectedDevice) { - mediaDeviceSession, - currentConnectedDevice -> - if (mediaDeviceSession !is Result.Data) { - return@combine null - } - ConnectedDeviceViewModel( - if (mediaDeviceSession.data?.isPlaybackActive == true) { - context.getString( - R.string.media_output_label_title, - mediaDeviceSession.data.session.appLabel - ) - } else { - context.getString(R.string.media_output_title_without_playing) - }, - currentConnectedDevice?.name, - ) + combine( + sessionWithPlaybackState.filterData(), + audioModeInteractor.isOngoingCall, + audioOutputInteractor.currentAudioDevice.filter { + it !is AudioOutputDevice.Unknown + }, + ) { mediaDeviceSession, isOngoingCall, currentConnectedDevice -> + val label = + when { + isOngoingCall -> context.getString(R.string.media_output_title_ongoing_call) + mediaDeviceSession?.isPlaybackActive == true -> + context.getString( + R.string.media_output_label_title, + mediaDeviceSession.session.appLabel + ) + else -> context.getString(R.string.media_output_title_without_playing) + } + ConnectedDeviceViewModel(label, currentConnectedDevice.name) } .stateIn( coroutineScope, @@ -101,16 +108,16 @@ constructor( ) val deviceIconViewModel: StateFlow<DeviceIconViewModel?> = - combine(sessionWithPlaybackState, interactor.currentConnectedDevice) { + combine(sessionWithPlaybackState.filterData(), audioOutputInteractor.currentAudioDevice) { mediaDeviceSession, currentConnectedDevice -> - if (mediaDeviceSession !is Result.Data) { - return@combine null - } val icon: Icon = - currentConnectedDevice?.icon?.let { Icon.Loaded(it, null) } + currentConnectedDevice + .takeIf { currentConnectedDevice !is AudioOutputDevice.Unknown } + ?.icon + ?.let { Icon.Loaded(it, null) } ?: Icon.Resource(R.drawable.ic_media_home_devices, null) - if (mediaDeviceSession.data?.isPlaybackActive == true) { + if (mediaDeviceSession?.isPlaybackActive == true) { DeviceIconViewModel.IsPlaying( icon = icon, iconColor = diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt index ac8092cd6f3a..fa400593c990 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.volume.panel.component.mediaoutput.shared.model.Medi import com.android.systemui.volume.panel.component.mediaoutput.shared.model.isTheSameSession import com.android.systemui.volume.panel.component.volume.domain.model.SliderType import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.shared.model.filterData import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope @@ -46,7 +47,7 @@ constructor( val volumePanelSliders: StateFlow<List<SliderType>> = combineTransform( mediaOutputInteractor.activeMediaDeviceSessions, - mediaOutputInteractor.defaultActiveMediaSession, + mediaOutputInteractor.defaultActiveMediaSession.filterData(), audioRepository.communicationDevice, ) { activeSessions, defaultSession, communicationDevice -> coroutineScope { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt index 741f5cf59853..26d6a9a0153d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt @@ -26,6 +26,7 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Au import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.CastVolumeSliderViewModel import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.shared.model.filterData import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -80,9 +81,13 @@ constructor( val isExpanded: StateFlow<Boolean> = merge( mutableIsExpanded, - mediaOutputInteractor.defaultActiveMediaSession.flatMapLatest { - if (it == null) flowOf(true) - else mediaDeviceSessionInteractor.playbackState(it).map { it?.isActive != true } + mediaOutputInteractor.defaultActiveMediaSession.filterData().flatMapLatest { session + -> + if (session == null) flowOf(true) + else + mediaDeviceSessionInteractor.playbackState(session).map { + it?.isActive != true + } }, ) .stateIn(scope, SharingStarted.Eagerly, false) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt index 8793538aac0d..5daed9908556 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt @@ -16,6 +16,10 @@ package com.android.systemui.volume.panel.shared.model +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull + /** Models a loadable result */ sealed interface Result<T> { @@ -25,3 +29,9 @@ sealed interface Result<T> { /** The data is loaded successfully */ data class Data<T>(val data: T) : Result<T> } + +/** Wraps flow into [Result]. */ +fun <T> Flow<T>.wrapInResult(): Flow<Result<T>> = map { Result.Data(it) } + +/** Filters only [Result.Data] from the flow. */ +fun <T> Flow<Result<T>>.filterData(): Flow<T> = mapNotNull { it as? Result.Data<T> }.map { it.data } |