diff --git a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt b/src/com/android/settings/network/telephony/CallStateFlow.kt similarity index 51% rename from src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt rename to src/com/android/settings/network/telephony/CallStateFlow.kt index 416dda19a2c..f5164e072fa 100644 --- a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt +++ b/src/com/android/settings/network/telephony/CallStateFlow.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 The Android Open Source Project + * Copyright (C) 2023 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. @@ -17,19 +17,16 @@ package com.android.settings.network.telephony import android.content.Context -import com.android.settings.network.SatelliteRepository +import android.telephony.TelephonyCallback import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -class SubscriptionActivationRepository( - private val context: Context, - private val callStateRepository: CallStateRepository = CallStateRepository(context), - private val satelliteRepository: SatelliteRepository = SatelliteRepository(context), -) { - fun isActivationChangeableFlow(): Flow = combine( - callStateRepository.isInCallFlow(), - satelliteRepository.getIsSessionStartedFlow() - ) { isInCall, isSatelliteModemEnabled -> - !isInCall && !isSatelliteModemEnabled +/** + * Flow for call state. + */ +fun Context.callStateFlow(subId: Int): Flow = telephonyCallbackFlow(subId) { + object : TelephonyCallback(), TelephonyCallback.CallStateListener { + override fun onCallStateChanged(state: Int) { + trySend(state) + } } } diff --git a/src/com/android/settings/network/telephony/CallStateRepository.kt b/src/com/android/settings/network/telephony/CallStateRepository.kt deleted file mode 100644 index 1c93af32b19..00000000000 --- a/src/com/android/settings/network/telephony/CallStateRepository.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.settings.network.telephony - -import android.content.Context -import android.telephony.TelephonyCallback -import android.telephony.TelephonyManager -import android.util.Log -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.onEach - -@OptIn(ExperimentalCoroutinesApi::class) -class CallStateRepository(private val context: Context) { - private val subscriptionManager = context.requireSubscriptionManager() - - /** Flow for call state of given [subId]. */ - fun callStateFlow(subId: Int): Flow = context.telephonyCallbackFlow(subId) { - object : TelephonyCallback(), TelephonyCallback.CallStateListener { - override fun onCallStateChanged(state: Int) { - trySend(state) - } - } - } - - /** - * Flow for in call state. - * - * @return true if any active subscription's call state is not idle. - */ - fun isInCallFlow(): Flow = context.subscriptionsChangedFlow() - .flatMapLatest { - val subIds = subscriptionManager.activeSubscriptionIdList - combine(subIds.map(::callStateFlow)) { states -> - states.any { it != TelephonyManager.CALL_STATE_IDLE } - } - } - .conflate() - .flowOn(Dispatchers.Default) - .onEach { Log.d(TAG, "isInCallFlow: $it") } - - private companion object { - private const val TAG = "CallStateRepository" - } -} diff --git a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt index 07b4e60820f..312d4468bfd 100644 --- a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt +++ b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt @@ -23,8 +23,10 @@ import android.telephony.TelephonyManager import androidx.lifecycle.LifecycleOwner import androidx.preference.Preference import androidx.preference.PreferenceScreen +import com.android.settings.R import com.android.settings.core.BasePreferenceController import com.android.settings.network.SubscriptionUtil +import com.android.settings.network.telephony.MobileNetworkUtils import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle /** This controls a preference allowing the user to delete the profile for an eSIM. */ @@ -55,10 +57,9 @@ class DeleteSimProfilePreferenceController(context: Context, preferenceKey: Stri } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { - CallStateRepository(mContext).callStateFlow(subscriptionId) - .collectLatestWithLifecycle(viewLifecycleOwner) { - preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE) - } + mContext.callStateFlow(subscriptionId).collectLatestWithLifecycle(viewLifecycleOwner) { + preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE) + } } override fun handlePreferenceTreeClick(preference: Preference): Boolean { diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt index f89a0dba16a..00c55eccc3d 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt @@ -18,6 +18,7 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -25,17 +26,19 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R +import com.android.settings.network.SatelliteRepository import com.android.settings.network.SubscriptionUtil import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.MainSwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map class MobileNetworkSwitchController @JvmOverloads constructor( context: Context, preferenceKey: String, private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context), - private val subscriptionActivationRepository: SubscriptionActivationRepository = - SubscriptionActivationRepository(context), + private val satelliteRepository: SatelliteRepository = SatelliteRepository(context) ) : ComposePreferenceController(context, preferenceKey) { private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID @@ -54,7 +57,12 @@ class MobileNetworkSwitchController @JvmOverloads constructor( subscriptionRepository.isSubscriptionEnabledFlow(subId) }.collectAsStateWithLifecycle(initialValue = null) val changeable by remember { - subscriptionActivationRepository.isActivationChangeableFlow() + combine( + context.callStateFlow(subId).map { it == TelephonyManager.CALL_STATE_IDLE }, + satelliteRepository.getIsSessionStartedFlow() + ) { isCallStateIdle, isSatelliteModemEnabled -> + isCallStateIdle && !isSatelliteModemEnabled + } }.collectAsStateWithLifecycle(initialValue = true) MainSwitchPreference(model = object : SwitchPreferenceModel { override val title = stringResource(R.string.mobile_network_use_sim_on) diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt index 3bb267940d2..f184092821e 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt @@ -30,6 +30,7 @@ import com.android.settings.R import com.android.settings.network.telephony.wificalling.WifiCallingRepository import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext /** @@ -40,7 +41,7 @@ import kotlinx.coroutines.withContext open class WifiCallingPreferenceController @JvmOverloads constructor( context: Context, key: String, - private val callStateRepository: CallStateRepository = CallStateRepository(context), + private val callStateFlowFactory: (subId: Int) -> Flow = context::callStateFlow, private val wifiCallingRepositoryFactory: (subId: Int) -> WifiCallingRepository = { subId -> WifiCallingRepository(context, subId) }, @@ -90,7 +91,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( if (isReady) update() } - callStateRepository.callStateFlow(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) { + callStateFlowFactory(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) { preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE) } } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CallStateRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CallStateFlowTest.kt similarity index 66% rename from tests/spa_unit/src/com/android/settings/network/telephony/CallStateRepositoryTest.kt rename to tests/spa_unit/src/com/android/settings/network/telephony/CallStateFlowTest.kt index 8213ecf4842..d353d443715 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/CallStateRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/CallStateFlowTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 The Android Open Source Project + * Copyright (C) 2023 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. @@ -17,7 +17,6 @@ package com.android.settings.network.telephony import android.content.Context -import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import androidx.test.core.app.ApplicationProvider @@ -37,7 +36,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.spy @RunWith(AndroidJUnit4::class) -class CallStateRepositoryTest { +class CallStateFlowTest { private var callStateListener: TelephonyCallback.CallStateListener? = null private val mockTelephonyManager = mock { @@ -48,24 +47,13 @@ class CallStateRepositoryTest { } } - private val mockSubscriptionManager = mock { - on { activeSubscriptionIdList } doReturn intArrayOf(SUB_ID) - on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer { - val listener = it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener - listener.onSubscriptionsChanged() - } - } - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager - on { subscriptionManager } doReturn mockSubscriptionManager } - private val repository = CallStateRepository(context) - @Test fun callStateFlow_initial_sendInitialState() = runBlocking { - val flow = repository.callStateFlow(SUB_ID) + val flow = context.callStateFlow(SUB_ID) val state = flow.firstWithTimeoutOrNull() @@ -75,7 +63,7 @@ class CallStateRepositoryTest { @Test fun callStateFlow_changed_sendChangedState() = runBlocking { val listDeferred = async { - repository.callStateFlow(SUB_ID).toListWithTimeout() + context.callStateFlow(SUB_ID).toListWithTimeout() } delay(100) @@ -86,27 +74,6 @@ class CallStateRepositoryTest { .inOrder() } - @Test - fun isInCallFlow_initial() = runBlocking { - val isInCall = repository.isInCallFlow().firstWithTimeoutOrNull() - - assertThat(isInCall).isFalse() - } - - @Test - fun isInCallFlow_changed_sendChangedState() = runBlocking { - val listDeferred = async { - repository.isInCallFlow().toListWithTimeout() - } - delay(100) - - callStateListener?.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING) - - assertThat(listDeferred.await()) - .containsExactly(false, true) - .inOrder() - } - private companion object { const val SUB_ID = 1 } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt deleted file mode 100644 index dd9c505635b..00000000000 --- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.settings.network.telephony - -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settings.network.SatelliteRepository -import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.stub - -@RunWith(AndroidJUnit4::class) -class SubscriptionActivationRepositoryTest { - - private val context: Context = ApplicationProvider.getApplicationContext() - private val mockCallStateRepository = mock() - private val mockSatelliteRepository = mock() - - private val repository = - SubscriptionActivationRepository(context, mockCallStateRepository, mockSatelliteRepository) - - @Test - fun isActivationChangeableFlow_changeable() = runBlocking { - mockCallStateRepository.stub { - on { isInCallFlow() } doReturn flowOf(false) - } - mockSatelliteRepository.stub { - on { getIsSessionStartedFlow() } doReturn flowOf(false) - } - - val changeable = repository.isActivationChangeableFlow().firstWithTimeoutOrNull() - - assertThat(changeable).isTrue() - } - - @Test - fun isActivationChangeableFlow_inCall_notChangeable() = runBlocking { - mockCallStateRepository.stub { - on { isInCallFlow() } doReturn flowOf(true) - } - mockSatelliteRepository.stub { - on { getIsSessionStartedFlow() } doReturn flowOf(false) - } - - val changeable = repository.isActivationChangeableFlow().firstWithTimeoutOrNull() - - assertThat(changeable).isFalse() - } - - @Test - fun isActivationChangeableFlow_satelliteSessionStarted_notChangeable() = runBlocking { - mockCallStateRepository.stub { - on { isInCallFlow() } doReturn flowOf(false) - } - mockSatelliteRepository.stub { - on { getIsSessionStartedFlow() } doReturn flowOf(true) - } - - val changeable = repository.isActivationChangeableFlow().firstWithTimeoutOrNull() - - assertThat(changeable).isFalse() - } -} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt index 005ad67af64..92776df3dd2 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt @@ -58,9 +58,7 @@ class WifiCallingPreferenceControllerTest { } private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context) - private val mockCallStateRepository = mock { - on { callStateFlow(SUB_ID) } doReturn flowOf(TelephonyManager.CALL_STATE_IDLE) - } + private var callState = TelephonyManager.CALL_STATE_IDLE private val mockWifiCallingRepository = mock { on { getWiFiCallingMode() } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN @@ -73,7 +71,7 @@ class WifiCallingPreferenceControllerTest { private val controller = WifiCallingPreferenceController( context = context, key = TEST_KEY, - callStateRepository = mockCallStateRepository, + callStateFlowFactory = { flowOf(callState) }, wifiCallingRepositoryFactory = { mockWifiCallingRepository }, ).init(subId = SUB_ID, callingPreferenceCategoryController) @@ -114,9 +112,7 @@ class WifiCallingPreferenceControllerTest { @Test fun isEnabled_callIdle_enabled() = runBlocking { - mockCallStateRepository.stub { - on { callStateFlow(SUB_ID) } doReturn flowOf(TelephonyManager.CALL_STATE_IDLE) - } + callState = TelephonyManager.CALL_STATE_IDLE controller.onViewCreated(TestLifecycleOwner()) delay(100) @@ -126,9 +122,7 @@ class WifiCallingPreferenceControllerTest { @Test fun isEnabled_notCallIdle_disabled() = runBlocking { - mockCallStateRepository.stub { - on { callStateFlow(SUB_ID) } doReturn flowOf(TelephonyManager.CALL_STATE_RINGING) - } + callState = TelephonyManager.CALL_STATE_RINGING controller.onViewCreated(TestLifecycleOwner()) delay(100)