Files
app_Settings/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.kt
Chaohui Wang d3448f3a91 Migrate all data usage format use case
Now they are all aligned.

Bug: 321861088
Flag: EXEMPT bug fix
Test: unit test
Change-Id: I1981fd32fd24de7c9eff93e0cd638e862f01b9b9
2025-01-02 18:14:10 -08:00

162 lines
6.1 KiB
Kotlin

/*
* 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.datausage
import android.content.Context
import android.net.NetworkPolicy
import android.net.NetworkTemplate
import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.datausage.lib.DataUsageFormatter
import com.android.settings.datausage.lib.DataUsageLib.getMobileTemplate
import com.android.settings.datausage.lib.INetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkCycleDataRepository
import com.android.settings.network.ProxySubscriptionManager
import com.android.settings.network.policy.NetworkPolicyRepository
import com.android.settings.network.telephony.TelephonyBasePreferenceController
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlin.math.max
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* This is the controller for a data usage header that retrieves carrier data from the new
* subscriptions framework API if available. The controller reads subscription information from the
* framework and falls back to legacy usage data if none are available.
*/
open class DataUsageSummaryPreferenceController @JvmOverloads constructor(
context: Context,
subId: Int,
private val proxySubscriptionManager: ProxySubscriptionManager =
ProxySubscriptionManager.getInstance(context),
private val networkPolicyRepository: NetworkPolicyRepository = NetworkPolicyRepository(context),
private val networkCycleDataRepositoryFactory: (
template: NetworkTemplate,
) -> INetworkCycleDataRepository = { NetworkCycleDataRepository(context, it) },
private val dataPlanRepositoryFactory: (
networkCycleDataRepository: INetworkCycleDataRepository,
) -> DataPlanRepository = { DataPlanRepositoryImpl(it) }
) : TelephonyBasePreferenceController(context, KEY) {
init {
mSubId = subId
}
private val subInfo by lazy {
if (DataUsageUtils.hasMobileData(mContext)) {
proxySubscriptionManager.getAccessibleSubscriptionInfo(mSubId)
} else null
}
private val networkTemplate by lazy { getMobileTemplate(mContext, mSubId) }
private val networkCycleDataRepository by lazy {
networkCycleDataRepositoryFactory(networkTemplate)
}
private lateinit var preference: DataUsageSummaryPreference
private val dataUsageFormatter = DataUsageFormatter(mContext)
override fun getAvailabilityStatus(subId: Int) =
if (subInfo != null) AVAILABLE else CONDITIONALLY_UNAVAILABLE
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)!!
}
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
networkPolicyRepository.networkPolicyFlow(networkTemplate)
.collectLatestWithLifecycle(viewLifecycleOwner) { policy ->
preference.isVisible = subInfo != null && policy != null
if (policy != null) update(policy)
}
}
private suspend fun update(policy: NetworkPolicy) {
preference.setLimitInfo(policy.getLimitInfo())
val dataBarSize = max(policy.limitBytes, policy.warningBytes)
if (dataBarSize > NetworkPolicy.WARNING_DISABLED) {
setDataBarSize(dataBarSize)
}
val dataPlanInfo = withContext(Dispatchers.Default) {
dataPlanRepositoryFactory(networkCycleDataRepository).getDataPlanInfo(
policy = policy,
plans = proxySubscriptionManager.get().getSubscriptionPlans(mSubId),
)
}
Log.d(TAG, "dataPlanInfo: $dataPlanInfo")
preference.setUsageNumbers(dataPlanInfo.dataPlanUse, dataPlanInfo.dataPlanSize)
if (dataPlanInfo.dataBarSize > 0) {
preference.setChartEnabled(true)
setDataBarSize(dataPlanInfo.dataBarSize)
preference.setProgress(dataPlanInfo.dataPlanUse / dataPlanInfo.dataBarSize.toFloat())
} else {
preference.setChartEnabled(false)
}
preference.setUsageInfo(
dataPlanInfo.cycleEnd,
dataPlanInfo.snapshotTime,
subInfo?.carrierName,
dataPlanInfo.dataPlanCount,
)
}
private fun setDataBarSize(dataBarSize: Long) {
preference.setLabels(
dataUsageFormatter.formatDataUsage(/* byteValue = */ 0),
dataUsageFormatter.formatDataUsage(dataBarSize)
)
}
private fun NetworkPolicy.getLimitInfo(): CharSequence? = when {
warningBytes > 0 && limitBytes > 0 -> {
TextUtils.expandTemplate(
mContext.getText(R.string.cell_data_warning_and_limit),
dataUsageFormatter.formatDataUsage(warningBytes),
dataUsageFormatter.formatDataUsage(limitBytes),
)
}
warningBytes > 0 -> {
TextUtils.expandTemplate(
mContext.getText(R.string.cell_data_warning),
dataUsageFormatter.formatDataUsage(warningBytes),
)
}
limitBytes > 0 -> {
TextUtils.expandTemplate(
mContext.getText(R.string.cell_data_limit),
dataUsageFormatter.formatDataUsage(limitBytes),
)
}
else -> null
}
companion object {
private const val TAG = "DataUsageSummaryPC"
private const val KEY = "status_header"
}
}