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

Commit 4752ce9b authored by Anton Potapov's avatar Anton Potapov
Browse files

Move media output state logic to the interactor.

Apparently, there is a defference in active sessions returned by the
MediaSessionManager when starting a call and getting one. This caused
the panel to correctly determine the device when starting a call, but
not when getting one.

Now Media Output Component state is completely detached from the media
session when there is an ongoing call.

Flag: com.android.systemui.new_volume_panel
Fixes: 342976548
Test: manual on the phone
Test: atest MediaOutputComponentInteractorTest
Test: atest MediaOutputViewModelTest
Change-Id: Ibeda4a7ac10053314e6e436fc5a458429d9293d7
parent db2b5f08
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

private const val builtInDeviceName = "This phone"

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -71,6 +73,10 @@ class AudioOutputInteractorTest : SysuiTestCase() {
                addOverride(R.drawable.ic_media_speaker_device, testIcon)

                addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)

                addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
                addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
                addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
            }
        }
    }
@@ -90,7 +96,7 @@ class AudioOutputInteractorTest : SysuiTestCase() {

                assertThat(device).isInstanceOf(AudioOutputDevice.BuiltIn::class.java)
                assertThat(device!!.icon).isEqualTo(testIcon)
                assertThat(device!!.name).isEqualTo("built_in")
                assertThat(device!!.name).isEqualTo(builtInDeviceName)
            }
        }
    }
+141 −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.component.mediaoutput.domain.interactor

import android.graphics.drawable.TestStubDrawable
import android.media.AudioManager
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.R
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.TestAudioDevicesFactory
import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.data.repository.audioSharingRepository
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaControllerRepository
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.shared.model.filterData
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

private const val builtInDeviceName = "This phone"

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MediaOutputComponentInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var underTest: MediaOutputComponentInteractor

    @Before
    fun setUp() =
        with(kosmos) {
            audioRepository.setMode(AudioManager.MODE_NORMAL)
            localMediaRepository.updateCurrentConnectedDevice(
                TestMediaDevicesFactory.builtInMediaDevice(deviceIcon = testIcon)
            )

            with(context.orCreateTestableResources) {
                addOverride(R.drawable.ic_smartphone, testIcon)

                addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
                addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
                addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
            }

            underTest = mediaOutputComponentInteractor
        }

    @Test
    fun inCall_stateIs_Calling() =
        with(kosmos) {
            testScope.runTest {
                with(audioRepository) {
                    setMode(AudioManager.MODE_IN_CALL)
                    setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
                }

                val model by collectLastValue(underTest.mediaOutputModel.filterData())
                runCurrent()

                assertThat(model)
                    .isEqualTo(
                        MediaOutputComponentModel.Calling(
                            AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
                            false,
                        )
                    )
            }
        }

    @Test
    fun hasSession_stateIs_MediaSession() =
        with(kosmos) {
            testScope.runTest {
                mediaControllerRepository.setActiveSessions(listOf(localMediaController))

                val model by collectLastValue(underTest.mediaOutputModel.filterData())
                runCurrent()

                with(model as MediaOutputComponentModel.MediaSession) {
                    assertThat(session.appLabel).isEqualTo("local_media_controller_label")
                    assertThat(session.packageName).isEqualTo("local.test.pkg")
                    assertThat(session.canAdjustVolume).isTrue()
                    assertThat(device)
                        .isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
                    assertThat(isInAudioSharing).isFalse()
                }
            }
        }

    @Test
    fun noMediaOrCall_stateIs_Idle() =
        with(kosmos) {
            testScope.runTest {
                audioSharingRepository.setInAudioSharing(true)

                val model by collectLastValue(underTest.mediaOutputModel.filterData())
                runCurrent()

                assertThat(model)
                    .isEqualTo(
                        MediaOutputComponentModel.Idle(
                            AudioOutputDevice.BuiltIn("built_in_media", testIcon),
                            true,
                        )
                    )
            }
        }

    private companion object {
        val testIcon = TestStubDrawable()
    }
}
+2 −8
Original line number Diff line number Diff line
@@ -31,14 +31,11 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.data.repository.audioSharingRepository
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
import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputActionsInteractor
import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -66,10 +63,7 @@ class MediaOutputViewModelTest : SysuiTestCase() {
                    applicationContext,
                    testScope.backgroundScope,
                    mediaOutputActionsInteractor,
                    mediaDeviceSessionInteractor,
                    audioOutputInteractor,
                    audioModeInteractor,
                    mediaOutputInteractor,
                    mediaOutputComponentInteractor,
                    uiEventLogger,
                )

+7 −2
Original line number Diff line number Diff line
@@ -17,15 +17,18 @@
package com.android.systemui.volume.domain.interactor

import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.media.AudioDeviceInfo
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.MediaDevice.MediaDeviceType
import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioSharingRepository
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
@@ -36,6 +39,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -47,6 +51,7 @@ import kotlinx.coroutines.flow.stateIn
class AudioOutputInteractor
@Inject
constructor(
    @Application private val context: Context,
    audioRepository: AudioRepository,
    audioModeInteractor: AudioModeInteractor,
    @VolumePanelScope scope: CoroutineScope,
@@ -58,7 +63,7 @@ constructor(
    audioSharingRepository: AudioSharingRepository,
) {

    val currentAudioDevice: Flow<AudioOutputDevice> =
    val currentAudioDevice: StateFlow<AudioOutputDevice> =
        audioModeInteractor.isOngoingCall
            .flatMapLatest { isOngoingCall ->
                if (isOngoingCall) {
@@ -102,7 +107,7 @@ constructor(
            )
        }
        return AudioOutputDevice.BuiltIn(
            name = productName.toString(),
            name = PhoneMediaDevice.getMediaTransferThisDeviceName(context),
            icon = deviceIconInteractor.loadIcon(type),
        )
    }
+5 −7
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject

@@ -29,14 +29,12 @@ import javax.inject.Inject
@VolumePanelScope
class MediaOutputActionsInteractor
@Inject
constructor(
    private val mediaOutputDialogManager: MediaOutputDialogManager,
) {
constructor(private val mediaOutputDialogManager: MediaOutputDialogManager) {

    fun onBarClick(sessionWithPlaybackState: SessionWithPlaybackState?, expandable: Expandable?) {
        if (sessionWithPlaybackState?.isPlaybackActive == true) {
    fun onBarClick(model: MediaOutputComponentModel?, expandable: Expandable?) {
        if (model is MediaOutputComponentModel.MediaSession) {
            mediaOutputDialogManager.createAndShowWithController(
                sessionWithPlaybackState.session.packageName,
                model.session.packageName,
                false,
                expandable?.dialogController()
            )
Loading