Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 007ef134 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

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
parent facd2f71
Loading
Loading
Loading
Loading
+7 −22
Original line number Diff line number Diff line
@@ -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<List<SubscriptionInfo>> {
        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())
}
+16 −25
Original line number Diff line number Diff line
@@ -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
    )
+43 −0
Original line number Diff line number Diff line
/*
 * 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)
+14 −0
Original line number Diff line number Diff line
@@ -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 {
+70 −0
Original line number Diff line number Diff line
/*
 * 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<SubscriptionManager> {
        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)
    }
}