Merge "Fix Can't Able to Click Sims" into main

This commit is contained in:
Chaohui Wang
2024-09-26 10:15:30 +00:00
committed by Android (Google) Code Review
3 changed files with 78 additions and 15 deletions

View File

@@ -23,11 +23,13 @@ import android.util.Log
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.android.settings.network.SubscriptionUtil import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@@ -36,6 +38,8 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
private const val TAG = "SubscriptionRepository" private const val TAG = "SubscriptionRepository"
@@ -132,20 +136,7 @@ class SubscriptionRepository(private val context: Context) {
fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription() fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()
/** Flow for subscriptions changes. */ /** Flow for subscriptions changes. */
fun subscriptionsChangedFlow() = callbackFlow { fun subscriptionsChangedFlow() = getSharedSubscriptionsChangedFlow(context)
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. */ /** Flow of active subscription ids. */
fun activeSubscriptionIdListFlow(): Flow<List<Int>> = fun activeSubscriptionIdListFlow(): Flow<List<Int>> =
@@ -172,6 +163,57 @@ class SubscriptionRepository(private val context: Context) {
flowOf(null) flowOf(null)
} }
} }
companion object {
private lateinit var SharedSubscriptionsChangedFlow: Flow<Unit>
private fun getSharedSubscriptionsChangedFlow(context: Context): Flow<Unit> {
if (!this::SharedSubscriptionsChangedFlow.isInitialized) {
SharedSubscriptionsChangedFlow =
context.applicationContext
.requireSubscriptionManager()
.subscriptionsChangedFlow()
.shareIn(
scope = CoroutineScope(Dispatchers.Default),
started = SharingStarted.WhileSubscribed(),
replay = 1,
)
}
return SharedSubscriptionsChangedFlow
}
/**
* Flow for subscriptions changes.
*
* Note: Even the SubscriptionManager.addOnSubscriptionsChangedListener's doc says the
* SubscriptionManager.OnSubscriptionsChangedListener.onSubscriptionsChanged() method will
* also be invoked once initially when calling it, there still case that the
* onSubscriptionsChanged() method is not invoked initially. For example, when the
* onSubscriptionsChanged event never happens before, on a device never ever has any
* subscriptions.
*/
private fun SubscriptionManager.subscriptionsChangedFlow() =
callbackFlow {
val listener =
object : SubscriptionManager.OnSubscriptionsChangedListener() {
override fun onSubscriptionsChanged() {
trySend(Unit)
}
override fun onAddListenerFailed() {
close()
}
}
addOnSubscriptionsChangedListener(Dispatchers.Default.asExecutor(), listener)
awaitClose { removeOnSubscriptionsChangedListener(listener) }
}
.onStart { emit(Unit) } // Ensure this flow is never empty
.conflate()
.onEach { Log.d(TAG, "subscriptions changed") }
.flowOn(Dispatchers.Default)
}
} }
val Context.subscriptionManager: SubscriptionManager? val Context.subscriptionManager: SubscriptionManager?

View File

@@ -62,10 +62,15 @@ class MobileNetworkSwitchControllerTest {
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false) on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
} }
private val mockSubscriptionActivationRepository = mock<SubscriptionActivationRepository> {
on { isActivationChangeableFlow() } doReturn flowOf(true)
}
private val controller = MobileNetworkSwitchController( private val controller = MobileNetworkSwitchController(
context = context, context = context,
preferenceKey = TEST_KEY, preferenceKey = TEST_KEY,
subscriptionRepository = mockSubscriptionRepository, subscriptionRepository = mockSubscriptionRepository,
subscriptionActivationRepository = mockSubscriptionActivationRepository,
).apply { init(SUB_ID) } ).apply { init(SUB_ID) }
@Test @Test

View File

@@ -91,7 +91,23 @@ class SubscriptionRepositoryTest {
subInfoListener?.onSubscriptionsChanged() subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).hasSize(2) assertThat(listDeferred.await().size).isAtLeast(2)
}
@Test
fun subscriptionsChangedFlow_managerNotCallOnSubscriptionsChangedInitially() = runBlocking {
mockSubscriptionManager.stub {
on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer
{
subInfoListener =
it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
// not call onSubscriptionsChanged here
}
}
val initialValue = repository.subscriptionsChangedFlow().firstWithTimeoutOrNull()
assertThat(initialValue).isSameInstanceAs(Unit)
} }
@Test @Test