diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java index 32662a22347..baa1dafa469 100644 --- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.applications.appinfo; import android.content.Context; -import android.net.NetworkStats; import android.net.NetworkTemplate; import android.os.Bundle; import android.os.Process; @@ -34,8 +33,8 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.datausage.AppDataUsage; -import com.android.settings.datausage.DataUsageUtils; -import com.android.settings.network.SubscriptionUtil; +import com.android.settings.datausage.lib.NetworkTemplates; +import com.android.settings.spa.app.appinfo.AppDataUsagePreferenceKt; import com.android.settingslib.AppItem; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -46,6 +45,10 @@ import com.android.settingslib.net.NetworkCycleDataForUidLoader; import java.util.List; +/** + * @deprecated Will be removed, use {@link AppDataUsagePreferenceKt} instead. + */ +@Deprecated(forRemoval = true) public class AppDataUsagePreferenceController extends AppInfoPreferenceControllerBase implements LoaderManager.LoaderCallbacks>, LifecycleObserver, OnResume, OnPause { @@ -92,7 +95,7 @@ public class AppDataUsagePreferenceController extends AppInfoPreferenceControlle @Override public Loader> onCreateLoader(int id, Bundle args) { - final NetworkTemplate template = getTemplate(mContext); + final NetworkTemplate template = NetworkTemplates.INSTANCE.getDefaultTemplate(mContext); final int uid = mParent.getAppEntry().info.uid; final NetworkCycleDataForUidLoader.Builder builder = @@ -147,18 +150,6 @@ public class AppDataUsagePreferenceController extends AppInfoPreferenceControlle return mContext.getString(R.string.computing_size); } - private static NetworkTemplate getTemplate(Context context) { - if (SubscriptionUtil.isSimHardwareVisible(context) - && DataUsageUtils.hasReadyMobileRadio(context)) { - return new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE).setMeteredness( - NetworkStats.METERED_YES).build(); - } - if (DataUsageUtils.hasWifiRadio(context)) { - return new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(); - } - return new NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build(); - } - @VisibleForTesting boolean isBandwidthControlEnabled() { return Utils.isBandwidthControlEnabled(); diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java index f74a5eacc99..f2e0261e5f0 100644 --- a/src/com/android/settings/datausage/AppDataUsage.java +++ b/src/com/android/settings/datausage/AppDataUsage.java @@ -29,7 +29,6 @@ import android.net.NetworkTemplate; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; -import android.telephony.SubscriptionManager; import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.Log; @@ -44,6 +43,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.datausage.lib.AppDataUsageDetailsRepository; +import com.android.settings.datausage.lib.NetworkTemplates; import com.android.settings.datausage.lib.NetworkUsageDetailsData; import com.android.settings.network.SubscriptionUtil; import com.android.settings.widget.EntityHeaderController; @@ -118,15 +118,16 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC mSelectedCycle = (args != null) ? args.getLong(ARG_SELECTED_CYCLE) : 0L; if (mTemplate == null) { - mTemplate = DataUsageUtils.getDefaultTemplate(mContext, - SubscriptionManager.getDefaultDataSubscriptionId()); + mTemplate = NetworkTemplates.INSTANCE.getDefaultTemplate(mContext); } + final Activity activity = requireActivity(); + activity.setTitle(NetworkTemplates.getTitleResId(mTemplate)); if (mAppItem == null) { int uid = (args != null) ? args.getInt(AppInfoBase.ARG_PACKAGE_UID, -1) : getActivity().getIntent().getIntExtra(AppInfoBase.ARG_PACKAGE_UID, -1); if (uid == -1) { // TODO: Log error. - getActivity().finish(); + activity.finish(); } else { addUid(uid); mAppItem = new AppItem(uid); diff --git a/src/com/android/settings/datausage/AppDataUsageActivity.java b/src/com/android/settings/datausage/AppDataUsageActivity.java index 48bedceba28..9a5a2cb686d 100644 --- a/src/com/android/settings/datausage/AppDataUsageActivity.java +++ b/src/com/android/settings/datausage/AppDataUsageActivity.java @@ -17,9 +17,9 @@ package com.android.settings.datausage; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; +import android.provider.Settings; import android.util.Log; -import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settingslib.AppItem; @@ -61,14 +61,12 @@ public class AppDataUsageActivity extends SettingsActivity { args.putParcelable(AppDataUsage.ARG_APP_ITEM, appItem); intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); intent.putExtra(EXTRA_SHOW_FRAGMENT, AppDataUsage.class.getName()); - intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.data_usage_app_summary_title); super.onCreate(savedInstanceState); } @Override protected boolean isValidFragment(String fragmentName) { - return super.isValidFragment(fragmentName) - || AppDataUsage.class.getName().equals(fragmentName); + return AppDataUsage.class.getName().equals(fragmentName); } } diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java index c3ddb2eac2d..0c07c19123c 100644 --- a/src/com/android/settings/datausage/BillingCycleSettings.java +++ b/src/com/android/settings/datausage/BillingCycleSettings.java @@ -44,6 +44,7 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.datausage.lib.NetworkTemplates; import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.search.BaseSearchIndexProvider; @@ -128,8 +129,7 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements } if (mNetworkTemplate == null) { - mNetworkTemplate = DataUsageUtils.getDefaultTemplate(context, - DataUsageUtils.getDefaultSubscriptionId(context)); + mNetworkTemplate = NetworkTemplates.INSTANCE.getDefaultTemplate(context); } mBillingCycle = findPreference(KEY_BILLING_CYCLE); diff --git a/src/com/android/settings/datausage/DataUsageUtils.java b/src/com/android/settings/datausage/DataUsageUtils.java index 0c6f4c8c6ae..2bbf3e2a462 100644 --- a/src/com/android/settings/datausage/DataUsageUtils.java +++ b/src/com/android/settings/datausage/DataUsageUtils.java @@ -17,7 +17,6 @@ package com.android.settings.datausage; import static android.content.pm.PackageManager.FEATURE_ETHERNET; import static android.content.pm.PackageManager.FEATURE_USB_HOST; import static android.content.pm.PackageManager.FEATURE_WIFI; -import static android.telephony.TelephonyManager.SIM_STATE_READY; import android.app.usage.NetworkStats.Bucket; import android.app.usage.NetworkStatsManager; @@ -49,7 +48,6 @@ import java.util.Optional; public final class DataUsageUtils { static final boolean TEST_RADIOS = false; static final String TEST_RADIOS_PROP = "test.radios"; - private static final boolean LOGD = false; private static final String ETHERNET = "ethernet"; private static final String TAG = "DataUsageUtils"; @@ -106,44 +104,6 @@ public final class DataUsageUtils { return tele.isDataCapable(); } - /** - * Test if device has a mobile data radio with SIM in ready state. - */ - public static boolean hasReadyMobileRadio(Context context) { - if (DataUsageUtils.TEST_RADIOS) { - return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains("mobile"); - } - final List subInfoList = - ProxySubscriptionManager.getInstance(context) - .getActiveSubscriptionsInfo(); - // No activated Subscriptions - if (subInfoList == null) { - if (LOGD) { - Log.d(TAG, "hasReadyMobileRadio: subInfoList=null"); - } - return false; - } - final TelephonyManager tele = context.getSystemService(TelephonyManager.class); - // require both supported network and ready SIM - boolean isReady = true; - for (SubscriptionInfo subInfo : subInfoList) { - isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY; - if (LOGD) { - Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo); - } - } - - final boolean isDataCapable = tele.isDataCapable(); - final boolean retVal = isDataCapable && isReady; - if (LOGD) { - Log.d(TAG, "hasReadyMobileRadio:" - + " telephonManager.isDataCapable()=" - + isDataCapable - + " isReady=" + isReady); - } - return retVal; - } - /** * Whether device has a Wi-Fi data radio. */ diff --git a/src/com/android/settings/datausage/lib/NetworkTemplates.kt b/src/com/android/settings/datausage/lib/NetworkTemplates.kt new file mode 100644 index 00000000000..90200706ba1 --- /dev/null +++ b/src/com/android/settings/datausage/lib/NetworkTemplates.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 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.lib + +import android.content.Context +import android.net.NetworkTemplate +import android.telephony.SubscriptionManager +import androidx.annotation.StringRes +import com.android.settings.R +import com.android.settings.datausage.DataUsageUtils + +interface INetworkTemplates { + /** + * Returns the default network template based on the availability of mobile data, Wifi. Returns + * ethernet template if both mobile data and Wifi are not available. + */ + fun getDefaultTemplate(context: Context): NetworkTemplate +} + +object NetworkTemplates : INetworkTemplates { + @JvmStatic + @StringRes + fun NetworkTemplate.getTitleResId(): Int = + when (matchRule) { + NetworkTemplate.MATCH_MOBILE, + NetworkTemplate.MATCH_CARRIER -> R.string.cellular_data_usage + + NetworkTemplate.MATCH_WIFI -> R.string.wifi_data_usage + NetworkTemplate.MATCH_ETHERNET -> R.string.ethernet_data_usage + else -> R.string.data_usage_app_summary_title + } + + /** + * Returns the default network template based on the availability of mobile data, Wifi. Returns + * ethernet template if both mobile data and Wifi are not available. + */ + override fun getDefaultTemplate(context: Context): NetworkTemplate = + DataUsageUtils.getDefaultTemplate( + context, + SubscriptionManager.getDefaultDataSubscriptionId(), + ) +} diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt index 5210dc7ddc3..6c128dc2e95 100644 --- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt +++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt @@ -18,13 +18,13 @@ package com.android.settings.spa.app.appinfo import android.content.Context import android.content.pm.ApplicationInfo -import android.net.NetworkStats import android.net.NetworkTemplate import android.os.Process import android.text.format.DateUtils import android.text.format.Formatter import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -32,25 +32,41 @@ import com.android.settings.R import com.android.settings.Utils import com.android.settings.applications.appinfo.AppInfoDashboardFragment import com.android.settings.datausage.AppDataUsage -import com.android.settings.datausage.DataUsageUtils +import com.android.settings.datausage.lib.INetworkTemplates +import com.android.settings.datausage.lib.NetworkTemplates +import com.android.settings.datausage.lib.NetworkTemplates.getTitleResId import com.android.settingslib.net.NetworkCycleDataForUid import com.android.settingslib.net.NetworkCycleDataForUidLoader import com.android.settingslib.spa.framework.compose.toState import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spaprivileged.model.app.hasFlag +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext @Composable -fun AppDataUsagePreference(app: ApplicationInfo) { +fun AppDataUsagePreference( + app: ApplicationInfo, + networkTemplates: INetworkTemplates = NetworkTemplates, +) { val context = LocalContext.current - val presenter = remember { AppDataUsagePresenter(context, app) } + val coroutineScope = rememberCoroutineScope() + val presenter = remember { + AppDataUsagePresenter(context, app, coroutineScope, networkTemplates) + } if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return Preference(object : PreferenceModel { - override val title = stringResource(R.string.data_usage_app_summary_title) + override val title = stringResource( + presenter.titleResIdFlow.collectAsStateWithLifecycle( + initialValue = R.string.summary_placeholder, + ).value + ) override val summary = presenter.summaryFlow.collectAsStateWithLifecycle( initialValue = stringResource(R.string.computing_size), ) @@ -62,6 +78,8 @@ fun AppDataUsagePreference(app: ApplicationInfo) { private class AppDataUsagePresenter( private val context: Context, private val app: ApplicationInfo, + coroutineScope: CoroutineScope, + networkTemplates: INetworkTemplates, ) { val isAvailableFlow = flow { emit(isAvailable()) } @@ -71,10 +89,17 @@ private class AppDataUsagePresenter( fun isEnabled() = app.hasFlag(ApplicationInfo.FLAG_INSTALLED) - val summaryFlow = flow { emit(getSummary()) } + private val templateFlow = flow { + emit(withContext(Dispatchers.IO) { + networkTemplates.getDefaultTemplate(context) + }) + }.shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1) - private suspend fun getSummary() = withContext(Dispatchers.IO) { - val appUsageData = getAppUsageData() + val titleResIdFlow = templateFlow.map { it.getTitleResId() } + val summaryFlow = templateFlow.map { getSummary(it) } + + private suspend fun getSummary(template: NetworkTemplate) = withContext(Dispatchers.IO) { + val appUsageData = getAppUsageData(template) val totalBytes = appUsageData.sumOf { it.totalUsage } if (totalBytes == 0L) { context.getString(R.string.no_data_usage) @@ -88,15 +113,15 @@ private class AppDataUsagePresenter( } } - private suspend fun getAppUsageData(): List = + private suspend fun getAppUsageData(template: NetworkTemplate): List = withContext(Dispatchers.IO) { - createLoader().loadInBackground() ?: emptyList() + createLoader(template).loadInBackground() ?: emptyList() } - private fun createLoader(): NetworkCycleDataForUidLoader = + private fun createLoader(template: NetworkTemplate): NetworkCycleDataForUidLoader = NetworkCycleDataForUidLoader.builder(context).apply { setRetrieveDetail(false) - setNetworkTemplate(getTemplate()) + setNetworkTemplate(template) addUid(app.uid) if (Process.isApplicationUid(app.uid)) { // Also add in network usage for the app's SDK sandbox @@ -104,18 +129,6 @@ private class AppDataUsagePresenter( } }.build() - private fun getTemplate(): NetworkTemplate = when { - DataUsageUtils.hasReadyMobileRadio(context) -> { - NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE) - .setMeteredness(NetworkStats.METERED_YES) - .build() - } - DataUsageUtils.hasWifiRadio(context) -> { - NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build() - } - else -> NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build() - } - fun startActivity() { AppInfoDashboardFragment.startAppInfoFragment( AppDataUsage::class.java, diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt index 019c143ce18..48010e01405 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt @@ -38,6 +38,7 @@ import com.android.settings.R import com.android.settings.Utils import com.android.settings.applications.appinfo.AppInfoDashboardFragment import com.android.settings.datausage.AppDataUsage +import com.android.settings.datausage.lib.INetworkTemplates import com.android.settingslib.net.NetworkCycleDataForUid import com.android.settingslib.net.NetworkCycleDataForUidLoader import com.android.settingslib.spa.testutils.delay @@ -106,7 +107,7 @@ class AppDataUsagePreferenceTest { setContent(notInstalledApp) - composeTestRule.onNodeWithText(context.getString(R.string.data_usage_app_summary_title)) + composeTestRule.onNodeWithText(context.getString(R.string.cellular_data_usage)) .assertIsDisplayed() .assertIsNotEnabled() } @@ -115,7 +116,7 @@ class AppDataUsagePreferenceTest { fun whenAppInstalled_enabled() { setContent(APP) - composeTestRule.onNodeWithText(context.getString(R.string.data_usage_app_summary_title)) + composeTestRule.onNodeWithText(context.getString(R.string.cellular_data_usage)) .assertIsDisplayed() .assertIsEnabled() } @@ -169,14 +170,19 @@ class AppDataUsagePreferenceTest { private fun setContent(app: ApplicationInfo = APP) { composeTestRule.setContent { CompositionLocalProvider(LocalContext provides context) { - AppDataUsagePreference(app) + AppDataUsagePreference(app, TestNetworkTemplates) } } composeTestRule.delay() } + private object TestNetworkTemplates : INetworkTemplates { + override fun getDefaultTemplate(context: Context): NetworkTemplate = + NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE).build() + } + private companion object { - const val PACKAGE_NAME = "packageName" + const val PACKAGE_NAME = "package.name" const val UID = 123 val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME