• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.app.settings.SettingsEnums
20 import android.net.NetworkPolicy
21 import android.net.NetworkTemplate
22 import android.os.Bundle
23 import android.provider.Settings
24 import android.telephony.SubscriptionManager
25 import android.util.EventLog
26 import android.util.Log
27 import android.view.View
28 import androidx.annotation.OpenForTesting
29 import androidx.annotation.VisibleForTesting
30 import androidx.fragment.app.viewModels
31 import androidx.preference.Preference
32 import com.android.settings.R
33 import com.android.settings.dashboard.DashboardFragment
34 import com.android.settings.datausage.lib.BillingCycleRepository
35 import com.android.settings.datausage.lib.NetworkUsageData
36 import com.android.settings.network.SubscriptionUtil
37 import com.android.settings.network.telephony.SubscriptionRepository
38 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
39 import com.android.settingslib.spaprivileged.framework.common.userManager
40 import kotlin.jvm.optionals.getOrNull
41 
42 /**
43  * Panel showing data usage history across various networks, including options
44  * to inspect based on usage cycle and control through [NetworkPolicy].
45  */
46 @OpenForTesting
47 open class DataUsageList : DashboardFragment() {
48     @VisibleForTesting
49     var template: NetworkTemplate? = null
50         private set
51 
52     @VisibleForTesting
53     var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
54         private set
55 
56     private lateinit var billingCycleRepository: BillingCycleRepository
57 
58     private var usageAmount: Preference? = null
59     private var dataUsageListAppsController: DataUsageListAppsController? = null
60     private var chartDataUsagePreferenceController: ChartDataUsagePreferenceController? = null
61     private var dataUsageListHeaderController: DataUsageListHeaderController? = null
62 
63     private val viewModel: DataUsageListViewModel by viewModels()
64 
65     override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST
66 
67     override fun onCreate(savedInstanceState: Bundle?) {
68         super.onCreate(savedInstanceState)
69         billingCycleRepository = BillingCycleRepository(requireContext())
70         if (requireContext().userManager.isGuestUser) {
71             Log.e(TAG, "This setting isn't available for guest user")
72             EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user")
73             finish()
74             return
75         }
76         if (!billingCycleRepository.isBandwidthControlEnabled()) {
77             Log.w(TAG, "No bandwidth control; leaving")
78             finish()
79             return
80         }
81         usageAmount = findPreference(KEY_USAGE_AMOUNT)
82         processArgument()
83         val template = template
84         if (template == null) {
85             Log.e(TAG, "No template; leaving")
86             finish()
87             return
88         }
89         dataUsageListAppsController = use(DataUsageListAppsController::class.java).apply {
90             init(template)
91         }
92         chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java)
93             .apply { init(template) }
94 
95         updateWarning()
96     }
97 
98     private fun updateWarning() {
99         val template = template ?: return
100         val warningPreference = findPreference<Preference>(KEY_WARNING)!!
101         if (template.matchRule != NetworkTemplate.MATCH_WIFI) {
102             warningPreference.setSummary(R.string.operator_warning)
103         } else if (SubscriptionUtil.isSimHardwareVisible(context)) {
104             warningPreference.setSummary(R.string.non_carrier_data_usage_warning)
105         }
106     }
107 
108     override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
109         super.onViewCreated(v, savedInstanceState)
110 
111         billingCycleRepository.isModifiableFlow(subId)
112             .collectLatestWithLifecycle(viewLifecycleOwner, action = ::updatePolicy)
113 
114         val template = template ?: return
115         viewModel.templateFlow.value = template
116         dataUsageListHeaderController = DataUsageListHeaderController(
117             setPinnedHeaderView(R.layout.apps_filter_spinner),
118             template,
119             metricsCategory,
120             viewLifecycleOwner,
121             viewModel.cyclesFlow,
122             ::updateSelectedCycle,
123         )
124         viewModel.cyclesFlow.collectLatestWithLifecycle(viewLifecycleOwner) { cycles ->
125             dataUsageListAppsController?.updateCycles(cycles)
126         }
127         viewModel.chartDataFlow.collectLatestWithLifecycle(viewLifecycleOwner) { chartData ->
128             chartDataUsagePreferenceController?.update(chartData)
129         }
130         finishIfSubscriptionDisabled()
131     }
132 
133     private fun finishIfSubscriptionDisabled() {
134         if (SubscriptionManager.isUsableSubscriptionId(subId)) {
135             SubscriptionRepository(requireContext()).isSubscriptionEnabledFlow(subId)
136                 .collectLatestWithLifecycle(viewLifecycleOwner) { isSubscriptionEnabled ->
137                     if (!isSubscriptionEnabled) finish()
138                 }
139         }
140     }
141 
142     override fun getPreferenceScreenResId() = R.xml.data_usage_list
143 
144     override fun getLogTag() = TAG
145 
146     private fun processArgument() {
147         arguments?.let {
148             subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID)
149             template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java)
150         }
151         if (template == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
152             subId = intent.getIntExtra(
153                 Settings.EXTRA_SUB_ID,
154                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
155             )
156             template = intent.getParcelableExtra(
157                 Settings.EXTRA_NETWORK_TEMPLATE,
158                 NetworkTemplate::class.java,
159             ) ?: DataUsageUtils.getMobileNetworkTemplateFromSubId(context, intent).getOrNull()
160         }
161     }
162 
163     /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
164     private fun updatePolicy(isModifiable: Boolean) {
165         dataUsageListHeaderController?.setConfigButtonVisible(isModifiable)
166         chartDataUsagePreferenceController?.setBillingCycleModifiable(isModifiable)
167     }
168 
169     /**
170      * Updates the chart and detail data when initial loaded or selected cycle changed.
171      */
172     private fun updateSelectedCycle(usageData: NetworkUsageData) {
173         Log.d(TAG, "showing cycle $usageData")
174 
175         usageAmount?.title = usageData.getDataUsedString(requireContext())
176         viewModel.selectedCycleFlow.value = usageData
177 
178         updateApps(usageData)
179     }
180 
181     /** Updates applications data usage. */
182     private fun updateApps(usageData: NetworkUsageData) {
183         dataUsageListAppsController?.update(
184             subId = subId,
185             startTime = usageData.startTime,
186             endTime = usageData.endTime,
187         )
188     }
189 
190     companion object {
191         const val EXTRA_SUB_ID = "sub_id"
192         const val EXTRA_NETWORK_TEMPLATE = "network_template"
193 
194         private const val TAG = "DataUsageList"
195         private const val KEY_USAGE_AMOUNT = "usage_amount"
196 
197         @VisibleForTesting
198         const val KEY_WARNING = "warning"
199     }
200 }
201