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.network.telephony 18 19 import android.content.Context 20 import android.content.Intent 21 import android.net.NetworkTemplate 22 import android.provider.Settings 23 import android.telephony.SubscriptionManager 24 import androidx.annotation.VisibleForTesting 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.LifecycleOwner 27 import androidx.lifecycle.lifecycleScope 28 import androidx.lifecycle.repeatOnLifecycle 29 import androidx.preference.Preference 30 import androidx.preference.PreferenceScreen 31 import com.android.settings.R 32 import com.android.settings.core.BasePreferenceController 33 import com.android.settings.datausage.DataUsageUtils 34 import com.android.settings.datausage.lib.DataUsageLib 35 import com.android.settings.datausage.lib.NetworkCycleDataRepository 36 import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange 37 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem 38 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult 39 import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder 40 import kotlinx.coroutines.Dispatchers 41 import kotlinx.coroutines.launch 42 import kotlinx.coroutines.withContext 43 44 /** Preference controller for "Data usage" */ 45 class DataUsagePreferenceController(context: Context, key: String) : 46 BasePreferenceController(context, key) { 47 48 private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID 49 private lateinit var preference: Preference 50 private var networkTemplate: NetworkTemplate? = null 51 52 fun init(subId: Int) { 53 this.subId = subId 54 } 55 56 override fun getAvailabilityStatus() = AVAILABLE 57 58 override fun displayPreference(screen: PreferenceScreen) { 59 super.displayPreference(screen) 60 preference = screen.findPreference(preferenceKey)!! 61 } 62 63 override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { 64 preference.summary = mContext.getPlaceholder() 65 viewLifecycleOwner.lifecycleScope.launch { 66 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 67 update() 68 } 69 } 70 } 71 72 override fun handlePreferenceTreeClick(preference: Preference): Boolean { 73 if (preference.key != preferenceKey || networkTemplate == null) return false 74 val intent = 75 Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply { 76 setPackage(mContext.packageName) 77 putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate) 78 putExtra(Settings.EXTRA_SUB_ID, subId) 79 } 80 mContext.startActivity(intent) 81 return true 82 } 83 84 private suspend fun update() { 85 val (summary, enabled) = withContext(Dispatchers.Default) { 86 networkTemplate = getNetworkTemplate() 87 getDataUsageSummaryAndEnabled() 88 } 89 preference.isEnabled = enabled 90 preference.summary = summary 91 } 92 93 private fun getNetworkTemplate(): NetworkTemplate? = 94 if (SubscriptionManager.isValidSubscriptionId(subId)) { 95 DataUsageLib.getMobileTemplate(mContext, subId) 96 } else null 97 98 @VisibleForTesting 99 fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? = 100 networkTemplate?.let { NetworkCycleDataRepository(mContext, it) } 101 102 private fun getDataUsageSummaryAndEnabled(): Pair<String?, Boolean> { 103 val repository = createNetworkCycleDataRepository() ?: return null to false 104 105 repository.loadFirstCycle()?.let { usageData -> 106 val formattedDataUsage = 107 mContext.getString( 108 R.string.data_usage_template, 109 usageData.formatUsage(mContext), 110 usageData.formatDateRange(mContext), 111 ) 112 val hasUsage = usageData.usage > 0 || repository.queryUsage(AllTimeRange).usage > 0 113 return formattedDataUsage to hasUsage 114 } 115 116 val allTimeUsage = repository.queryUsage(AllTimeRange) 117 return allTimeUsage.getDataUsedString(mContext) to (allTimeUsage.usage > 0) 118 } 119 120 companion object { 121 class DataUsageSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem { 122 override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { 123 if (!DataUsageUtils.hasMobileData(context)) return null 124 return MobileNetworkSettingsSearchResult( 125 key = "data_usage_summary", 126 title = context.getString(R.string.app_cellular_data_usage), 127 ) 128 } 129 } 130 } 131 } 132