1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.datausage
18 
19 import android.content.Context
20 import android.net.NetworkPolicy
21 import android.net.NetworkTemplate
22 import android.text.TextUtils
23 import android.util.Log
24 import androidx.lifecycle.LifecycleOwner
25 import androidx.preference.PreferenceScreen
26 import com.android.settings.R
27 import com.android.settings.datausage.lib.DataUsageLib.getMobileTemplate
28 import com.android.settings.datausage.lib.INetworkCycleDataRepository
29 import com.android.settings.datausage.lib.NetworkCycleDataRepository
30 import com.android.settings.network.ProxySubscriptionManager
31 import com.android.settings.network.policy.NetworkPolicyRepository
32 import com.android.settings.network.telephony.TelephonyBasePreferenceController
33 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
34 import kotlin.math.max
35 import kotlinx.coroutines.Dispatchers
36 import kotlinx.coroutines.withContext
37 
38 /**
39  * This is the controller for a data usage header that retrieves carrier data from the new
40  * subscriptions framework API if available. The controller reads subscription information from the
41  * framework and falls back to legacy usage data if none are available.
42  */
43 open class DataUsageSummaryPreferenceController @JvmOverloads constructor(
44     context: Context,
45     subId: Int,
46     private val proxySubscriptionManager: ProxySubscriptionManager =
47         ProxySubscriptionManager.getInstance(context),
48     private val networkPolicyRepository: NetworkPolicyRepository = NetworkPolicyRepository(context),
49     private val networkCycleDataRepositoryFactory: (
50         template: NetworkTemplate,
51     ) -> INetworkCycleDataRepository = { NetworkCycleDataRepository(context, it) },
52     private val dataPlanRepositoryFactory: (
53         networkCycleDataRepository: INetworkCycleDataRepository,
<lambda>null54     ) -> DataPlanRepository = { DataPlanRepositoryImpl(it) }
55 ) : TelephonyBasePreferenceController(context, KEY) {
56 
57     init {
58         mSubId = subId
59     }
60 
<lambda>null61     private val subInfo by lazy {
62         if (DataUsageUtils.hasMobileData(mContext)) {
63             proxySubscriptionManager.getAccessibleSubscriptionInfo(mSubId)
64         } else null
65     }
66 
<lambda>null67     private val networkTemplate by lazy { getMobileTemplate(mContext, mSubId) }
68 
<lambda>null69     private val networkCycleDataRepository by lazy {
70         networkCycleDataRepositoryFactory(networkTemplate)
71     }
72 
73     private lateinit var preference: DataUsageSummaryPreference
74 
getAvailabilityStatusnull75     override fun getAvailabilityStatus(subId: Int) =
76         if (subInfo != null) AVAILABLE else CONDITIONALLY_UNAVAILABLE
77 
78     override fun displayPreference(screen: PreferenceScreen) {
79         super.displayPreference(screen)
80         preference = screen.findPreference(preferenceKey)!!
81     }
82 
onViewCreatednull83     override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
84         networkPolicyRepository.networkPolicyFlow(networkTemplate)
85             .collectLatestWithLifecycle(viewLifecycleOwner) { policy ->
86                 preference.isVisible = subInfo != null && policy != null
87                 if (policy != null) update(policy)
88             }
89     }
90 
updatenull91     private suspend fun update(policy: NetworkPolicy) {
92         preference.setLimitInfo(policy.getLimitInfo())
93         val dataBarSize = max(policy.limitBytes, policy.warningBytes)
94         if (dataBarSize > NetworkPolicy.WARNING_DISABLED) {
95             setDataBarSize(dataBarSize)
96         }
97         val dataPlanInfo = withContext(Dispatchers.Default) {
98             dataPlanRepositoryFactory(networkCycleDataRepository).getDataPlanInfo(
99                 policy = policy,
100                 plans = proxySubscriptionManager.get().getSubscriptionPlans(mSubId),
101             )
102         }
103         Log.d(TAG, "dataPlanInfo: $dataPlanInfo")
104         preference.setUsageNumbers(dataPlanInfo.dataPlanUse, dataPlanInfo.dataPlanSize)
105         if (dataPlanInfo.dataBarSize > 0) {
106             preference.setChartEnabled(true)
107             setDataBarSize(dataPlanInfo.dataBarSize)
108             preference.setProgress(dataPlanInfo.dataPlanUse / dataPlanInfo.dataBarSize.toFloat())
109         } else {
110             preference.setChartEnabled(false)
111         }
112 
113         preference.setUsageInfo(
114             dataPlanInfo.cycleEnd,
115             dataPlanInfo.snapshotTime,
116             subInfo?.carrierName,
117             dataPlanInfo.dataPlanCount,
118         )
119     }
120 
setDataBarSizenull121     private fun setDataBarSize(dataBarSize: Long) {
122         preference.setLabels(
123             DataUsageUtils.formatDataUsage(mContext, /* byteValue = */ 0),
124             DataUsageUtils.formatDataUsage(mContext, dataBarSize)
125         )
126     }
127 
getLimitInfonull128     private fun NetworkPolicy.getLimitInfo(): CharSequence? = when {
129         warningBytes > 0 && limitBytes > 0 -> {
130             TextUtils.expandTemplate(
131                 mContext.getText(R.string.cell_data_warning_and_limit),
132                 DataUsageUtils.formatDataUsage(mContext, warningBytes),
133                 DataUsageUtils.formatDataUsage(mContext, limitBytes),
134             )
135         }
136 
137         warningBytes > 0 -> {
138             TextUtils.expandTemplate(
139                 mContext.getText(R.string.cell_data_warning),
140                 DataUsageUtils.formatDataUsage(mContext, warningBytes),
141             )
142         }
143 
144         limitBytes > 0 -> {
145             TextUtils.expandTemplate(
146                 mContext.getText(R.string.cell_data_limit),
147                 DataUsageUtils.formatDataUsage(mContext, limitBytes),
148             )
149         }
150 
151         else -> null
152     }
153 
154     companion object {
155         private const val TAG = "DataUsageSummaryPC"
156         private const val KEY = "status_header"
157     }
158 }
159