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

Commit 6142ad92 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Create CallStateRepository.isInCallFlow

Which will emit true if any active sub is in call.

And also create SubscriptionActivationRepository to use isInCallFlow.

Bug: 336209156
Test: manual - on MobileNetworkSwitchController
Test: unit test
Change-Id: I75460bf17961349557ac1e19e7f6b15249f3d7b0
parent fb9930eb
Loading
Loading
Loading
Loading
+64 −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.network.telephony

import android.content.Context
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach

@OptIn(ExperimentalCoroutinesApi::class)
class CallStateRepository(private val context: Context) {
    private val subscriptionManager = context.requireSubscriptionManager()

    /** Flow for call state of given [subId]. */
    fun callStateFlow(subId: Int): Flow<Int> = context.telephonyCallbackFlow(subId) {
        object : TelephonyCallback(), TelephonyCallback.CallStateListener {
            override fun onCallStateChanged(state: Int) {
                trySend(state)
            }
        }
    }

    /**
     * Flow for in call state.
     *
     * @return true if any active subscription's call state is not idle.
     */
    fun isInCallFlow(): Flow<Boolean> = context.subscriptionsChangedFlow()
        .flatMapLatest {
            val subIds = subscriptionManager.activeSubscriptionIdList
            combine(subIds.map(::callStateFlow)) { states ->
                states.any { it != TelephonyManager.CALL_STATE_IDLE }
            }
        }
        .conflate()
        .flowOn(Dispatchers.Default)
        .onEach { Log.d(TAG, "isInCallFlow: $it") }

    private companion object {
        private const val TAG = "CallStateRepository"
    }
}
+4 −5
Original line number Diff line number Diff line
@@ -23,10 +23,8 @@ import android.telephony.TelephonyManager
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle

/** This controls a preference allowing the user to delete the profile for an eSIM.  */
@@ -57,7 +55,8 @@ class DeleteSimProfilePreferenceController(context: Context, preferenceKey: Stri
    }

    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
        mContext.callStateFlow(subscriptionId).collectLatestWithLifecycle(viewLifecycleOwner) {
        CallStateRepository(mContext).callStateFlow(subscriptionId)
            .collectLatestWithLifecycle(viewLifecycleOwner) {
                preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE)
            }
    }
+3 −11
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.settings.network.telephony

import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -26,19 +25,17 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.network.SatelliteRepository
import com.android.settings.network.SubscriptionUtil
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map

class MobileNetworkSwitchController @JvmOverloads constructor(
    context: Context,
    preferenceKey: String,
    private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context),
    private val satelliteRepository: SatelliteRepository = SatelliteRepository(context)
    private val subscriptionActivationRepository: SubscriptionActivationRepository =
        SubscriptionActivationRepository(context),
) : ComposePreferenceController(context, preferenceKey) {

    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -57,12 +54,7 @@ class MobileNetworkSwitchController @JvmOverloads constructor(
            subscriptionRepository.isSubscriptionEnabledFlow(subId)
        }.collectAsStateWithLifecycle(initialValue = null)
        val changeable by remember {
            combine(
                context.callStateFlow(subId).map { it == TelephonyManager.CALL_STATE_IDLE },
                satelliteRepository.getIsSessionStartedFlow()
            ) { isCallStateIdle, isSatelliteModemEnabled ->
                isCallStateIdle && !isSatelliteModemEnabled
            }
            subscriptionActivationRepository.isActivationChangeableFlow()
        }.collectAsStateWithLifecycle(initialValue = true)
        MainSwitchPreference(model = object : SwitchPreferenceModel {
            override val title = stringResource(R.string.mobile_network_use_sim_on)
+13 −10
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 * 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.
@@ -17,16 +17,19 @@
package com.android.settings.network.telephony

import android.content.Context
import android.telephony.TelephonyCallback
import com.android.settings.network.SatelliteRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine

/**
 * Flow for call state.
 */
fun Context.callStateFlow(subId: Int): Flow<Int> = telephonyCallbackFlow(subId) {
    object : TelephonyCallback(), TelephonyCallback.CallStateListener {
        override fun onCallStateChanged(state: Int) {
            trySend(state)
        }
class SubscriptionActivationRepository(
    private val context: Context,
    private val callStateRepository: CallStateRepository = CallStateRepository(context),
    private val satelliteRepository: SatelliteRepository = SatelliteRepository(context),
) {
    fun isActivationChangeableFlow(): Flow<Boolean> = combine(
        callStateRepository.isInCallFlow(),
        satelliteRepository.getIsSessionStartedFlow()
    ) { isInCall, isSatelliteModemEnabled ->
        !isInCall && !isSatelliteModemEnabled
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import com.android.settings.R
import com.android.settings.network.telephony.wificalling.WifiCallingRepository
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext

/**
@@ -41,7 +40,7 @@ import kotlinx.coroutines.withContext
open class WifiCallingPreferenceController @JvmOverloads constructor(
    context: Context,
    key: String,
    private val callStateFlowFactory: (subId: Int) -> Flow<Int> = context::callStateFlow,
    private val callStateRepository: CallStateRepository = CallStateRepository(context),
    private val wifiCallingRepositoryFactory: (subId: Int) -> WifiCallingRepository = { subId ->
        WifiCallingRepository(context, subId)
    },
@@ -91,7 +90,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor(
                if (isReady) update()
            }

        callStateFlowFactory(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) {
        callStateRepository.callStateFlow(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) {
            preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE)
        }
    }
Loading