Loading packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt 0 → 100644 +110 −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.view.accessibility.data.repository import android.view.accessibility.CaptioningManager import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext interface CaptioningRepository { /** The system audio caption enabled state. */ val isSystemAudioCaptioningEnabled: StateFlow<Boolean> /** The system audio caption UI enabled state. */ val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> /** Sets [isSystemAudioCaptioningEnabled]. */ suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) } class CaptioningRepositoryImpl( private val captioningManager: CaptioningManager, private val backgroundCoroutineContext: CoroutineContext, coroutineScope: CoroutineScope, ) : CaptioningRepository { private val captioningChanges: SharedFlow<CaptioningChange> = callbackFlow { val listener = CaptioningChangeProducingListener(this) captioningManager.addCaptioningChangeListener(listener) awaitClose { captioningManager.removeCaptioningChangeListener(listener) } } .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0) override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> = captioningChanges .filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class) .map { it.isEnabled } .stateIn( coroutineScope, SharingStarted.WhileSubscribed(), captioningManager.isSystemAudioCaptioningEnabled ) override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> = captioningChanges .filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class) .map { it.isEnabled } .stateIn( coroutineScope, SharingStarted.WhileSubscribed(), captioningManager.isSystemAudioCaptioningUiEnabled, ) override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) { withContext(backgroundCoroutineContext) { captioningManager.isSystemAudioCaptioningEnabled = isEnabled } } private sealed interface CaptioningChange { data class IsSystemAudioCaptioningEnabled(val isEnabled: Boolean) : CaptioningChange data class IsSystemUICaptioningEnabled(val isEnabled: Boolean) : CaptioningChange } private class CaptioningChangeProducingListener( private val scope: ProducerScope<CaptioningChange> ) : CaptioningManager.CaptioningChangeListener() { override fun onSystemAudioCaptioningChanged(enabled: Boolean) { emitChange(CaptioningChange.IsSystemAudioCaptioningEnabled(enabled)) } override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) { emitChange(CaptioningChange.IsSystemUICaptioningEnabled(enabled)) } private fun emitChange(change: CaptioningChange) { scope.launch { scope.send(change) } } } } packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt 0 → 100644 +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.view.accessibility.domain.interactor import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository import kotlinx.coroutines.flow.StateFlow class CaptioningInteractor(private val repository: CaptioningRepository) { val isSystemAudioCaptioningEnabled: StateFlow<Boolean> get() = repository.isSystemAudioCaptioningEnabled val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> get() = repository.isSystemAudioCaptioningUiEnabled suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) = repository.setIsSystemAudioCaptioningEnabled(enabled) } packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt 0 → 100644 +113 −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.view.accessibility.data.repository import android.view.accessibility.CaptioningManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest 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.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @Suppress("UnspecifiedRegisterReceiverFlag") @RunWith(AndroidJUnit4::class) class CaptioningRepositoryTest { @Captor private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener> @Mock private lateinit var captioningManager: CaptioningManager private lateinit var underTest: CaptioningRepository private val testScope = TestScope() @Before fun setup() { MockitoAnnotations.initMocks(this) underTest = CaptioningRepositoryImpl( captioningManager, testScope.testScheduler, testScope.backgroundScope ) } @Test fun isSystemAudioCaptioningEnabled_change_repositoryEmits() { testScope.runTest { `when`(captioningManager.isEnabled).thenReturn(false) val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>() underTest.isSystemAudioCaptioningEnabled .onEach { isSystemAudioCaptioningEnabled.add(it) } .launchIn(backgroundScope) runCurrent() triggerOnSystemAudioCaptioningChange() runCurrent() assertThat(isSystemAudioCaptioningEnabled) .containsExactlyElementsIn(listOf(false, true)) .inOrder() } } @Test fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() { testScope.runTest { `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false) val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>() underTest.isSystemAudioCaptioningUiEnabled .onEach { isSystemAudioCaptioningUiEnabled.add(it) } .launchIn(backgroundScope) runCurrent() triggerSystemAudioCaptioningUiChange() runCurrent() assertThat(isSystemAudioCaptioningUiEnabled) .containsExactlyElementsIn(listOf(false, true)) .inOrder() } } private fun triggerSystemAudioCaptioningUiChange(enabled: Boolean = true) { verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture()) listenerCaptor.value.onSystemAudioCaptioningUiChanged(enabled) } private fun triggerOnSystemAudioCaptioningChange(enabled: Boolean = true) { verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture()) listenerCaptor.value.onSystemAudioCaptioningChanged(enabled) } } packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/FakeCaptioningRepository.kt 0 → 100644 +40 −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.view.accessibility.data.repository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeCaptioningRepository : CaptioningRepository { private val mutableIsSystemAudioCaptioningEnabled = MutableStateFlow(false) override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> get() = mutableIsSystemAudioCaptioningEnabled.asStateFlow() private val mutableIsSystemAudioCaptioningUiEnabled = MutableStateFlow(false) override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> get() = mutableIsSystemAudioCaptioningUiEnabled.asStateFlow() override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) { mutableIsSystemAudioCaptioningEnabled.value = isEnabled } fun setIsSystemAudioCaptioningUiEnabled(isSystemAudioCaptioningUiEnabled: Boolean) { mutableIsSystemAudioCaptioningUiEnabled.value = isSystemAudioCaptioningUiEnabled } } packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt 0 → 100644 +47 −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.dagger import android.view.accessibility.CaptioningManager import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository import com.android.settingslib.view.accessibility.data.repository.CaptioningRepositoryImpl import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor 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 @Module interface CaptioningModule { companion object { @Provides fun provideCaptioningRepository( captioningManager: CaptioningManager, @Background coroutineContext: CoroutineContext, @Application coroutineScope: CoroutineScope, ): CaptioningRepository = CaptioningRepositoryImpl(captioningManager, coroutineContext, coroutineScope) @Provides fun provideCaptioningInteractor(repository: CaptioningRepository): CaptioningInteractor = CaptioningInteractor(repository) } } Loading
packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt 0 → 100644 +110 −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.view.accessibility.data.repository import android.view.accessibility.CaptioningManager import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext interface CaptioningRepository { /** The system audio caption enabled state. */ val isSystemAudioCaptioningEnabled: StateFlow<Boolean> /** The system audio caption UI enabled state. */ val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> /** Sets [isSystemAudioCaptioningEnabled]. */ suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) } class CaptioningRepositoryImpl( private val captioningManager: CaptioningManager, private val backgroundCoroutineContext: CoroutineContext, coroutineScope: CoroutineScope, ) : CaptioningRepository { private val captioningChanges: SharedFlow<CaptioningChange> = callbackFlow { val listener = CaptioningChangeProducingListener(this) captioningManager.addCaptioningChangeListener(listener) awaitClose { captioningManager.removeCaptioningChangeListener(listener) } } .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0) override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> = captioningChanges .filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class) .map { it.isEnabled } .stateIn( coroutineScope, SharingStarted.WhileSubscribed(), captioningManager.isSystemAudioCaptioningEnabled ) override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> = captioningChanges .filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class) .map { it.isEnabled } .stateIn( coroutineScope, SharingStarted.WhileSubscribed(), captioningManager.isSystemAudioCaptioningUiEnabled, ) override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) { withContext(backgroundCoroutineContext) { captioningManager.isSystemAudioCaptioningEnabled = isEnabled } } private sealed interface CaptioningChange { data class IsSystemAudioCaptioningEnabled(val isEnabled: Boolean) : CaptioningChange data class IsSystemUICaptioningEnabled(val isEnabled: Boolean) : CaptioningChange } private class CaptioningChangeProducingListener( private val scope: ProducerScope<CaptioningChange> ) : CaptioningManager.CaptioningChangeListener() { override fun onSystemAudioCaptioningChanged(enabled: Boolean) { emitChange(CaptioningChange.IsSystemAudioCaptioningEnabled(enabled)) } override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) { emitChange(CaptioningChange.IsSystemUICaptioningEnabled(enabled)) } private fun emitChange(change: CaptioningChange) { scope.launch { scope.send(change) } } } }
packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt 0 → 100644 +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.view.accessibility.domain.interactor import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository import kotlinx.coroutines.flow.StateFlow class CaptioningInteractor(private val repository: CaptioningRepository) { val isSystemAudioCaptioningEnabled: StateFlow<Boolean> get() = repository.isSystemAudioCaptioningEnabled val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> get() = repository.isSystemAudioCaptioningUiEnabled suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) = repository.setIsSystemAudioCaptioningEnabled(enabled) }
packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt 0 → 100644 +113 −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.view.accessibility.data.repository import android.view.accessibility.CaptioningManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest 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.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @Suppress("UnspecifiedRegisterReceiverFlag") @RunWith(AndroidJUnit4::class) class CaptioningRepositoryTest { @Captor private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener> @Mock private lateinit var captioningManager: CaptioningManager private lateinit var underTest: CaptioningRepository private val testScope = TestScope() @Before fun setup() { MockitoAnnotations.initMocks(this) underTest = CaptioningRepositoryImpl( captioningManager, testScope.testScheduler, testScope.backgroundScope ) } @Test fun isSystemAudioCaptioningEnabled_change_repositoryEmits() { testScope.runTest { `when`(captioningManager.isEnabled).thenReturn(false) val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>() underTest.isSystemAudioCaptioningEnabled .onEach { isSystemAudioCaptioningEnabled.add(it) } .launchIn(backgroundScope) runCurrent() triggerOnSystemAudioCaptioningChange() runCurrent() assertThat(isSystemAudioCaptioningEnabled) .containsExactlyElementsIn(listOf(false, true)) .inOrder() } } @Test fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() { testScope.runTest { `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false) val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>() underTest.isSystemAudioCaptioningUiEnabled .onEach { isSystemAudioCaptioningUiEnabled.add(it) } .launchIn(backgroundScope) runCurrent() triggerSystemAudioCaptioningUiChange() runCurrent() assertThat(isSystemAudioCaptioningUiEnabled) .containsExactlyElementsIn(listOf(false, true)) .inOrder() } } private fun triggerSystemAudioCaptioningUiChange(enabled: Boolean = true) { verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture()) listenerCaptor.value.onSystemAudioCaptioningUiChanged(enabled) } private fun triggerOnSystemAudioCaptioningChange(enabled: Boolean = true) { verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture()) listenerCaptor.value.onSystemAudioCaptioningChanged(enabled) } }
packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/FakeCaptioningRepository.kt 0 → 100644 +40 −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.view.accessibility.data.repository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeCaptioningRepository : CaptioningRepository { private val mutableIsSystemAudioCaptioningEnabled = MutableStateFlow(false) override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> get() = mutableIsSystemAudioCaptioningEnabled.asStateFlow() private val mutableIsSystemAudioCaptioningUiEnabled = MutableStateFlow(false) override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> get() = mutableIsSystemAudioCaptioningUiEnabled.asStateFlow() override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) { mutableIsSystemAudioCaptioningEnabled.value = isEnabled } fun setIsSystemAudioCaptioningUiEnabled(isSystemAudioCaptioningUiEnabled: Boolean) { mutableIsSystemAudioCaptioningUiEnabled.value = isSystemAudioCaptioningUiEnabled } }
packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt 0 → 100644 +47 −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.dagger import android.view.accessibility.CaptioningManager import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository import com.android.settingslib.view.accessibility.data.repository.CaptioningRepositoryImpl import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor 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 @Module interface CaptioningModule { companion object { @Provides fun provideCaptioningRepository( captioningManager: CaptioningManager, @Background coroutineContext: CoroutineContext, @Application coroutineScope: CoroutineScope, ): CaptioningRepository = CaptioningRepositoryImpl(captioningManager, coroutineContext, coroutineScope) @Provides fun provideCaptioningInteractor(repository: CaptioningRepository): CaptioningInteractor = CaptioningInteractor(repository) } }