diff options
7 files changed, 250 insertions, 0 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt new file mode 100644 index 000000000000..3355fb395ca0 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt @@ -0,0 +1,53 @@ +/* + * 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) +} diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt new file mode 100644 index 000000000000..053c59b15067 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt @@ -0,0 +1,35 @@ +/* + * 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 } +} diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index 644b72c383ac..ce3a7baf6be6 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -57,6 +57,7 @@ android_test { "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", "settingslib_media_flags_lib", + "kotlinx_coroutines_test", ], dxflags: ["--multi-dex"], diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt new file mode 100644 index 000000000000..686362fadf02 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt @@ -0,0 +1,32 @@ +/* + * 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 + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt new file mode 100644 index 000000000000..3bc1edc9c944 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt @@ -0,0 +1,79 @@ +/* + * 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, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt new file mode 100644 index 000000000000..8d5e55a2917e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt @@ -0,0 +1,47 @@ +/* + * 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.dagger + +import android.media.AudioManager +import com.android.settingslib.volume.data.repository.AudioRepository +import com.android.settingslib.volume.data.repository.AudioRepositoryImpl +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import dagger.Module +import dagger.Provides +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope + +/** Dagger module for audio code in the volume package */ +@Module +interface AudioModule { + + companion object { + + @Provides + fun provideAudioRepository( + audioManager: AudioManager, + @Background coroutineContext: CoroutineContext, + @Application coroutineScope: CoroutineScope, + ): AudioRepository = AudioRepositoryImpl(audioManager, coroutineContext, coroutineScope) + + @Provides + fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor = + AudioModeInteractor(repository) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index b1bfbe0016e1..c842e5f295b9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -60,6 +60,9 @@ import kotlinx.coroutines.CoroutineScope; /** Dagger Module for code in the volume package. */ @Module( + includes = { + AudioModule.class, + }, subcomponents = { VolumePanelComponent.class } |