Create DataUsageListAppsController
Move apps group logic from DataUsageList. Also add key to AppDataUsagePreference, which reduce flaky and keep scroll position when back from app detail page. Bug: 290856342 Test: manual - on DataUsageList Test: unit test Change-Id: I61e2b6bd9b192b7230e3553dbc6038f5d59bd303
This commit is contained in:
@@ -14,7 +14,8 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="usage_amount"
|
||||
@@ -32,6 +33,7 @@
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="apps_group"
|
||||
android:layout="@layout/preference_category_no_label" />
|
||||
android:layout="@layout/preference_category_no_label"
|
||||
settings:controller="com.android.settings.datausage.DataUsageListAppsController" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@@ -38,6 +38,7 @@ public class AppDataUsagePreference extends AppPreference {
|
||||
public AppDataUsagePreference(Context context, AppItem item, int percent,
|
||||
UidDetailProvider provider) {
|
||||
super(context);
|
||||
setKey("app_data_usage_" + item.key);
|
||||
mItem = item;
|
||||
mPercent = percent;
|
||||
|
||||
|
@@ -15,9 +15,7 @@
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.app.usage.NetworkStats;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
@@ -46,25 +44,19 @@ import androidx.lifecycle.Lifecycle;
|
||||
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository;
|
||||
import com.android.settings.network.MobileDataEnabledListener;
|
||||
import com.android.settings.network.MobileNetworkRepository;
|
||||
import com.android.settings.network.ProxySubscriptionManager;
|
||||
import com.android.settings.widget.LoadingViewController;
|
||||
import com.android.settingslib.AppItem;
|
||||
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
|
||||
import com.android.settingslib.net.NetworkCycleChartData;
|
||||
import com.android.settingslib.net.NetworkCycleChartDataLoader;
|
||||
import com.android.settingslib.net.NetworkStatsSummaryLoader;
|
||||
import com.android.settingslib.net.UidDetailProvider;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -85,14 +77,11 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
|
||||
private static final String KEY_USAGE_AMOUNT = "usage_amount";
|
||||
private static final String KEY_CHART_DATA = "chart_data";
|
||||
private static final String KEY_APPS_GROUP = "apps_group";
|
||||
private static final String KEY_TEMPLATE = "template";
|
||||
private static final String KEY_APP = "app";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int LOADER_CHART_DATA = 2;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_SUMMARY = 3;
|
||||
|
||||
@VisibleForTesting
|
||||
MobileDataEnabledListener mDataStateListener;
|
||||
@@ -113,18 +102,15 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
@Nullable
|
||||
private List<NetworkCycleChartData> mCycleData;
|
||||
|
||||
// Caches the cycles for startAppDataUsage usage, which need be cleared when resumed.
|
||||
private ArrayList<Long> mCycles;
|
||||
// Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
|
||||
// which need be cleared when resumed.
|
||||
private CycleAdapter.CycleItem mLastDisplayedCycle;
|
||||
private UidDetailProvider mUidDetailProvider;
|
||||
private CycleAdapter mCycleAdapter;
|
||||
private Preference mUsageAmount;
|
||||
private PreferenceGroup mApps;
|
||||
private View mHeader;
|
||||
private MobileNetworkRepository mMobileNetworkRepository;
|
||||
private SubscriptionInfoEntity mSubscriptionInfoEntity;
|
||||
private DataUsageListAppsController mDataUsageListAppsController;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -148,14 +134,19 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
return;
|
||||
}
|
||||
|
||||
mUidDetailProvider = new UidDetailProvider(activity);
|
||||
mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
|
||||
mChart = findPreference(KEY_CHART_DATA);
|
||||
mApps = findPreference(KEY_APPS_GROUP);
|
||||
|
||||
processArgument();
|
||||
if (mTemplate == null) {
|
||||
Log.e(TAG, "No template; leaving");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
updateSubscriptionInfoEntity();
|
||||
mDataStateListener = new MobileDataEnabledListener(activity, this);
|
||||
mDataUsageListAppsController = use(DataUsageListAppsController.class);
|
||||
mDataUsageListAppsController.init(mTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -216,7 +207,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
super.onResume();
|
||||
mLoadingViewController.showLoadingViewDelayed();
|
||||
mDataStateListener.start(mSubId);
|
||||
mCycles = null;
|
||||
mLastDisplayedCycle = null;
|
||||
|
||||
// kick off loader for network history
|
||||
@@ -234,16 +224,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
mDataStateListener.stop();
|
||||
|
||||
getLoaderManager().destroyLoader(LOADER_CHART_DATA);
|
||||
getLoaderManager().destroyLoader(LOADER_SUMMARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mUidDetailProvider != null) {
|
||||
mUidDetailProvider.clearCache();
|
||||
mUidDetailProvider = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,6 +332,7 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
if (mCycleData != null) {
|
||||
mCycleAdapter.updateCycleList(mCycleData);
|
||||
}
|
||||
mDataUsageListAppsController.setCycleData(mCycleData);
|
||||
updateSelectedCycle();
|
||||
}
|
||||
|
||||
@@ -402,8 +383,11 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
if (LOGD) Log.d(TAG, "updateDetailData()");
|
||||
|
||||
// kick off loader for detailed stats
|
||||
getLoaderManager().restartLoader(LOADER_SUMMARY, null /* args */,
|
||||
mNetworkStatsDetailCallbacks);
|
||||
mDataUsageListAppsController.update(
|
||||
mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
|
||||
mChart.getInspectStart(),
|
||||
mChart.getInspectEnd()
|
||||
);
|
||||
|
||||
final long totalBytes = mCycleData != null && !mCycleData.isEmpty()
|
||||
? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
|
||||
@@ -411,58 +395,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the given buckets.
|
||||
*/
|
||||
private void bindStats(List<AppDataUsageRepository.Bucket> buckets) {
|
||||
mApps.removeAll();
|
||||
AppDataUsageRepository repository = new AppDataUsageRepository(
|
||||
requireContext(),
|
||||
ActivityManager.getCurrentUser(),
|
||||
mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
|
||||
appItem -> mUidDetailProvider.getUidDetail(appItem.key, true).packageName
|
||||
);
|
||||
for (var itemPercentPair : repository.getAppPercent(buckets)) {
|
||||
final AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
|
||||
itemPercentPair.getFirst(), itemPercentPair.getSecond(), mUidDetailProvider);
|
||||
preference.setOnPreferenceClickListener(p -> {
|
||||
AppDataUsagePreference pref = (AppDataUsagePreference) p;
|
||||
startAppDataUsage(pref.getItem());
|
||||
return true;
|
||||
});
|
||||
mApps.addPreference(preference);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void startAppDataUsage(AppItem item) {
|
||||
if (mCycleData == null) {
|
||||
return;
|
||||
}
|
||||
final Bundle args = new Bundle();
|
||||
args.putParcelable(AppDataUsage.ARG_APP_ITEM, item);
|
||||
args.putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, mTemplate);
|
||||
if (mCycles == null) {
|
||||
mCycles = new ArrayList<>();
|
||||
for (NetworkCycleChartData data : mCycleData) {
|
||||
if (mCycles.isEmpty()) {
|
||||
mCycles.add(data.getEndTime());
|
||||
}
|
||||
mCycles.add(data.getStartTime());
|
||||
}
|
||||
}
|
||||
args.putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, mCycles);
|
||||
args.putLong(AppDataUsage.ARG_SELECTED_CYCLE,
|
||||
mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getEndTime());
|
||||
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(AppDataUsage.class.getName())
|
||||
.setTitleRes(R.string.data_usage_app_summary_title)
|
||||
.setArguments(args)
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.launch();
|
||||
}
|
||||
|
||||
private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
@@ -502,44 +434,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
}
|
||||
};
|
||||
|
||||
private final LoaderCallbacks<NetworkStats> mNetworkStatsDetailCallbacks =
|
||||
new LoaderCallbacks<>() {
|
||||
@Override
|
||||
@NonNull
|
||||
public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
|
||||
return new NetworkStatsSummaryLoader.Builder(getContext())
|
||||
.setStartTime(mChart.getInspectStart())
|
||||
.setEndTime(mChart.getInspectEnd())
|
||||
.setNetworkTemplate(mTemplate)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(
|
||||
@NonNull Loader<NetworkStats> loader, NetworkStats data) {
|
||||
bindStats(AppDataUsageRepository.Companion.convertToBuckets(data));
|
||||
updateEmptyVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<NetworkStats> loader) {
|
||||
mApps.removeAll();
|
||||
updateEmptyVisible();
|
||||
}
|
||||
|
||||
private void updateEmptyVisible() {
|
||||
if ((mApps.getPreferenceCount() != 0)
|
||||
!= (getPreferenceScreen().getPreferenceCount() != 0)) {
|
||||
if (mApps.getPreferenceCount() != 0) {
|
||||
getPreferenceScreen().addPreference(mUsageAmount);
|
||||
getPreferenceScreen().addPreference(mApps);
|
||||
} else {
|
||||
getPreferenceScreen().removeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean isGuestUser(Context context) {
|
||||
if (context == null) return false;
|
||||
final UserManager userManager = context.getSystemService(UserManager.class);
|
||||
|
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.net.NetworkTemplate
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.BasePreferenceController
|
||||
import com.android.settings.core.SubSettingLauncher
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository
|
||||
import com.android.settingslib.AppItem
|
||||
import com.android.settingslib.net.NetworkCycleChartData
|
||||
import com.android.settingslib.net.UidDetailProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class DataUsageListAppsController(context: Context, preferenceKey: String) :
|
||||
BasePreferenceController(context, preferenceKey) {
|
||||
|
||||
private val uidDetailProvider = UidDetailProvider(context)
|
||||
private lateinit var template: NetworkTemplate
|
||||
private lateinit var repository: AppDataUsageRepository
|
||||
private lateinit var preference: PreferenceGroup
|
||||
private lateinit var lifecycleScope: LifecycleCoroutineScope
|
||||
|
||||
private var cycleData: List<NetworkCycleChartData>? = null
|
||||
|
||||
fun init(template: NetworkTemplate) {
|
||||
this.template = template
|
||||
repository = AppDataUsageRepository(
|
||||
context = mContext,
|
||||
currentUserId = ActivityManager.getCurrentUser(),
|
||||
template = template,
|
||||
) { appItem: AppItem -> uidDetailProvider.getUidDetail(appItem.key, true).packageName }
|
||||
}
|
||||
|
||||
override fun getAvailabilityStatus() = AVAILABLE
|
||||
|
||||
override fun displayPreference(screen: PreferenceScreen) {
|
||||
super.displayPreference(screen)
|
||||
preference = screen.findPreference(preferenceKey)!!
|
||||
}
|
||||
|
||||
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||
lifecycleScope = viewLifecycleOwner.lifecycleScope
|
||||
}
|
||||
|
||||
fun setCycleData(cycleData: List<NetworkCycleChartData>?) {
|
||||
this.cycleData = cycleData
|
||||
}
|
||||
|
||||
fun update(carrierId: Int?, startTime: Long, endTime: Long) = lifecycleScope.launch {
|
||||
val apps = withContext(Dispatchers.Default) {
|
||||
repository.getAppPercent(carrierId, startTime, endTime).map { (appItem, percent) ->
|
||||
AppDataUsagePreference(mContext, appItem, percent, uidDetailProvider).apply {
|
||||
setOnPreferenceClickListener {
|
||||
startAppDataUsage(appItem, endTime)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preference.removeAll()
|
||||
for (app in apps) {
|
||||
preference.addPreference(app)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun startAppDataUsage(item: AppItem, endTime: Long) {
|
||||
val cycleData = cycleData ?: return
|
||||
val args = Bundle().apply {
|
||||
putParcelable(AppDataUsage.ARG_APP_ITEM, item)
|
||||
putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, template)
|
||||
val cycles = ArrayList<Long>().apply {
|
||||
for (data in cycleData) {
|
||||
if (isEmpty()) add(data.endTime)
|
||||
add(data.startTime)
|
||||
}
|
||||
}
|
||||
putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, cycles)
|
||||
putLong(AppDataUsage.ARG_SELECTED_CYCLE, endTime)
|
||||
}
|
||||
SubSettingLauncher(mContext).apply {
|
||||
setDestination(AppDataUsage::class.java.name)
|
||||
setTitleRes(R.string.data_usage_app_summary_title)
|
||||
setArguments(args)
|
||||
setSourceMetricsCategory(metricsCategory)
|
||||
}.launch()
|
||||
}
|
||||
}
|
@@ -17,11 +17,15 @@
|
||||
package com.android.settings.datausage.lib
|
||||
|
||||
import android.app.usage.NetworkStats
|
||||
import android.app.usage.NetworkStatsManager
|
||||
import android.content.Context
|
||||
import android.net.NetworkPolicyManager
|
||||
import android.net.NetworkTemplate
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.AppItem
|
||||
import com.android.settingslib.net.UidDetailProvider
|
||||
@@ -30,15 +34,18 @@ import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
class AppDataUsageRepository(
|
||||
private val context: Context,
|
||||
private val currentUserId: Int,
|
||||
private val carrierId: Int?,
|
||||
private val getPackageName: (AppItem) -> String,
|
||||
private val template: NetworkTemplate,
|
||||
private val getPackageName: (AppItem) -> String?,
|
||||
) {
|
||||
data class Bucket(
|
||||
val uid: Int,
|
||||
val bytes: Long,
|
||||
)
|
||||
private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
|
||||
|
||||
fun getAppPercent(buckets: List<Bucket>): List<Pair<AppItem, Int>> {
|
||||
fun getAppPercent(carrierId: Int?, startTime: Long, endTime: Long): List<Pair<AppItem, Int>> {
|
||||
val networkStats = querySummary(startTime, endTime) ?: return emptyList()
|
||||
return getAppPercent(carrierId, convertToBuckets(networkStats))
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun getAppPercent(carrierId: Int?, buckets: List<Bucket>): List<Pair<AppItem, Int>> {
|
||||
val items = ArrayList<AppItem>()
|
||||
val knownItems = SparseArray<AppItem>()
|
||||
val profiles = context.userManager.userProfiles
|
||||
@@ -61,7 +68,7 @@ class AppDataUsageRepository(
|
||||
item.restricted = true
|
||||
}
|
||||
|
||||
val filteredItems = filterItems(items).sorted()
|
||||
val filteredItems = filterItems(carrierId, items).sorted()
|
||||
val largest: Long = filteredItems.maxOfOrNull { it.total } ?: 0
|
||||
return filteredItems.map { item ->
|
||||
val percentTotal = if (largest > 0) (item.total * 100 / largest).toInt() else 0
|
||||
@@ -69,7 +76,14 @@ class AppDataUsageRepository(
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterItems(items: List<AppItem>): List<AppItem> {
|
||||
private fun querySummary(startTime: Long, endTime: Long): NetworkStats? = try {
|
||||
networkStatsManager.querySummary(template, startTime, endTime)
|
||||
} catch (e: RuntimeException) {
|
||||
Log.e(TAG, "Exception querying network detail.", e)
|
||||
null
|
||||
}
|
||||
|
||||
private fun filterItems(carrierId: Int?, items: List<AppItem>): List<AppItem> {
|
||||
// When there is no specified SubscriptionInfo, Wi-Fi data usage will be displayed.
|
||||
// In this case, the carrier service package also needs to be hidden.
|
||||
if (carrierId != null && carrierId !in context.resources.getIntArray(
|
||||
@@ -178,7 +192,15 @@ class AppDataUsageRepository(
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun convertToBuckets(stats: NetworkStats): List<Bucket> {
|
||||
private const val TAG = "AppDataUsageRepository"
|
||||
|
||||
@VisibleForTesting
|
||||
data class Bucket(
|
||||
val uid: Int,
|
||||
val bytes: Long,
|
||||
)
|
||||
|
||||
private fun convertToBuckets(stats: NetworkStats): List<Bucket> {
|
||||
val buckets = mutableListOf<Bucket>()
|
||||
stats.use {
|
||||
val bucket = NetworkStats.Bucket()
|
||||
|
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -44,19 +43,15 @@ import androidx.loader.app.LoaderManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.network.MobileDataEnabledListener;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.widget.LoadingViewController;
|
||||
import com.android.settingslib.AppItem;
|
||||
import com.android.settingslib.NetworkPolicyEditor;
|
||||
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
|
||||
import com.android.settingslib.net.NetworkCycleChartData;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
@@ -67,9 +62,6 @@ import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class DataUsageListTest {
|
||||
|
||||
@@ -195,34 +187,6 @@ public class DataUsageListTest {
|
||||
assertThat(mDataUsageList.mSubId).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
|
||||
final long startTime = 1521583200000L;
|
||||
final long endTime = 1521676800000L;
|
||||
final List<NetworkCycleChartData> data = new ArrayList<>();
|
||||
final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
|
||||
builder.setStartTime(startTime)
|
||||
.setEndTime(endTime);
|
||||
data.add(builder.build());
|
||||
ReflectionHelpers.setField(mDataUsageList, "mCycleData", data);
|
||||
final Spinner spinner = mock(Spinner.class);
|
||||
when(spinner.getSelectedItemPosition()).thenReturn(0);
|
||||
ReflectionHelpers.setField(mDataUsageList, "mCycleSpinner", spinner);
|
||||
final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
mDataUsageList.startAppDataUsage(new AppItem());
|
||||
|
||||
verify(mActivity).startActivity(intent.capture());
|
||||
final Bundle arguments =
|
||||
intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
|
||||
assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(endTime);
|
||||
final ArrayList<Long> cycles =
|
||||
(ArrayList) arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES);
|
||||
assertThat(cycles).hasSize(2);
|
||||
assertThat(cycles.get(0)).isEqualTo(endTime);
|
||||
assertThat(cycles.get(1)).isEqualTo(startTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onViewCreated_shouldHideCycleSpinner() {
|
||||
final View view = new View(mActivity);
|
||||
@@ -255,7 +219,6 @@ public class DataUsageListTest {
|
||||
mDataUsageList.onPause();
|
||||
|
||||
verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_CHART_DATA);
|
||||
verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_SUMMARY);
|
||||
}
|
||||
|
||||
private View getHeader() {
|
||||
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.NetworkTemplate
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.SettingsActivity
|
||||
import com.android.settingslib.AppItem
|
||||
import com.android.settingslib.net.NetworkCycleChartData
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.doNothing
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DataUsageListAppsControllerTest {
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
doNothing().whenever(mock).startActivity(any())
|
||||
}
|
||||
|
||||
private val controller = DataUsageListAppsController(context, "test_key")
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
controller.init(mock<NetworkTemplate>())
|
||||
val data = NetworkCycleChartData.Builder().apply {
|
||||
setStartTime(START_TIME)
|
||||
setEndTime(END_TIME)
|
||||
}.build()
|
||||
controller.setCycleData(listOf(data))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
|
||||
controller.startAppDataUsage(AppItem(), END_TIME)
|
||||
|
||||
val intent = argumentCaptor<Intent> {
|
||||
verify(context).startActivity(capture())
|
||||
}.firstValue
|
||||
val arguments = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
|
||||
assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(END_TIME)
|
||||
assertThat(
|
||||
arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES, ArrayList::class.java)
|
||||
).containsExactly(END_TIME, START_TIME).inOrder()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val START_TIME = 1521583200000L
|
||||
const val END_TIME = 1521676800000L
|
||||
}
|
||||
}
|
@@ -20,12 +20,13 @@ import android.content.Context
|
||||
import android.content.pm.UserInfo
|
||||
import android.content.res.Resources
|
||||
import android.net.NetworkPolicyManager
|
||||
import android.net.NetworkTemplate
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository.Bucket
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.Bucket
|
||||
import com.android.settingslib.AppItem
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -72,15 +73,15 @@ class AppDataUsageRepositoryTest {
|
||||
val repository = AppDataUsageRepository(
|
||||
context = context,
|
||||
currentUserId = USER_ID,
|
||||
carrierId = null,
|
||||
getPackageName = { "" },
|
||||
template = Template,
|
||||
getPackageName = { null },
|
||||
)
|
||||
val buckets = listOf(
|
||||
Bucket(uid = APP_ID_1, bytes = 1),
|
||||
Bucket(uid = APP_ID_2, bytes = 2),
|
||||
)
|
||||
|
||||
val appPercentList = repository.getAppPercent(buckets)
|
||||
val appPercentList = repository.getAppPercent(null, buckets)
|
||||
|
||||
assertThat(appPercentList).hasSize(2)
|
||||
appPercentList[0].first.apply {
|
||||
@@ -102,15 +103,15 @@ class AppDataUsageRepositoryTest {
|
||||
val repository = AppDataUsageRepository(
|
||||
context = context,
|
||||
currentUserId = USER_ID,
|
||||
carrierId = HIDING_CARRIER_ID,
|
||||
getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else "" },
|
||||
template = Template,
|
||||
getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else null },
|
||||
)
|
||||
val buckets = listOf(
|
||||
Bucket(uid = APP_ID_1, bytes = 1),
|
||||
Bucket(uid = APP_ID_2, bytes = 2),
|
||||
)
|
||||
|
||||
val appPercentList = repository.getAppPercent(buckets)
|
||||
val appPercentList = repository.getAppPercent(HIDING_CARRIER_ID, buckets)
|
||||
|
||||
assertThat(appPercentList).hasSize(1)
|
||||
appPercentList[0].first.apply {
|
||||
@@ -127,5 +128,7 @@ class AppDataUsageRepositoryTest {
|
||||
const val APP_ID_2 = 110002
|
||||
const val HIDING_CARRIER_ID = 4
|
||||
const val HIDING_PACKAGE_NAME = "hiding.package.name"
|
||||
|
||||
val Template: NetworkTemplate = mock<NetworkTemplate>()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user