New DataUsageListHeaderController
Move spinner and config button to DataUsageListHeaderController. Bug: 290856342 Test: manual - on DataUsageList and AppDataUsage Test: unit test Change-Id: I655bd3441b2305708c0052f1203538bb07eef2af
This commit is contained in:
@@ -310,7 +310,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
|
||||
private void initCycle() {
|
||||
mCycle = findPreference(KEY_CYCLE);
|
||||
mCycleAdapter = new CycleAdapter(mContext, mCycle, mCycleListener);
|
||||
mCycleAdapter = new CycleAdapter(mContext, mCycle);
|
||||
if (mCycles != null) {
|
||||
// If coming from a page like DataUsageList where already has a selected cycle, display
|
||||
// that before loading to reduce flicker.
|
||||
@@ -435,7 +435,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
return SettingsEnums.APP_DATA_USAGE;
|
||||
}
|
||||
|
||||
private AdapterView.OnItemSelectedListener mCycleListener =
|
||||
private final AdapterView.OnItemSelectedListener mCycleListener =
|
||||
new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
@@ -471,6 +471,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
|
||||
public void onLoadFinished(@NonNull Loader<List<NetworkCycleDataForUid>> loader,
|
||||
List<NetworkCycleDataForUid> data) {
|
||||
mUsageData = data;
|
||||
mCycle.setOnItemSelectedListener(mCycleListener);
|
||||
mCycleAdapter.updateCycleList(data);
|
||||
if (mSelectedCycle > 0L) {
|
||||
final int numCycles = data.size();
|
||||
|
||||
@@ -294,14 +294,6 @@ public class ChartDataUsagePreference extends Preference {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
public long getInspectStart() {
|
||||
return mStart;
|
||||
}
|
||||
|
||||
public long getInspectEnd() {
|
||||
return mEnd;
|
||||
}
|
||||
|
||||
public void setNetworkCycleData(NetworkCycleChartData data) {
|
||||
mNetworkCycleChartData = data;
|
||||
mStart = data.getStartTime();
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.net.NetworkCycleData;
|
||||
@@ -25,13 +24,10 @@ import java.util.List;
|
||||
public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem> {
|
||||
|
||||
private final SpinnerInterface mSpinner;
|
||||
private final AdapterView.OnItemSelectedListener mListener;
|
||||
|
||||
public CycleAdapter(Context context, SpinnerInterface spinner,
|
||||
AdapterView.OnItemSelectedListener listener) {
|
||||
public CycleAdapter(Context context, SpinnerInterface spinner) {
|
||||
super(context);
|
||||
mSpinner = spinner;
|
||||
mListener = listener;
|
||||
mSpinner.setAdapter(this);
|
||||
}
|
||||
|
||||
@@ -67,7 +63,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
|
||||
* updating the inspection range on chartData.
|
||||
*/
|
||||
public void updateCycleList(List<? extends NetworkCycleData> cycleData) {
|
||||
mSpinner.setOnItemSelectedListener(mListener);
|
||||
// stash away currently selected cycle to try restoring below
|
||||
final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
|
||||
mSpinner.getSelectedItem();
|
||||
@@ -122,8 +117,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
|
||||
public interface SpinnerInterface {
|
||||
void setAdapter(CycleAdapter cycleAdapter);
|
||||
|
||||
void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener);
|
||||
|
||||
Object getSelectedItem();
|
||||
|
||||
void setSelection(int position);
|
||||
|
||||
@@ -28,11 +28,6 @@ import android.telephony.SubscriptionManager;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.AccessibilityDelegate;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -43,8 +38,6 @@ import androidx.loader.content.Loader;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
|
||||
import com.android.settings.datausage.lib.BillingCycleRepository;
|
||||
import com.android.settings.network.MobileDataEnabledListener;
|
||||
import com.android.settings.network.MobileNetworkRepository;
|
||||
@@ -58,6 +51,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* Panel showing data usage history across various networks, including options
|
||||
* to inspect based on usage cycle and control through {@link NetworkPolicy}.
|
||||
@@ -90,8 +85,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
@VisibleForTesting
|
||||
int mNetworkType;
|
||||
@VisibleForTesting
|
||||
Spinner mCycleSpinner;
|
||||
@VisibleForTesting
|
||||
LoadingViewController mLoadingViewController;
|
||||
|
||||
private ChartDataUsagePreference mChart;
|
||||
@@ -102,13 +95,15 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
// 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 CycleAdapter mCycleAdapter;
|
||||
private Preference mUsageAmount;
|
||||
private View mHeader;
|
||||
private MobileNetworkRepository mMobileNetworkRepository;
|
||||
private SubscriptionInfoEntity mSubscriptionInfoEntity;
|
||||
private DataUsageListAppsController mDataUsageListAppsController;
|
||||
private BillingCycleRepository mBillingCycleRepository;
|
||||
@VisibleForTesting
|
||||
DataUsageListHeaderController mDataUsageListHeaderController;
|
||||
|
||||
private boolean mIsBillingCycleModifiable = false;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -158,50 +153,15 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
public void onViewCreated(@NonNull View v, Bundle savedInstanceState) {
|
||||
super.onViewCreated(v, savedInstanceState);
|
||||
|
||||
mHeader = setPinnedHeaderView(R.layout.apps_filter_spinner);
|
||||
mHeader.findViewById(R.id.filter_settings).setOnClickListener(btn -> {
|
||||
final Bundle args = new Bundle();
|
||||
args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(BillingCycleSettings.class.getName())
|
||||
.setTitleRes(R.string.billing_cycle)
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.setArguments(args)
|
||||
.launch();
|
||||
});
|
||||
mCycleSpinner = mHeader.findViewById(R.id.filter_spinner);
|
||||
mCycleSpinner.setVisibility(View.GONE);
|
||||
mCycleAdapter = new CycleAdapter(mCycleSpinner.getContext(), new SpinnerInterface() {
|
||||
@Override
|
||||
public void setAdapter(CycleAdapter cycleAdapter) {
|
||||
mCycleSpinner.setAdapter(cycleAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
|
||||
mCycleSpinner.setOnItemSelectedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSelectedItem() {
|
||||
return mCycleSpinner.getSelectedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(int position) {
|
||||
mCycleSpinner.setSelection(position);
|
||||
}
|
||||
}, mCycleListener);
|
||||
mCycleSpinner.setAccessibilityDelegate(new AccessibilityDelegate() {
|
||||
@Override
|
||||
public void sendAccessibilityEvent(View host, int eventType) {
|
||||
if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) {
|
||||
// Ignore TYPE_VIEW_SELECTED or TalkBack will speak for it at onResume.
|
||||
return;
|
||||
mDataUsageListHeaderController = new DataUsageListHeaderController(
|
||||
setPinnedHeaderView(R.layout.apps_filter_spinner),
|
||||
mTemplate,
|
||||
getMetricsCategory(),
|
||||
(cycle, position) -> {
|
||||
updateSelectedCycle(cycle, position);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
super.sendAccessibilityEvent(host, eventType);
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
mLoadingViewController = new LoadingViewController(
|
||||
getView().findViewById(R.id.loading_container), getListView());
|
||||
@@ -213,6 +173,7 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
mLoadingViewController.showLoadingViewDelayed();
|
||||
mDataStateListener.start(mSubId);
|
||||
mLastDisplayedCycle = null;
|
||||
updatePolicy();
|
||||
|
||||
// kick off loader for network history
|
||||
// TODO: consider chaining two loaders together instead of reloading
|
||||
@@ -291,36 +252,31 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updatePolicy() {
|
||||
final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
|
||||
final View configureButton = mHeader.findViewById(R.id.filter_settings);
|
||||
//SUB SELECT
|
||||
if (policy != null && isMobileDataAvailable()) {
|
||||
mChart.setNetworkPolicy(policy);
|
||||
configureButton.setVisibility(View.VISIBLE);
|
||||
mIsBillingCycleModifiable = isBillingCycleModifiable();
|
||||
if (mIsBillingCycleModifiable) {
|
||||
mChart.setNetworkPolicy(services.mPolicyEditor.getPolicy(mTemplate));
|
||||
} else {
|
||||
// controls are disabled; don't bind warning/limit sweeps
|
||||
mChart.setNetworkPolicy(null);
|
||||
configureButton.setVisibility(View.GONE);
|
||||
mChart.setNetworkPolicy(null); // don't bind warning / limit sweeps
|
||||
}
|
||||
|
||||
// generate cycle list based on policy and available history
|
||||
if (mCycleData != null) {
|
||||
mCycleAdapter.updateCycleList(mCycleData);
|
||||
}
|
||||
mDataUsageListAppsController.setCycleData(mCycleData);
|
||||
updateSelectedCycle();
|
||||
updateConfigButtonVisibility();
|
||||
}
|
||||
|
||||
private boolean isMobileDataAvailable() {
|
||||
@VisibleForTesting
|
||||
boolean isBillingCycleModifiable() {
|
||||
return mBillingCycleRepository.isModifiable(mSubId)
|
||||
&& SubscriptionManager.from(requireContext())
|
||||
.getActiveSubscriptionInfo(mSubId) != null;
|
||||
}
|
||||
|
||||
private void updateConfigButtonVisibility() {
|
||||
mDataUsageListHeaderController.setConfigButtonVisible(
|
||||
mIsBillingCycleModifiable && mCycleData != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the chart and detail data when initial loaded or selected cycle changed.
|
||||
*/
|
||||
private void updateSelectedCycle() {
|
||||
private void updateSelectedCycle(CycleAdapter.CycleItem cycle, int position) {
|
||||
// Avoid from updating UI after #onStop.
|
||||
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
|
||||
return;
|
||||
@@ -332,11 +288,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
return;
|
||||
}
|
||||
|
||||
final int position = mCycleSpinner.getSelectedItemPosition();
|
||||
if (mCycleAdapter.getCount() == 0 || position < 0) {
|
||||
return;
|
||||
}
|
||||
final CycleAdapter.CycleItem cycle = mCycleAdapter.getItem(position);
|
||||
if (Objects.equals(cycle, mLastDisplayedCycle)) {
|
||||
// Avoid duplicate update to avoid page flash.
|
||||
return;
|
||||
@@ -350,9 +301,10 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
|
||||
// update chart to show selected cycle, and update detail data
|
||||
// to match updated sweep bounds.
|
||||
mChart.setNetworkCycleData(mCycleData.get(position));
|
||||
NetworkCycleChartData cycleChartData = mCycleData.get(position);
|
||||
mChart.setNetworkCycleData(cycleChartData);
|
||||
|
||||
updateDetailData();
|
||||
updateDetailData(cycleChartData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,34 +312,21 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
* current mode. Updates {@link #mAdapter} with sorted list
|
||||
* of applications data usage.
|
||||
*/
|
||||
private void updateDetailData() {
|
||||
private void updateDetailData(NetworkCycleChartData cycleChartData) {
|
||||
if (LOGD) Log.d(TAG, "updateDetailData()");
|
||||
|
||||
// kick off loader for detailed stats
|
||||
mDataUsageListAppsController.update(
|
||||
mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
|
||||
mChart.getInspectStart(),
|
||||
mChart.getInspectEnd()
|
||||
cycleChartData.getStartTime(),
|
||||
cycleChartData.getEndTime()
|
||||
);
|
||||
|
||||
final long totalBytes = mCycleData != null && !mCycleData.isEmpty()
|
||||
? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
|
||||
final long totalBytes = cycleChartData.getTotalUsage();
|
||||
final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
|
||||
mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
|
||||
}
|
||||
|
||||
private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
updateSelectedCycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
final LoaderCallbacks<List<NetworkCycleChartData>> mNetworkCycleDataCallbacks =
|
||||
new LoaderCallbacks<>() {
|
||||
@@ -404,9 +343,9 @@ public class DataUsageList extends DataUsageBaseFragment
|
||||
List<NetworkCycleChartData> data) {
|
||||
mLoadingViewController.showContent(false /* animate */);
|
||||
mCycleData = data;
|
||||
// calculate policy cycles based on available data
|
||||
updatePolicy();
|
||||
mCycleSpinner.setVisibility(View.VISIBLE);
|
||||
mDataUsageListHeaderController.updateCycleData(mCycleData);
|
||||
updateConfigButtonVisibility();
|
||||
mDataUsageListAppsController.setCycleData(mCycleData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.net.NetworkTemplate
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.widget.AdapterView
|
||||
import android.widget.Spinner
|
||||
import androidx.annotation.OpenForTesting
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.SubSettingLauncher
|
||||
import com.android.settings.datausage.CycleAdapter.CycleItem
|
||||
import com.android.settings.datausage.CycleAdapter.SpinnerInterface
|
||||
import com.android.settingslib.net.NetworkCycleChartData
|
||||
|
||||
@OpenForTesting
|
||||
open class DataUsageListHeaderController(
|
||||
header: View,
|
||||
template: NetworkTemplate,
|
||||
sourceMetricsCategory: Int,
|
||||
private val onItemSelected: (cycleItem: CycleItem, position: Int) -> Unit,
|
||||
) {
|
||||
private val context = header.context
|
||||
private val configureButton: View = header.requireViewById(R.id.filter_settings)
|
||||
private val cycleSpinner: Spinner = header.requireViewById(R.id.filter_spinner)
|
||||
private val cycleAdapter = CycleAdapter(context, object : SpinnerInterface {
|
||||
override fun setAdapter(cycleAdapter: CycleAdapter) {
|
||||
cycleSpinner.adapter = cycleAdapter
|
||||
}
|
||||
|
||||
override fun getSelectedItem() = cycleSpinner.selectedItem
|
||||
|
||||
override fun setSelection(position: Int) {
|
||||
cycleSpinner.setSelection(position)
|
||||
}
|
||||
})
|
||||
|
||||
private val cycleListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
if (0 <= position && position < cycleAdapter.count) {
|
||||
cycleAdapter.getItem(position)?.let { cycleItem ->
|
||||
onItemSelected(cycleItem, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
configureButton.setOnClickListener {
|
||||
val args = Bundle().apply {
|
||||
putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, template)
|
||||
}
|
||||
SubSettingLauncher(context).apply {
|
||||
setDestination(BillingCycleSettings::class.java.name)
|
||||
setTitleRes(R.string.billing_cycle)
|
||||
setSourceMetricsCategory(sourceMetricsCategory)
|
||||
setArguments(args)
|
||||
}.launch()
|
||||
}
|
||||
cycleSpinner.visibility = View.GONE
|
||||
cycleSpinner.accessibilityDelegate = object : View.AccessibilityDelegate() {
|
||||
override fun sendAccessibilityEvent(host: View, eventType: Int) {
|
||||
if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) {
|
||||
// Ignore TYPE_VIEW_SELECTED or TalkBack will speak for it at onResume.
|
||||
return
|
||||
}
|
||||
super.sendAccessibilityEvent(host, eventType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun setConfigButtonVisible(visible: Boolean) {
|
||||
configureButton.visibility = if (visible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
open fun updateCycleData(cycleData: List<NetworkCycleChartData>) {
|
||||
cycleSpinner.onItemSelectedListener = cycleListener
|
||||
// calculate policy cycles based on available data
|
||||
// generate cycle list based on policy and available history
|
||||
cycleAdapter.updateCycleList(cycleData)
|
||||
cycleSpinner.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user