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

Commit 732636cb authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Refactor AutomaticDataSwitchingPreference" into main

parents bb8652e1 bfd8a517
Loading
Loading
Loading
Loading
+6 −13
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo
import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.sim.SimActivationNotifier
import com.android.settings.spa.network.setAutomaticData
import com.android.settings.spa.network.setDefaultData
@@ -31,6 +32,7 @@ import com.android.settings.spa.network.setDefaultSms
import com.android.settings.spa.network.setDefaultVoice
import com.android.settingslib.utils.ThreadUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext

class SimOnboardingService {
@@ -46,7 +48,7 @@ class SimOnboardingService {
    var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
    var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
    var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
    var targetPrimarySimAutoDataSwitch: Boolean = false
    val targetPrimarySimAutoDataSwitch = MutableStateFlow(false)
    var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
        get() {
            if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -349,19 +351,10 @@ class SimOnboardingService {
                    null,
                    targetPrimarySimMobileData
                )

                var nonDds = targetNonDds
                Log.d(
                    TAG,
                    "setAutomaticData: targetNonDds: $nonDds," +
                            " targetPrimarySimAutoDataSwitch: $targetPrimarySimAutoDataSwitch"
                TelephonyRepository(context).setAutomaticData(
                    targetNonDds,
                    targetPrimarySimAutoDataSwitch.value
                )
                if (nonDds != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                    val telephonyManagerForNonDds: TelephonyManager? =
                        context.getSystemService(TelephonyManager::class.java)
                            ?.createForSubscriptionId(nonDds)
                    setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
                }
            }
            // no next action, send finish
            callback(CallbackType.CALLBACK_FINISH)
+44 −2
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
package com.android.settings.network.telephony

import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.ProducerScope
@@ -26,15 +28,51 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

class TelephonyRepository(
    private val context: Context,
    private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(),
) {
    fun isMobileDataPolicyEnabledFlow(
        subId: Int,
        @TelephonyManager.MobileDataPolicy policy: Int,
    ): Flow<Boolean> {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)

        val telephonyManager = context.telephonyManager(subId)

        return subscriptionsChangedFlow.map {
            telephonyManager.isMobileDataPolicyEnabled(policy)
                .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") }
        }.conflate().flowOn(Dispatchers.Default)
    }

    fun setMobileDataPolicyEnabled(
        subId: Int,
        @TelephonyManager.MobileDataPolicy policy: Int,
        enabled: Boolean,
    ) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) return

        val telephonyManager = context.telephonyManager(subId)
        Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled")
        telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
    }

    private companion object {
        private const val TAG = "TelephonyRepository"
    }
}

/** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
fun <T> Context.telephonyCallbackFlow(
    subId: Int,
    block: ProducerScope<T>.() -> TelephonyCallback,
): Flow<T> = callbackFlow {
    val telephonyManager = getSystemService(TelephonyManager::class.java)!!
        .createForSubscriptionId(subId)
    val telephonyManager = telephonyManager(subId)

    val callback = block()

@@ -42,3 +80,7 @@ fun <T> Context.telephonyCallbackFlow(

    awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}.conflate().flowOn(Dispatchers.Default)

fun Context.telephonyManager(subId: Int): TelephonyManager =
    getSystemService(TelephonyManager::class.java)!!
        .createForSubscriptionId(subId)
+58 −0
Original line number Diff line number Diff line
/*
 * 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.spa.network

import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@Composable
fun AutomaticDataSwitchingPreference(
    isAutoDataEnabled: () -> Boolean?,
    setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
) {
    val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
    val coroutineScope = rememberCoroutineScope()
    SwitchPreference(
        object : SwitchPreferenceModel {
            override val title = stringResource(id = R.string.primary_sim_automatic_data_title)
            override val summary = { autoDataSummary }
            override val checked = { isAutoDataEnabled() }
            override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
                coroutineScope.launch(Dispatchers.Default) {
                    setAutoDataEnabled(newEnabled)
                }
            }
        }
    )
}

fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) {
    setMobileDataPolicyEnabled(
        subId = subId,
        policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
        enabled = newEnabled,
    )
    //TODO: setup backup calling
}
+14 −66
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
@@ -44,6 +43,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -53,8 +53,6 @@ import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
@@ -193,7 +191,6 @@ fun PrimarySimImpl(
    callsSelectedId: MutableIntState,
    textsSelectedId: MutableIntState,
    mobileDataSelectedId: MutableIntState,
    nonDds: MutableIntState,
    subscriptionManager: SubscriptionManager? =
        LocalContext.current.getSystemService(SubscriptionManager::class.java),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
@@ -223,23 +220,9 @@ fun PrimarySimImpl(
            )
        }
    },
    actionSetAutoDataSwitch: (Boolean) -> Unit = { newState ->
        coroutineScope.launch {
            val telephonyManagerForNonDds: TelephonyManager? =
                context.getSystemService(TelephonyManager::class.java)
                    ?.createForSubscriptionId(nonDds.intValue)
            Log.d(NetworkCellularGroupProvider.name, "NonDds:${nonDds.intValue} setAutomaticData")
            setAutomaticData(telephonyManagerForNonDds, newState)
        }
    },
    isAutoDataEnabled: () -> Boolean?,
    setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
) {
    val telephonyManagerForNonDds: TelephonyManager? =
            context.getSystemService(TelephonyManager::class.java)
                    ?.createForSubscriptionId(nonDds.intValue)
    val automaticDataChecked = rememberSaveable() {
        mutableStateOf(false)
    }

    CreatePrimarySimListPreference(
        stringResource(id = R.string.primary_sim_calls_title),
        primarySimInfo.callsAndSmsList,
@@ -262,31 +245,7 @@ fun PrimarySimImpl(
        actionSetMobileData
    )

    val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
    val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
    SwitchPreference(
        object : SwitchPreferenceModel {
            override val title = autoDataTitle
            override val summary = { autoDataSummary }
            override val checked = {
                if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                    coroutineScope.launch {
                        automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
                        Log.d(
                            NetworkCellularGroupProvider.name,
                            "NonDds:${nonDds.intValue}" +
                                "getAutomaticData:${automaticDataChecked.value}"
                        )
                    }
                }
                automaticDataChecked.value
            }
            override val onCheckedChange: ((Boolean) -> Unit)? = {
                automaticDataChecked.value = it
                actionSetAutoDataSwitch(it)
            }
        }
    )
    AutomaticDataSwitchingPreference(isAutoDataEnabled, setAutoDataEnabled)
}

@Composable
@@ -308,12 +267,21 @@ fun PrimarySimSectionImpl(
    }.collectAsStateWithLifecycle(initialValue = null).value ?: return

    Category(title = stringResource(id = R.string.primary_sim_title)) {
        val isAutoDataEnabled by remember(nonDds.intValue) {
            TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
                subId = nonDds.intValue,
                policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
            )
        }.collectAsStateWithLifecycle(initialValue = null)
        PrimarySimImpl(
            primarySimInfo,
            callsSelectedId,
            textsSelectedId,
            mobileDataSelectedId,
            nonDds
            isAutoDataEnabled = { isAutoDataEnabled },
            setAutoDataEnabled = { newEnabled ->
                TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
            },
        )
    }
}
@@ -381,23 +349,3 @@ suspend fun setDefaultData(
            wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
        }
    }

suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
    withContext(Dispatchers.Default) {
        telephonyManagerForNonDds != null
            && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
            TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
    }

suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
    withContext(Dispatchers.Default) {
        Log.d(
            NetworkCellularGroupProvider.name,
            "setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
        )
        telephonyManager?.setMobileDataPolicyEnabled(
            TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
            newState
        )
        //TODO: setup backup calling
    }
 No newline at end of file
+8 −6
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
@@ -75,9 +76,6 @@ fun SimOnboardingPrimarySimImpl(
        val mobileDataSelectedId = rememberSaveable {
            mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
        }
        val nonDdsRemember = rememberSaveable {
            mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
        }

        Column(Modifier.padding(SettingsDimension.itemPadding)) {
            SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
@@ -94,12 +92,14 @@ fun SimOnboardingPrimarySimImpl(
        callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
        textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
        mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
        val isAutoDataEnabled by
            onboardingService.targetPrimarySimAutoDataSwitch
                .collectAsStateWithLifecycle(initialValue = null)
        PrimarySimImpl(
            primarySimInfo = primarySimInfo,
            callsSelectedId = callsSelectedId,
            textsSelectedId = textsSelectedId,
            mobileDataSelectedId = mobileDataSelectedId,
            nonDds = nonDdsRemember,
            actionSetCalls = {
                callsSelectedId.intValue = it
                onboardingService.targetPrimarySimCalls = it},
@@ -109,8 +109,10 @@ fun SimOnboardingPrimarySimImpl(
            actionSetMobileData = {
                mobileDataSelectedId.intValue = it
                onboardingService.targetPrimarySimMobileData = it},
            actionSetAutoDataSwitch = {
                onboardingService.targetPrimarySimAutoDataSwitch = it},
            isAutoDataEnabled = { isAutoDataEnabled },
            setAutoDataEnabled = { newEnabled ->
                onboardingService.targetPrimarySimAutoDataSwitch.value = newEnabled
            },
        )
    }
}
Loading