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

Commit 0a6fe047 authored by Anton Potapov's avatar Anton Potapov
Browse files

Add loading state in the MediaOutputViewModel

We need to distinguish between loading and null session in the
MediaOutputViewModel to wait for the initial load to finish.

Flag: aconfig new_volume_panel TRUNKFOOD
Test: manual on the phone
Fixes: 332721662
Change-Id: Ib9eb6cdade98fdd75ccacbac7b49caa782ba8bd6
parent 020580d7
Loading
Loading
Loading
Loading
+24 −10
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlayback
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -34,6 +35,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -53,32 +55,40 @@ constructor(
    private val uiEventLogger: UiEventLogger,
) {

    private val sessionWithPlayback: StateFlow<SessionWithPlayback?> =
    private val sessionWithPlayback: StateFlow<Result<SessionWithPlayback?>> =
        interactor.defaultActiveMediaSession
            .flatMapLatest { session ->
                if (session == null) {
                    flowOf(null)
                    flowOf(Result.Data<SessionWithPlayback?>(null))
                } else {
                    mediaDeviceSessionInteractor.playbackState(session).map { playback ->
                        playback?.let { SessionWithPlayback(session, it) }
                    mediaDeviceSessionInteractor
                        .playbackState(session)
                        .map { playback ->
                            playback?.let {
                                Result.Data<SessionWithPlayback?>(SessionWithPlayback(session, it))
                            }
                        }
                        .filterNotNull()
                }
            }
            .stateIn(
                coroutineScope,
                SharingStarted.Eagerly,
                null,
                Result.Loading(),
            )

    val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
        combine(sessionWithPlayback, interactor.currentConnectedDevice) {
                mediaDeviceSession,
                currentConnectedDevice ->
                if (mediaDeviceSession !is Result.Data) {
                    return@combine null
                }
                ConnectedDeviceViewModel(
                    if (mediaDeviceSession?.playback?.isActive == true) {
                    if (mediaDeviceSession.data?.playback?.isActive == true) {
                        context.getString(
                            R.string.media_output_label_title,
                            mediaDeviceSession.session.appLabel
                            mediaDeviceSession.data.session.appLabel
                        )
                    } else {
                        context.getString(R.string.media_output_title_without_playing)
@@ -96,7 +106,10 @@ constructor(
        combine(sessionWithPlayback, interactor.currentConnectedDevice) {
                mediaDeviceSession,
                currentConnectedDevice ->
                if (mediaDeviceSession?.playback?.isActive == true) {
                if (mediaDeviceSession !is Result.Data) {
                    return@combine null
                }
                if (mediaDeviceSession.data?.playback?.isActive == true) {
                    val icon =
                        currentConnectedDevice?.icon?.let { Icon.Loaded(it, null) }
                            ?: Icon.Resource(
@@ -130,6 +143,7 @@ constructor(

    fun onBarClick(expandable: Expandable) {
        uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
        actionsInteractor.onBarClick(sessionWithPlayback.value, expandable)
        val result = sessionWithPlayback.value
        actionsInteractor.onBarClick((result as? Result.Data)?.data, expandable)
    }
}
+27 −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.volume.panel.shared.model

/** Models a loadable result */
sealed interface Result<T> {

    /** The data is still loading */
    class Loading<T> : Result<T>

    /** The data is loaded successfully */
    data class Data<T>(val data: T) : Result<T>
}