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

Commit e86cef6e authored by Anton Potapov's avatar Anton Potapov
Browse files

Add AudioRepository to provide audio control functionality for the new

VolumePanel.

Bug: 318349141
Flag: aconfig new_volume_panel DISABLED
Test: atest AudioModeInteractorTest
Change-Id: I31f25fc4c55512f0820b244d77c5bfb4423594b5
parent bf7e3de0
Loading
Loading
Loading
Loading
+53 −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.settingslib.volume.data.repository

import android.media.AudioManager
import com.android.internal.util.ConcurrentUtils
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

/** Provides audio managing functionality and data. */
interface AudioRepository {

    /** Current [AudioManager.getMode]. */
    val mode: StateFlow<Int>
}

class AudioRepositoryImpl(
    private val audioManager: AudioManager,
    backgroundCoroutineContext: CoroutineContext,
    coroutineScope: CoroutineScope,
) : AudioRepository {

    override val mode: StateFlow<Int> =
        callbackFlow {
                val listener =
                    AudioManager.OnModeChangedListener { newMode -> launch { send(newMode) } }
                audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener)
                awaitClose { audioManager.removeOnModeChangedListener(listener) }
            }
            .flowOn(backgroundCoroutineContext)
            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
}
+35 −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.settingslib.volume.domain.interactor

import android.media.AudioManager
import com.android.settingslib.volume.data.repository.AudioRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class AudioModeInteractor(repository: AudioRepository) {

    private val ongoingCallModes =
        setOf(
            AudioManager.MODE_RINGTONE,
            AudioManager.MODE_IN_CALL,
            AudioManager.MODE_IN_COMMUNICATION,
        )

    /** Returns if current [AudioManager.getMode] call is an ongoing call */
    val isOngoingCall: Flow<Boolean> = repository.mode.map { it in ongoingCallModes }
}
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ android_test {
        "SettingsLibSettingsSpinner",
        "SettingsLibUsageProgressBarPreference",
        "settingslib_media_flags_lib",
        "kotlinx_coroutines_test",
    ],

    dxflags: ["--multi-dex"],
+32 −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.settingslib.volume.data.repository

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class FakeAudioRepository : AudioRepository {

    private val mutableMode = MutableStateFlow(0)
    override val mode: StateFlow<Int>
        get() = mutableMode.asStateFlow()

    fun setMode(newMode: Int) {
        mutableMode.value = newMode
    }
}
+79 −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.settingslib.volume.domain.interactor

import android.media.AudioManager
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.settingslib.volume.data.repository.FakeAudioRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class AudioModeInteractorTest {

    private val testScope = TestScope()
    private val fakeAudioRepository = FakeAudioRepository()

    private val underTest = AudioModeInteractor(fakeAudioRepository)

    @Test
    fun ongoingCallModes_isOnGoingCall() {
        testScope.runTest {
            for (mode in ongoingCallModes) {
                var isOngoingCall = false
                underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope)

                fakeAudioRepository.setMode(mode)
                runCurrent()

                assertThat(isOngoingCall).isTrue()
            }
        }
    }

    @Test
    fun notOngoingCallModes_isNotOnGoingCall() {
        testScope.runTest {
            var isOngoingCall = true
            underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope)

            fakeAudioRepository.setMode(AudioManager.MODE_CURRENT)
            runCurrent()

            assertThat(isOngoingCall).isFalse()
        }
    }

    private companion object {
        private val ongoingCallModes =
            setOf(
                AudioManager.MODE_RINGTONE,
                AudioManager.MODE_IN_CALL,
                AudioManager.MODE_IN_COMMUNICATION,
            )
    }
}
Loading