Loading res/xml/data_usage_list.xml +3 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,9 @@ android:title="@string/summary_placeholder"> <com.android.settings.datausage.ChartDataUsagePreference android:key="chart_data" /> android:key="chart_data" settings:controller="com.android.settings.datausage.ChartDataUsagePreferenceController" /> <Preference android:key="non_carrier_data_usage_warning" Loading src/com/android/settings/datausage/AppDataUsage.java +4 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ 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; Loading Loading @@ -472,7 +473,9 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC List<NetworkCycleDataForUid> data) { mUsageData = data; mCycle.setOnItemSelectedListener(mCycleListener); mCycleAdapter.updateCycleList(data); mCycleAdapter.updateCycleList(data.stream() .map(cycle -> new Range<>(cycle.getStartTime(), cycle.getEndTime())) .toList()); if (mSelectedCycle > 0L) { final int numCycles = data.size(); int position = 0; Loading src/com/android/settings/datausage/ChartDataUsagePreference.java +38 −32 Original line number Diff line number Diff line Loading @@ -26,15 +26,17 @@ import android.util.AttributeSet; import android.util.DataUnit; import android.util.SparseIntArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.datausage.lib.NetworkCycleChartData; import com.android.settings.datausage.lib.NetworkUsageData; import com.android.settings.widget.UsageView; import com.android.settingslib.net.NetworkCycleChartData; import com.android.settingslib.net.NetworkCycleData; import java.util.ArrayList; import java.util.Comparator; Loading @@ -51,8 +53,8 @@ public class ChartDataUsagePreference extends Preference { private final int mWarningColor; private final int mLimitColor; private Resources mResources; private NetworkPolicy mPolicy; private final Resources mResources; @Nullable private NetworkPolicy mPolicy; private long mStart; private long mEnd; private NetworkCycleChartData mNetworkCycleChartData; Loading @@ -67,18 +69,16 @@ public class ChartDataUsagePreference extends Preference { } @Override public void onBindViewHolder(PreferenceViewHolder holder) { public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); final UsageView chart = (UsageView) holder.findViewById(R.id.data_usage); if (mNetworkCycleChartData == null) { return; } final UsageView chart = holder.itemView.requireViewById(R.id.data_usage); final int top = getTop(); chart.clearPaths(); chart.configureGraph(toInt(mEnd - mStart), top); calcPoints(chart, mNetworkCycleChartData.getUsageBuckets()); setupContentDescription(chart, mNetworkCycleChartData.getUsageBuckets()); if (mNetworkCycleChartData != null) { calcPoints(chart, mNetworkCycleChartData.getDailyUsage()); setupContentDescription(chart, mNetworkCycleChartData.getDailyUsage()); } chart.setBottomLabels(new CharSequence[] { Utils.formatDateRange(getContext(), mStart, mStart), Utils.formatDateRange(getContext(), mEnd, mEnd), Loading @@ -88,23 +88,21 @@ public class ChartDataUsagePreference extends Preference { } public int getTop() { final long totalData = mNetworkCycleChartData.getTotalUsage(); final long totalData = mNetworkCycleChartData != null ? mNetworkCycleChartData.getTotal().getUsage() : 0; final long policyMax = mPolicy != null ? Math.max(mPolicy.limitBytes, mPolicy.warningBytes) : 0; return (int) (Math.max(totalData, policyMax) / RESOLUTION); } @VisibleForTesting void calcPoints(UsageView chart, List<NetworkCycleData> usageSummary) { if (usageSummary == null) { return; } void calcPoints(UsageView chart, @NonNull List<NetworkUsageData> usageSummary) { final SparseIntArray points = new SparseIntArray(); points.put(0, 0); final long now = System.currentTimeMillis(); long totalData = 0; for (NetworkCycleData data : usageSummary) { for (NetworkUsageData data : usageSummary) { final long startTime = data.getStartTime(); if (startTime > now) { break; Loading @@ -112,7 +110,7 @@ public class ChartDataUsagePreference extends Preference { final long endTime = data.getEndTime(); // increment by current bucket total totalData += data.getTotalUsage(); totalData += data.getUsage(); if (points.size() == 1) { points.put(toInt(startTime - mStart) - 1, -1); Loading @@ -125,7 +123,8 @@ public class ChartDataUsagePreference extends Preference { } } private void setupContentDescription(UsageView chart, List<NetworkCycleData> usageSummary) { private void setupContentDescription( UsageView chart, @NonNull List<NetworkUsageData> usageSummary) { final Context context = getContext(); final StringBuilder contentDescription = new StringBuilder(); final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH; Loading @@ -137,7 +136,7 @@ public class ChartDataUsagePreference extends Preference { .getString(R.string.data_usage_chart_brief_content_description, startDate, endDate); contentDescription.append(briefContentDescription); if (usageSummary == null || usageSummary.isEmpty()) { if (usageSummary.isEmpty()) { final String noDataContentDescription = mResources .getString(R.string.data_usage_chart_no_data_content_description); contentDescription.append(noDataContentDescription); Loading Loading @@ -170,17 +169,17 @@ public class ChartDataUsagePreference extends Preference { * Collect the date of the same percentage, e.g., Aug 2 to Aug 22: 0%; Aug 23: 2%. */ @VisibleForTesting List<DataUsageSummaryNode> getDensedStatsData(List<NetworkCycleData> usageSummary) { List<DataUsageSummaryNode> getDensedStatsData(@NonNull List<NetworkUsageData> usageSummary) { final List<DataUsageSummaryNode> dataUsageSummaryNodes = new ArrayList<>(); final long overallDataUsage = Math.max(1L, usageSummary.stream() .mapToLong(NetworkCycleData::getTotalUsage).sum()); .mapToLong(NetworkUsageData::getUsage).sum()); long cumulatedDataUsage = 0L; int cumulatedDataUsagePercentage = 0; // Collect List of DataUsageSummaryNode for data usage percentage information. for (NetworkCycleData data : usageSummary) { cumulatedDataUsage += data.getTotalUsage(); cumulatedDataUsagePercentage = (int) ((cumulatedDataUsage * 100) / overallDataUsage); for (NetworkUsageData data : usageSummary) { cumulatedDataUsage += data.getUsage(); int cumulatedDataUsagePercentage = (int) ((cumulatedDataUsage * 100) / overallDataUsage); final DataUsageSummaryNode node = new DataUsageSummaryNode(data.getStartTime(), data.getEndTime(), cumulatedDataUsagePercentage); Loading Loading @@ -268,8 +267,9 @@ public class ChartDataUsagePreference extends Preference { } if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { chart.setDividerLoc((int) (policy.warningBytes / RESOLUTION)); float weight = policy.warningBytes / RESOLUTION / (float) top; int dividerLoc = (int) (policy.warningBytes / RESOLUTION); chart.setDividerLoc(dividerLoc); float weight = dividerLoc / (float) top; float above = 1 - weight; chart.setSideLabelWeights(above, weight); middleVisibility = mWarningColor; Loading @@ -289,15 +289,21 @@ public class ChartDataUsagePreference extends Preference { return new SpannableStringBuilder().append(label, new ForegroundColorSpan(mLimitColor), 0); } public void setNetworkPolicy(NetworkPolicy policy) { /** Sets network policy. */ public void setNetworkPolicy(@Nullable NetworkPolicy policy) { mPolicy = policy; notifyChanged(); } /** Sets time. */ public void setTime(long start, long end) { mStart = start; mEnd = end; notifyChanged(); } public void setNetworkCycleData(NetworkCycleChartData data) { mNetworkCycleChartData = data; mStart = data.getStartTime(); mEnd = data.getEndTime(); notifyChanged(); } } src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * 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.net.NetworkTemplate import androidx.annotation.OpenForTesting import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceScreen import com.android.settings.core.BasePreferenceController import com.android.settings.datausage.lib.INetworkCycleDataRepository import com.android.settings.datausage.lib.NetworkCycleDataRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @OpenForTesting open class ChartDataUsagePreferenceController(context: Context, preferenceKey: String) : BasePreferenceController(context, preferenceKey) { private lateinit var repository: INetworkCycleDataRepository private lateinit var preference: ChartDataUsagePreference private lateinit var lifecycleScope: LifecycleCoroutineScope open fun init(template: NetworkTemplate) { this.repository = NetworkCycleDataRepository(mContext, template) } @VisibleForTesting fun init(repository: INetworkCycleDataRepository) { this.repository = repository } override fun getAvailabilityStatus() = AVAILABLE override fun displayPreference(screen: PreferenceScreen) { super.displayPreference(screen) preference = screen.findPreference(preferenceKey)!! } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { lifecycleScope = viewLifecycleOwner.lifecycleScope } /** * Sets whether billing cycle modifiable. * * Don't bind warning / limit sweeps if not modifiable. */ open fun setBillingCycleModifiable(isModifiable: Boolean) { preference.setNetworkPolicy( if (isModifiable) repository.getPolicy() else null ) } fun update(startTime: Long, endTime: Long) { preference.setTime(startTime, endTime) lifecycleScope.launch { val chartData = withContext(Dispatchers.Default) { repository.querySummary(startTime, endTime) } preference.setNetworkCycleData(chartData) } } } src/com/android/settings/datausage/CycleAdapter.java +4 −4 Original line number Diff line number Diff line Loading @@ -14,9 +14,9 @@ package com.android.settings.datausage; import android.content.Context; import android.util.Range; import com.android.settings.Utils; import com.android.settingslib.net.NetworkCycleData; import com.android.settingslib.widget.SettingsSpinnerAdapter; import java.util.List; Loading Loading @@ -62,15 +62,15 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem> * Rebuild list based on network data. Always selects the newest item, * updating the inspection range on chartData. */ public void updateCycleList(List<? extends NetworkCycleData> cycleData) { public void updateCycleList(List<Range<Long>> cycleData) { // stash away currently selected cycle to try restoring below final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem) mSpinner.getSelectedItem(); clear(); final Context context = getContext(); for (NetworkCycleData data : cycleData) { add(new CycleAdapter.CycleItem(context, data.getStartTime(), data.getEndTime())); for (Range<Long> cycle : cycleData) { add(new CycleAdapter.CycleItem(context, cycle.getLower(), cycle.getUpper())); } // force pick the current cycle (first item) Loading Loading
res/xml/data_usage_list.xml +3 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,9 @@ android:title="@string/summary_placeholder"> <com.android.settings.datausage.ChartDataUsagePreference android:key="chart_data" /> android:key="chart_data" settings:controller="com.android.settings.datausage.ChartDataUsagePreferenceController" /> <Preference android:key="non_carrier_data_usage_warning" Loading
src/com/android/settings/datausage/AppDataUsage.java +4 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ 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; Loading Loading @@ -472,7 +473,9 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC List<NetworkCycleDataForUid> data) { mUsageData = data; mCycle.setOnItemSelectedListener(mCycleListener); mCycleAdapter.updateCycleList(data); mCycleAdapter.updateCycleList(data.stream() .map(cycle -> new Range<>(cycle.getStartTime(), cycle.getEndTime())) .toList()); if (mSelectedCycle > 0L) { final int numCycles = data.size(); int position = 0; Loading
src/com/android/settings/datausage/ChartDataUsagePreference.java +38 −32 Original line number Diff line number Diff line Loading @@ -26,15 +26,17 @@ import android.util.AttributeSet; import android.util.DataUnit; import android.util.SparseIntArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.datausage.lib.NetworkCycleChartData; import com.android.settings.datausage.lib.NetworkUsageData; import com.android.settings.widget.UsageView; import com.android.settingslib.net.NetworkCycleChartData; import com.android.settingslib.net.NetworkCycleData; import java.util.ArrayList; import java.util.Comparator; Loading @@ -51,8 +53,8 @@ public class ChartDataUsagePreference extends Preference { private final int mWarningColor; private final int mLimitColor; private Resources mResources; private NetworkPolicy mPolicy; private final Resources mResources; @Nullable private NetworkPolicy mPolicy; private long mStart; private long mEnd; private NetworkCycleChartData mNetworkCycleChartData; Loading @@ -67,18 +69,16 @@ public class ChartDataUsagePreference extends Preference { } @Override public void onBindViewHolder(PreferenceViewHolder holder) { public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); final UsageView chart = (UsageView) holder.findViewById(R.id.data_usage); if (mNetworkCycleChartData == null) { return; } final UsageView chart = holder.itemView.requireViewById(R.id.data_usage); final int top = getTop(); chart.clearPaths(); chart.configureGraph(toInt(mEnd - mStart), top); calcPoints(chart, mNetworkCycleChartData.getUsageBuckets()); setupContentDescription(chart, mNetworkCycleChartData.getUsageBuckets()); if (mNetworkCycleChartData != null) { calcPoints(chart, mNetworkCycleChartData.getDailyUsage()); setupContentDescription(chart, mNetworkCycleChartData.getDailyUsage()); } chart.setBottomLabels(new CharSequence[] { Utils.formatDateRange(getContext(), mStart, mStart), Utils.formatDateRange(getContext(), mEnd, mEnd), Loading @@ -88,23 +88,21 @@ public class ChartDataUsagePreference extends Preference { } public int getTop() { final long totalData = mNetworkCycleChartData.getTotalUsage(); final long totalData = mNetworkCycleChartData != null ? mNetworkCycleChartData.getTotal().getUsage() : 0; final long policyMax = mPolicy != null ? Math.max(mPolicy.limitBytes, mPolicy.warningBytes) : 0; return (int) (Math.max(totalData, policyMax) / RESOLUTION); } @VisibleForTesting void calcPoints(UsageView chart, List<NetworkCycleData> usageSummary) { if (usageSummary == null) { return; } void calcPoints(UsageView chart, @NonNull List<NetworkUsageData> usageSummary) { final SparseIntArray points = new SparseIntArray(); points.put(0, 0); final long now = System.currentTimeMillis(); long totalData = 0; for (NetworkCycleData data : usageSummary) { for (NetworkUsageData data : usageSummary) { final long startTime = data.getStartTime(); if (startTime > now) { break; Loading @@ -112,7 +110,7 @@ public class ChartDataUsagePreference extends Preference { final long endTime = data.getEndTime(); // increment by current bucket total totalData += data.getTotalUsage(); totalData += data.getUsage(); if (points.size() == 1) { points.put(toInt(startTime - mStart) - 1, -1); Loading @@ -125,7 +123,8 @@ public class ChartDataUsagePreference extends Preference { } } private void setupContentDescription(UsageView chart, List<NetworkCycleData> usageSummary) { private void setupContentDescription( UsageView chart, @NonNull List<NetworkUsageData> usageSummary) { final Context context = getContext(); final StringBuilder contentDescription = new StringBuilder(); final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH; Loading @@ -137,7 +136,7 @@ public class ChartDataUsagePreference extends Preference { .getString(R.string.data_usage_chart_brief_content_description, startDate, endDate); contentDescription.append(briefContentDescription); if (usageSummary == null || usageSummary.isEmpty()) { if (usageSummary.isEmpty()) { final String noDataContentDescription = mResources .getString(R.string.data_usage_chart_no_data_content_description); contentDescription.append(noDataContentDescription); Loading Loading @@ -170,17 +169,17 @@ public class ChartDataUsagePreference extends Preference { * Collect the date of the same percentage, e.g., Aug 2 to Aug 22: 0%; Aug 23: 2%. */ @VisibleForTesting List<DataUsageSummaryNode> getDensedStatsData(List<NetworkCycleData> usageSummary) { List<DataUsageSummaryNode> getDensedStatsData(@NonNull List<NetworkUsageData> usageSummary) { final List<DataUsageSummaryNode> dataUsageSummaryNodes = new ArrayList<>(); final long overallDataUsage = Math.max(1L, usageSummary.stream() .mapToLong(NetworkCycleData::getTotalUsage).sum()); .mapToLong(NetworkUsageData::getUsage).sum()); long cumulatedDataUsage = 0L; int cumulatedDataUsagePercentage = 0; // Collect List of DataUsageSummaryNode for data usage percentage information. for (NetworkCycleData data : usageSummary) { cumulatedDataUsage += data.getTotalUsage(); cumulatedDataUsagePercentage = (int) ((cumulatedDataUsage * 100) / overallDataUsage); for (NetworkUsageData data : usageSummary) { cumulatedDataUsage += data.getUsage(); int cumulatedDataUsagePercentage = (int) ((cumulatedDataUsage * 100) / overallDataUsage); final DataUsageSummaryNode node = new DataUsageSummaryNode(data.getStartTime(), data.getEndTime(), cumulatedDataUsagePercentage); Loading Loading @@ -268,8 +267,9 @@ public class ChartDataUsagePreference extends Preference { } if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { chart.setDividerLoc((int) (policy.warningBytes / RESOLUTION)); float weight = policy.warningBytes / RESOLUTION / (float) top; int dividerLoc = (int) (policy.warningBytes / RESOLUTION); chart.setDividerLoc(dividerLoc); float weight = dividerLoc / (float) top; float above = 1 - weight; chart.setSideLabelWeights(above, weight); middleVisibility = mWarningColor; Loading @@ -289,15 +289,21 @@ public class ChartDataUsagePreference extends Preference { return new SpannableStringBuilder().append(label, new ForegroundColorSpan(mLimitColor), 0); } public void setNetworkPolicy(NetworkPolicy policy) { /** Sets network policy. */ public void setNetworkPolicy(@Nullable NetworkPolicy policy) { mPolicy = policy; notifyChanged(); } /** Sets time. */ public void setTime(long start, long end) { mStart = start; mEnd = end; notifyChanged(); } public void setNetworkCycleData(NetworkCycleChartData data) { mNetworkCycleChartData = data; mStart = data.getStartTime(); mEnd = data.getEndTime(); notifyChanged(); } }
src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * 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.net.NetworkTemplate import androidx.annotation.OpenForTesting import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceScreen import com.android.settings.core.BasePreferenceController import com.android.settings.datausage.lib.INetworkCycleDataRepository import com.android.settings.datausage.lib.NetworkCycleDataRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @OpenForTesting open class ChartDataUsagePreferenceController(context: Context, preferenceKey: String) : BasePreferenceController(context, preferenceKey) { private lateinit var repository: INetworkCycleDataRepository private lateinit var preference: ChartDataUsagePreference private lateinit var lifecycleScope: LifecycleCoroutineScope open fun init(template: NetworkTemplate) { this.repository = NetworkCycleDataRepository(mContext, template) } @VisibleForTesting fun init(repository: INetworkCycleDataRepository) { this.repository = repository } override fun getAvailabilityStatus() = AVAILABLE override fun displayPreference(screen: PreferenceScreen) { super.displayPreference(screen) preference = screen.findPreference(preferenceKey)!! } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { lifecycleScope = viewLifecycleOwner.lifecycleScope } /** * Sets whether billing cycle modifiable. * * Don't bind warning / limit sweeps if not modifiable. */ open fun setBillingCycleModifiable(isModifiable: Boolean) { preference.setNetworkPolicy( if (isModifiable) repository.getPolicy() else null ) } fun update(startTime: Long, endTime: Long) { preference.setTime(startTime, endTime) lifecycleScope.launch { val chartData = withContext(Dispatchers.Default) { repository.querySummary(startTime, endTime) } preference.setNetworkCycleData(chartData) } } }
src/com/android/settings/datausage/CycleAdapter.java +4 −4 Original line number Diff line number Diff line Loading @@ -14,9 +14,9 @@ package com.android.settings.datausage; import android.content.Context; import android.util.Range; import com.android.settings.Utils; import com.android.settingslib.net.NetworkCycleData; import com.android.settingslib.widget.SettingsSpinnerAdapter; import java.util.List; Loading Loading @@ -62,15 +62,15 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem> * Rebuild list based on network data. Always selects the newest item, * updating the inspection range on chartData. */ public void updateCycleList(List<? extends NetworkCycleData> cycleData) { public void updateCycleList(List<Range<Long>> cycleData) { // stash away currently selected cycle to try restoring below final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem) mSpinner.getSelectedItem(); clear(); final Context context = getContext(); for (NetworkCycleData data : cycleData) { add(new CycleAdapter.CycleItem(context, data.getStartTime(), data.getEndTime())); for (Range<Long> cycle : cycleData) { add(new CycleAdapter.CycleItem(context, cycle.getLower(), cycle.getUpper())); } // force pick the current cycle (first item) Loading