From 007ef134b677baf2e1fa557ea8b045d337cc3d4a Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 15 Dec 2023 12:10:31 +0800 Subject: [PATCH] Hide MMS messages if switch mobile data automatically Per designer's comment, hide the "MMS messages" if user cannot use it to control MMS. Fix: 303759893 Test: manual - turn "Switch mobile data automatically" on / off Test: unit test Change-Id: I874524c0cedb48f7daf87f32920e26428ae78c89 --- .../network/SubscriptionInfoListViewModel.kt | 29 ++------ .../MmsMessagePreferenceController.kt | 41 +++++------ .../telephony/SubscriptionRepository.kt | 43 ++++++++++++ .../MmsMessagePreferenceControllerTest.kt | 14 ++++ .../telephony/SubscriptionRepositoryTest.kt | 70 +++++++++++++++++++ 5 files changed, 150 insertions(+), 47 deletions(-) create mode 100644 src/com/android/settings/network/telephony/SubscriptionRepository.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt diff --git a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt index ee88177e491..ed930d4c8db 100644 --- a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt +++ b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt @@ -17,37 +17,22 @@ package com.android.settings.network import android.app.Application -import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager - import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope - +import com.android.settings.network.telephony.subscriptionsChangedFlow import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asExecutor -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel(application) { + private val subscriptionManager = + application.getSystemService(SubscriptionManager::class.java)!! private val scope = viewModelScope + Dispatchers.Default - val subscriptionInfoListFlow = callbackFlow> { - val subscriptionManager = application.getSystemService(SubscriptionManager::class.java)!! - val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() { - override fun onSubscriptionsChanged() { - trySend(SubscriptionUtil.getActiveSubscriptions(subscriptionManager)) - } - } - - subscriptionManager.addOnSubscriptionsChangedListener( - Dispatchers.Default.asExecutor(), - listener, - ) - - awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) } - }.conflate().stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList()) + val subscriptionInfoListFlow = application.subscriptionsChangedFlow().map { + SubscriptionUtil.getActiveSubscriptions(subscriptionManager) + }.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList()) } diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt index 23a97388c66..4e41038f068 100644 --- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt +++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt @@ -17,33 +17,25 @@ package com.android.settings.network.telephony import android.content.Context -import android.os.Handler -import android.os.Looper import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.data.ApnSetting -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceScreen -import com.android.settings.network.MobileDataContentObserver +import com.android.settings.network.mobileDataEnabledFlow +import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle +import kotlinx.coroutines.flow.combine /** * Preference controller for "MMS messages" */ class MmsMessagePreferenceController(context: Context, key: String) : - TelephonyTogglePreferenceController(context, key), DefaultLifecycleObserver { + TelephonyTogglePreferenceController(context, key) { private lateinit var telephonyManager: TelephonyManager private var preferenceScreen: PreferenceScreen? = null - private val mobileDataContentObserver = - MobileDataContentObserver(Handler(Looper.getMainLooper())).apply { - setOnMobileDataChangedListener { - preferenceScreen?.let { super.displayPreference(it) } - } - } - fun init(subId: Int) { mSubId = subId telephonyManager = mContext.getSystemService(TelephonyManager::class.java)!! @@ -53,26 +45,25 @@ class MmsMessagePreferenceController(context: Context, key: String) : override fun getAvailabilityStatus(subId: Int) = if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && !telephonyManager.isDataEnabled && - telephonyManager.isApnMetered(ApnSetting.TYPE_MMS) + telephonyManager.isApnMetered(ApnSetting.TYPE_MMS) && + !telephonyManager.isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH + ) ) AVAILABLE else CONDITIONALLY_UNAVAILABLE - override fun onStart(owner: LifecycleOwner) { - if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - mobileDataContentObserver.register(mContext, mSubId) - } - } - - override fun onStop(owner: LifecycleOwner) { - if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - mobileDataContentObserver.unRegister(mContext) - } - } - override fun displayPreference(screen: PreferenceScreen) { super.displayPreference(screen) preferenceScreen = screen } + override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + combine( + mContext.mobileDataEnabledFlow(mSubId), + mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes + ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { + preferenceScreen?.let { super.displayPreference(it) } } + } + override fun isChecked(): Boolean = telephonyManager.isMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED ) diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt new file mode 100644 index 00000000000..7a14d6bf302 --- /dev/null +++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt @@ -0,0 +1,43 @@ +/* + * 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. + * 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.SubscriptionManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn + +fun Context.subscriptionsChangedFlow() = callbackFlow { + val subscriptionManager = getSystemService(SubscriptionManager::class.java)!! + + val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() { + override fun onSubscriptionsChanged() { + trySend(Unit) + } + } + + subscriptionManager.addOnSubscriptionsChangedListener( + Dispatchers.Default.asExecutor(), + listener, + ) + + awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) } +}.conflate().flowOn(Dispatchers.Default) diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt index 33a857539c6..588881941b9 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt @@ -79,6 +79,20 @@ class MmsMessagePreferenceControllerTest { assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) } + @Test + fun getAvailabilityStatus_autoDataSwitch_returnUnavailable() { + mockTelephonyManager.stub { + on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true + } + + val availabilityStatus = controller.getAvailabilityStatus(SUB_ID) + + assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) + } + @Test fun getAvailabilityStatus_mobileDataOffWithValidSubId_returnAvailable() { mockTelephonyManager.stub { diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt new file mode 100644 index 00000000000..28871340888 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt @@ -0,0 +1,70 @@ +/* + * 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. + * 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.SubscriptionManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +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.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy + +@RunWith(AndroidJUnit4::class) +class SubscriptionRepositoryTest { + private var subInfoListener: SubscriptionManager.OnSubscriptionsChangedListener? = null + + private val mockSubscriptionManager = mock { + on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer { + subInfoListener = it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener + subInfoListener?.onSubscriptionsChanged() + } + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager + } + + @Test + fun subscriptionsChangedFlow_hasInitialValue() = runBlocking { + val initialValue = context.subscriptionsChangedFlow().firstWithTimeoutOrNull() + + assertThat(initialValue).isSameInstanceAs(Unit) + } + + @Test + fun subscriptionsChangedFlow_changed() = runBlocking { + val listDeferred = async { + context.subscriptionsChangedFlow().toListWithTimeout() + } + delay(100) + + subInfoListener?.onSubscriptionsChanged() + + assertThat(listDeferred.await()).hasSize(2) + } +}