Create AppDataUsageCycleController
To improve performance and better organization and testings. Fix: 240931350 Test: manual - on AppDataUsage Test: unit test Change-Id: I277133b55378a3445aceb826d771b14c0fc91e4a
This commit is contained in:
@@ -17,6 +17,7 @@ package com.android.settings.datausage;
|
||||
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
|
||||
|
||||
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUid;
|
||||
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUidList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -32,15 +33,9 @@ import android.telephony.SubscriptionManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.util.Range;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
@@ -48,17 +43,19 @@ 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.NetworkUsageDetailsData;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.AppItem;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
import com.android.settingslib.net.NetworkCycleDataForUid;
|
||||
import com.android.settingslib.net.NetworkCycleDataForUidLoader;
|
||||
import com.android.settingslib.net.UidDetail;
|
||||
import com.android.settingslib.net.UidDetailProvider;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -77,11 +74,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
private static final String KEY_FOREGROUND_USAGE = "foreground_usage";
|
||||
private static final String KEY_BACKGROUND_USAGE = "background_usage";
|
||||
private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
|
||||
private static final String KEY_CYCLE = "cycle";
|
||||
private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
|
||||
|
||||
private static final int LOADER_APP_USAGE_DATA = 2;
|
||||
|
||||
private PackageManager mPackageManager;
|
||||
private final ArraySet<String> mPackages = new ArraySet<>();
|
||||
private Preference mTotalUsage;
|
||||
@@ -94,14 +88,10 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
CharSequence mLabel;
|
||||
@VisibleForTesting
|
||||
String mPackageName;
|
||||
private CycleAdapter mCycleAdapter;
|
||||
|
||||
@Nullable
|
||||
private List<NetworkCycleDataForUid> mUsageData;
|
||||
@VisibleForTesting
|
||||
NetworkTemplate mTemplate;
|
||||
private AppItem mAppItem;
|
||||
private SpinnerPreference mCycle;
|
||||
private RestrictedSwitchPreference mUnrestrictedData;
|
||||
private DataSaverBackend mDataSaverBackend;
|
||||
private Context mContext;
|
||||
@@ -160,7 +150,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
|
||||
mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
|
||||
|
||||
initCycle();
|
||||
final List<Integer> uidList = getAppUidList(mAppItem.uids);
|
||||
initCycle(uidList);
|
||||
|
||||
final UidDetailProvider uidDetailProvider = getUidDetailProvider();
|
||||
|
||||
@@ -191,7 +182,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
}
|
||||
mDataSaverBackend = new DataSaverBackend(mContext);
|
||||
|
||||
use(AppDataUsageListController.class).init(mAppItem.uids);
|
||||
use(AppDataUsageListController.class).init(uidList);
|
||||
} else {
|
||||
final Context context = getActivity();
|
||||
final UidDetail uidDetail = uidDetailProvider.getUidDetail(mAppItem.key, true);
|
||||
@@ -207,11 +198,9 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// No animations will occur before:
|
||||
// - LOADER_APP_USAGE_DATA initially updates the cycle
|
||||
// - updatePrefs() initially updates the preference visibility
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// No animations will occur before bindData() initially updates the cycle.
|
||||
// This is mainly for the cycle spinner, because when the page is entered from the
|
||||
// AppInfoDashboardFragment, there is no way to know whether the cycle data is available
|
||||
// before finished the async loading.
|
||||
@@ -219,11 +208,14 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
// setBackPreferenceListAnimatorIfLoaded().
|
||||
mIsLoading = true;
|
||||
getListView().setItemAnimator(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mDataSaverBackend != null) {
|
||||
mDataSaverBackend.addListener(this);
|
||||
}
|
||||
LoaderManager.getInstance(this).restartLoader(LOADER_APP_USAGE_DATA, null /* args */,
|
||||
mUidDataCallbacks);
|
||||
updatePrefs();
|
||||
}
|
||||
|
||||
@@ -268,14 +260,16 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
return new UidDetailProvider(mContext);
|
||||
}
|
||||
|
||||
private void initCycle() {
|
||||
mCycle = findPreference(KEY_CYCLE);
|
||||
mCycleAdapter = new CycleAdapter(mContext, mCycle);
|
||||
@VisibleForTesting
|
||||
void initCycle(List<Integer> uidList) {
|
||||
var controller = use(AppDataUsageCycleController.class);
|
||||
var repository = new AppDataUsageDetailsRepository(mContext, mTemplate, mCycles, uidList);
|
||||
controller.init(repository, data -> {
|
||||
bindData(data);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
if (mCycles != null) {
|
||||
// If coming from a page like DataUsageList where already has a selected cycle, display
|
||||
// that before loading to reduce flicker.
|
||||
mCycleAdapter.setInitialCycleList(mCycles, mSelectedCycle);
|
||||
mCycle.setHasCycles(true);
|
||||
controller.setInitialCycles(mCycles, mSelectedCycle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,22 +320,13 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void bindData(int position) {
|
||||
final long backgroundBytes, foregroundBytes;
|
||||
if (mUsageData == null || position >= mUsageData.size()) {
|
||||
backgroundBytes = foregroundBytes = 0;
|
||||
mCycle.setHasCycles(false);
|
||||
} else {
|
||||
mCycle.setHasCycles(true);
|
||||
final NetworkCycleDataForUid data = mUsageData.get(position);
|
||||
backgroundBytes = data.getBackgroudUsage();
|
||||
foregroundBytes = data.getForegroudUsage();
|
||||
}
|
||||
final long totalBytes = backgroundBytes + foregroundBytes;
|
||||
|
||||
mTotalUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, totalBytes));
|
||||
mForegroundUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, foregroundBytes));
|
||||
mBackgroundUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, backgroundBytes));
|
||||
void bindData(@NonNull NetworkUsageDetailsData data) {
|
||||
mIsLoading = false;
|
||||
mTotalUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, data.getTotalUsage()));
|
||||
mForegroundUsage.setSummary(
|
||||
DataUsageUtils.formatDataUsage(mContext, data.getForegroundUsage()));
|
||||
mBackgroundUsage.setSummary(
|
||||
DataUsageUtils.formatDataUsage(mContext, data.getBackgroundUsage()));
|
||||
}
|
||||
|
||||
private boolean getAppRestrictBackground() {
|
||||
@@ -391,71 +376,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
return SettingsEnums.APP_DATA_USAGE;
|
||||
}
|
||||
|
||||
private final AdapterView.OnItemSelectedListener mCycleListener =
|
||||
new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
bindData(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
final LoaderManager.LoaderCallbacks<List<NetworkCycleDataForUid>> mUidDataCallbacks =
|
||||
new LoaderManager.LoaderCallbacks<>() {
|
||||
@Override
|
||||
@NonNull
|
||||
public Loader<List<NetworkCycleDataForUid>> onCreateLoader(int id, Bundle args) {
|
||||
final NetworkCycleDataForUidLoader.Builder<?> builder =
|
||||
NetworkCycleDataForUidLoader.builder(mContext);
|
||||
builder.setRetrieveDetail(true)
|
||||
.setNetworkTemplate(mTemplate);
|
||||
for (int i = 0; i < mAppItem.uids.size(); i++) {
|
||||
builder.addUid(mAppItem.uids.keyAt(i));
|
||||
}
|
||||
if (mCycles != null) {
|
||||
builder.setCycles(mCycles);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<List<NetworkCycleDataForUid>> loader,
|
||||
List<NetworkCycleDataForUid> data) {
|
||||
mUsageData = data;
|
||||
mCycle.setOnItemSelectedListener(mCycleListener);
|
||||
mCycleAdapter.updateCycleList(data.stream()
|
||||
.map(cycle -> new Range<>(cycle.getStartTime(), cycle.getEndTime()))
|
||||
.toList());
|
||||
if (mSelectedCycle > 0L) {
|
||||
final int numCycles = data.size();
|
||||
int position = 0;
|
||||
for (int i = 0; i < numCycles; i++) {
|
||||
final NetworkCycleDataForUid cycleData = data.get(i);
|
||||
if (cycleData.getEndTime() == mSelectedCycle) {
|
||||
position = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (position > 0) {
|
||||
mCycle.setSelection(position);
|
||||
}
|
||||
bindData(position);
|
||||
} else {
|
||||
bindData(0 /* position */);
|
||||
}
|
||||
mIsLoading = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<List<NetworkCycleDataForUid>> loader) {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onDataSaverChanged(boolean isDataSaving) {
|
||||
|
||||
|
||||
@@ -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.content.Context
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.core.BasePreferenceController
|
||||
import com.android.settings.datausage.lib.AppDataUsageDetailsRepository
|
||||
import com.android.settings.datausage.lib.IAppDataUsageDetailsRepository
|
||||
import com.android.settings.datausage.lib.NetworkUsageDetailsData
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AppDataUsageCycleController(context: Context, preferenceKey: String) :
|
||||
BasePreferenceController(context, preferenceKey) {
|
||||
|
||||
private lateinit var repository: IAppDataUsageDetailsRepository
|
||||
private var onUsageDataUpdated: (NetworkUsageDetailsData) -> Unit = {}
|
||||
private lateinit var preference: SpinnerPreference
|
||||
private var cycleAdapter: CycleAdapter? = null
|
||||
|
||||
private var initialCycles: List<Long> = emptyList()
|
||||
private var initialSelectedEndTime: Long = -1
|
||||
|
||||
private var usageDetailsDataList: List<NetworkUsageDetailsData> = emptyList()
|
||||
|
||||
fun init(
|
||||
repository: IAppDataUsageDetailsRepository,
|
||||
onUsageDataUpdated: (NetworkUsageDetailsData) -> Unit,
|
||||
) {
|
||||
this.repository = repository
|
||||
this.onUsageDataUpdated = onUsageDataUpdated
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial cycles.
|
||||
*
|
||||
* If coming from a page like DataUsageList where already has a selected cycle, display that
|
||||
* before loading to reduce flicker.
|
||||
*/
|
||||
fun setInitialCycles(initialCycles: List<Long>, initialSelectedEndTime: Long) {
|
||||
this.initialCycles = initialCycles
|
||||
this.initialSelectedEndTime = initialSelectedEndTime
|
||||
}
|
||||
|
||||
override fun getAvailabilityStatus() = AVAILABLE
|
||||
|
||||
override fun displayPreference(screen: PreferenceScreen) {
|
||||
super.displayPreference(screen)
|
||||
preference = screen.findPreference(preferenceKey)!!
|
||||
if (cycleAdapter == null) {
|
||||
cycleAdapter = CycleAdapter(mContext, preference).apply {
|
||||
if (initialCycles.isNotEmpty()) {
|
||||
setInitialCycleList(initialCycles, initialSelectedEndTime)
|
||||
preference.setHasCycles(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun update() {
|
||||
usageDetailsDataList = withContext(Dispatchers.Default) {
|
||||
repository.queryDetailsForCycles()
|
||||
}
|
||||
if (usageDetailsDataList.isEmpty()) {
|
||||
preference.setHasCycles(false)
|
||||
onUsageDataUpdated(NetworkUsageDetailsData.AllZero)
|
||||
return
|
||||
}
|
||||
|
||||
preference.setHasCycles(true)
|
||||
cycleAdapter?.updateCycleList(usageDetailsDataList.map { it.range })
|
||||
preference.setOnItemSelectedListener(cycleListener)
|
||||
}
|
||||
|
||||
private val cycleListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
usageDetailsDataList.getOrNull(position)?.let(onUsageDataUpdated)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.core.BasePreferenceController
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.getAppUid
|
||||
import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.getAppUidList
|
||||
import com.android.settings.datausage.lib.AppPreferenceRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -43,8 +44,8 @@ open class AppDataUsageListController @JvmOverloads constructor(
|
||||
private var uids: List<Int> = emptyList()
|
||||
private lateinit var preference: PreferenceGroup
|
||||
|
||||
fun init(uids: SparseBooleanArray) {
|
||||
this.uids = uids.keyIterator().asSequence().map { getAppUid(it) }.distinct().toList()
|
||||
fun init(uids: List<Int>) {
|
||||
this.uids = uids
|
||||
}
|
||||
|
||||
override fun getAvailabilityStatus() = AVAILABLE
|
||||
|
||||
@@ -94,7 +94,6 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne
|
||||
@Override
|
||||
public void onItemSelected(
|
||||
AdapterView<?> parent, View view, int position, long id) {
|
||||
if (mPosition == position) return;
|
||||
mPosition = position;
|
||||
mCurrentObject = mAdapter.getItem(position);
|
||||
if (mListener != null) {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.app.usage.NetworkStats
|
||||
import android.app.usage.NetworkStatsManager
|
||||
import android.content.Context
|
||||
import android.net.NetworkTemplate
|
||||
import android.util.Log
|
||||
import android.util.Range
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
interface IAppDataUsageDetailsRepository {
|
||||
suspend fun queryDetailsForCycles(): List<NetworkUsageDetailsData>
|
||||
}
|
||||
|
||||
class AppDataUsageDetailsRepository @JvmOverloads constructor(
|
||||
context: Context,
|
||||
private val template: NetworkTemplate,
|
||||
private val cycles: List<Long>?,
|
||||
private val uids: List<Int>,
|
||||
private val networkCycleDataRepository: INetworkCycleDataRepository =
|
||||
NetworkCycleDataRepository(context, template)
|
||||
) : IAppDataUsageDetailsRepository {
|
||||
private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
|
||||
|
||||
override suspend fun queryDetailsForCycles(): List<NetworkUsageDetailsData> = coroutineScope {
|
||||
getCycles().map {
|
||||
async {
|
||||
queryDetails(it)
|
||||
}
|
||||
}.awaitAll().filter { it.totalUsage > 0 }
|
||||
}
|
||||
|
||||
private fun getCycles(): List<Range<Long>> =
|
||||
cycles?.zipWithNext { endTime, startTime -> Range(startTime, endTime) }
|
||||
?: networkCycleDataRepository.getCycles()
|
||||
|
||||
private fun queryDetails(range: Range<Long>): NetworkUsageDetailsData {
|
||||
var totalUsage = 0L
|
||||
var foregroundUsage = 0L
|
||||
for (uid in uids) {
|
||||
val usage = getUsage(range, uid, NetworkStats.Bucket.STATE_ALL)
|
||||
if (usage > 0L) {
|
||||
totalUsage += usage
|
||||
foregroundUsage +=
|
||||
getUsage(range, uid, NetworkStats.Bucket.STATE_FOREGROUND)
|
||||
}
|
||||
}
|
||||
return NetworkUsageDetailsData(
|
||||
range = range,
|
||||
totalUsage = totalUsage,
|
||||
foregroundUsage = foregroundUsage,
|
||||
backgroundUsage = totalUsage - foregroundUsage,
|
||||
)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun getUsage(range: Range<Long>, uid: Int, state: Int): Long = try {
|
||||
networkStatsManager.queryDetailsForUidTagState(
|
||||
template, range.lower, range.upper, uid, NetworkStats.Bucket.TAG_NONE, state,
|
||||
).getTotalUsage()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception querying network detail.", e)
|
||||
0
|
||||
}
|
||||
|
||||
private fun NetworkStats.getTotalUsage(): Long = use {
|
||||
var bytes = 0L
|
||||
val bucket = NetworkStats.Bucket()
|
||||
while (getNextBucket(bucket)) {
|
||||
bytes += bucket.rxBytes + bucket.txBytes
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TAG = "AppDataUsageDetailsRepo"
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,9 @@ import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import android.util.SparseBooleanArray
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.util.keyIterator
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.AppItem
|
||||
import com.android.settingslib.net.UidDetailProvider
|
||||
@@ -195,6 +197,10 @@ class AppDataUsageRepository(
|
||||
val bytes: Long,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun getAppUidList(uids: SparseBooleanArray) =
|
||||
uids.keyIterator().asSequence().map { getAppUid(it) }.distinct().toList()
|
||||
|
||||
@JvmStatic
|
||||
fun getAppUid(uid: Int): Int {
|
||||
if (Process.isSdkSandboxUid(uid)) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import kotlinx.coroutines.coroutineScope
|
||||
|
||||
interface INetworkCycleDataRepository {
|
||||
suspend fun loadCycles(): List<NetworkUsageData>
|
||||
fun getCycles(): List<Range<Long>>
|
||||
fun getPolicy(): NetworkPolicy?
|
||||
suspend fun querySummary(startTime: Long, endTime: Long): NetworkCycleChartData?
|
||||
}
|
||||
@@ -48,7 +49,7 @@ class NetworkCycleDataRepository(
|
||||
override suspend fun loadCycles(): List<NetworkUsageData> =
|
||||
getCycles().queryUsage().filter { it.usage > 0 }
|
||||
|
||||
private fun getCycles(): List<Range<Long>> {
|
||||
override fun getCycles(): List<Range<Long>> {
|
||||
val policy = getPolicy() ?: return queryCyclesAsFourWeeks()
|
||||
return policy.cycleIterator().asSequence().map {
|
||||
Range(it.lower.toInstant().toEpochMilli(), it.upper.toInstant().toEpochMilli())
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.util.Range
|
||||
|
||||
/**
|
||||
* Details data structure representing usage data in a period.
|
||||
*/
|
||||
data class NetworkUsageDetailsData(
|
||||
val range: Range<Long>,
|
||||
val totalUsage: Long,
|
||||
val foregroundUsage: Long,
|
||||
val backgroundUsage: Long,
|
||||
) {
|
||||
companion object {
|
||||
val AllZero = NetworkUsageDetailsData(
|
||||
range = Range(0, 0),
|
||||
totalUsage = 0,
|
||||
foregroundUsage = 0,
|
||||
backgroundUsage = 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user