SubscriptionRepository.activeSubscriptionIdListFlow

Bug: 328293508
Flag: EXEMPT refactor
Test: manual - on Mobile Settings
Test: unit test
Change-Id: I63a86569f4fa3a27bd38d9853f6141890d26b881
This commit is contained in:
Chaohui Wang
2024-06-25 11:42:22 +08:00
parent 69f686828b
commit edc72a9b0f
5 changed files with 59 additions and 41 deletions

View File

@@ -25,14 +25,17 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
@OptIn(ExperimentalCoroutinesApi::class)
class CallStateRepository(private val context: Context) {
private val subscriptionManager = context.requireSubscriptionManager()
class CallStateRepository(
private val context: Context,
private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context),
) {
/** Flow for call state of given [subId]. */
fun callStateFlow(subId: Int): Flow<Int> = context.telephonyCallbackFlow(subId) {
@@ -48,9 +51,8 @@ class CallStateRepository(private val context: Context) {
*
* @return true if any active subscription's call state is not idle.
*/
fun isInCallFlow(): Flow<Boolean> = context.subscriptionsChangedFlow()
.flatMapLatest {
val subIds = subscriptionManager.activeSubscriptionIdList
fun isInCallFlow(): Flow<Boolean> = subscriptionRepository.activeSubscriptionIdListFlow()
.flatMapLatest { subIds ->
if (subIds.isEmpty()) {
flowOf(false)
} else {
@@ -59,9 +61,10 @@ class CallStateRepository(private val context: Context) {
}
}
}
.distinctUntilChanged()
.conflate()
.flowOn(Dispatchers.Default)
.onEach { Log.d(TAG, "isInCallFlow: $it") }
.flowOn(Dispatchers.Default)
private companion object {
private const val TAG = "CallStateRepository"

View File

@@ -29,6 +29,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
@@ -68,6 +69,30 @@ class SubscriptionRepository(private val context: Context) {
}
fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()
/** Flow for subscriptions changes. */
fun subscriptionsChangedFlow() = callbackFlow {
val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
override fun onSubscriptionsChanged() {
trySend(Unit)
}
}
subscriptionManager.addOnSubscriptionsChangedListener(
Dispatchers.Default.asExecutor(),
listener,
)
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
/** Flow of active subscription ids. */
fun activeSubscriptionIdListFlow(): Flow<List<Int>> = context.subscriptionsChangedFlow()
.map { subscriptionManager.activeSubscriptionIdList.sorted() }
.distinctUntilChanged()
.conflate()
.onEach { Log.d(TAG, "activeSubscriptionIdList: $it") }
.flowOn(Dispatchers.Default)
}
val Context.subscriptionManager: SubscriptionManager?
@@ -79,22 +104,8 @@ fun Context.phoneNumberFlow(subscriptionInfo: SubscriptionInfo) = subscriptionsC
SubscriptionUtil.getBidiFormattedPhoneNumber(this, subscriptionInfo)
}.filterNot { it.isNullOrEmpty() }.flowOn(Dispatchers.Default)
fun Context.subscriptionsChangedFlow() = callbackFlow {
val subscriptionManager = requireSubscriptionManager()
val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
override fun onSubscriptionsChanged() {
trySend(Unit)
}
}
subscriptionManager.addOnSubscriptionsChangedListener(
Dispatchers.Default.asExecutor(),
listener,
)
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
fun Context.subscriptionsChangedFlow(): Flow<Unit> =
SubscriptionRepository(this).subscriptionsChangedFlow()
/**
* Return a list of subscriptions that are available and visible to the user.

View File

@@ -25,10 +25,9 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.android.settings.R
import com.android.settings.network.telephony.MobileDataRepository
import com.android.settings.network.telephony.SubscriptionRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settings.network.telephony.safeGetConfig
import com.android.settings.network.telephony.subscriptionsChangedFlow
import com.android.settings.network.telephony.telephonyManager
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import kotlinx.coroutines.Dispatchers
@@ -48,7 +47,7 @@ class CrossSimCallingViewModel(
private val application: Application,
) : AndroidViewModel(application) {
private val subscriptionManager = application.requireSubscriptionManager()
private val subscriptionRepository = SubscriptionRepository(application)
private val carrierConfigManager =
application.getSystemService(CarrierConfigManager::class.java)!!
private val scope = viewModelScope + Dispatchers.Default
@@ -59,9 +58,8 @@ class CrossSimCallingViewModel(
init {
val resources = application.resources
if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
application.subscriptionsChangedFlow()
.flatMapLatest {
val activeSubIds = subscriptionManager.activeSubscriptionIdList.toList()
subscriptionRepository.activeSubscriptionIdListFlow()
.flatMapLatest { activeSubIds ->
merge(
activeSubIds.anyMobileDataEnableChangedFlow(),
updateChannel.receiveAsFlow(),

View File

@@ -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
@@ -27,6 +26,7 @@ import com.android.settingslib.spa.testutils.toListWithTimeout
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,20 +49,15 @@ class CallStateRepositoryTest {
}
}
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { activeSubscriptionIdList } doReturn intArrayOf(SUB_ID)
on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer {
val listener = it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
listener.onSubscriptionsChanged()
}
private val mockSubscriptionRepository = mock<SubscriptionRepository> {
on { activeSubscriptionIdListFlow() } doReturn flowOf(listOf(SUB_ID))
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
on { subscriptionManager } doReturn mockSubscriptionManager
}
private val repository = CallStateRepository(context)
private val repository = CallStateRepository(context, mockSubscriptionRepository)
@Test
fun callStateFlow_initial_sendInitialState() = runBlocking {
@@ -89,8 +84,8 @@ class CallStateRepositoryTest {
@Test
fun isInCallFlow_noActiveSubscription() = runBlocking {
mockSubscriptionManager.stub {
on { activeSubscriptionIdList } doReturn intArrayOf()
mockSubscriptionRepository.stub {
on { activeSubscriptionIdListFlow() } doReturn flowOf(emptyList())
}
val isInCall = repository.isInCallFlow().firstWithTimeoutOrNull()

View File

@@ -77,7 +77,7 @@ class SubscriptionRepositoryTest {
@Test
fun subscriptionsChangedFlow_hasInitialValue() = runBlocking {
val initialValue = context.subscriptionsChangedFlow().firstWithTimeoutOrNull()
val initialValue = repository.subscriptionsChangedFlow().firstWithTimeoutOrNull()
assertThat(initialValue).isSameInstanceAs(Unit)
}
@@ -85,7 +85,7 @@ class SubscriptionRepositoryTest {
@Test
fun subscriptionsChangedFlow_changed() = runBlocking {
val listDeferred = async {
context.subscriptionsChangedFlow().toListWithTimeout()
repository.subscriptionsChangedFlow().toListWithTimeout()
}
delay(100)
@@ -94,6 +94,17 @@ class SubscriptionRepositoryTest {
assertThat(listDeferred.await()).hasSize(2)
}
@Test
fun activeSubscriptionIdListFlow(): Unit = runBlocking {
mockSubscriptionManager.stub {
on { activeSubscriptionIdList } doReturn intArrayOf(SUB_ID_IN_SLOT_0)
}
val activeSubIds = repository.activeSubscriptionIdListFlow().firstWithTimeoutOrNull()
assertThat(activeSubIds).containsExactly(SUB_ID_IN_SLOT_0)
}
@Test
fun getSelectableSubscriptionInfoList_sortedBySimSlotIndex() {
mockSubscriptionManager.stub {