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;
|
||||
}
|
||||
|
@@ -34,17 +34,12 @@ import android.net.NetworkTemplate;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.datausage.lib.BillingCycleRepository;
|
||||
import com.android.settings.network.MobileDataEnabledListener;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
@@ -52,6 +47,7 @@ import com.android.settings.widget.LoadingViewController;
|
||||
import com.android.settingslib.NetworkPolicyEditor;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
|
||||
import com.android.settingslib.net.NetworkCycleChartData;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -69,7 +65,11 @@ import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = DataUsageListTest.ShadowDataUsageBaseFragment.class)
|
||||
public class DataUsageListTest {
|
||||
@Rule
|
||||
public MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@@ -84,6 +84,8 @@ public class DataUsageListTest {
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private BillingCycleRepository mBillingCycleRepository;
|
||||
@Mock
|
||||
private DataUsageListHeaderController mDataUsageListHeaderController;
|
||||
|
||||
private Activity mActivity;
|
||||
|
||||
@@ -98,7 +100,6 @@ public class DataUsageListTest {
|
||||
mActivity = spy(mActivityController.get());
|
||||
mNetworkServices.mPolicyEditor = mock(NetworkPolicyEditor.class);
|
||||
mDataUsageList.mDataStateListener = mMobileDataEnabledListener;
|
||||
mDataUsageList.mTemplate = mock(NetworkTemplate.class);
|
||||
|
||||
doReturn(mActivity).when(mDataUsageList).getContext();
|
||||
doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class);
|
||||
@@ -110,11 +111,12 @@ public class DataUsageListTest {
|
||||
mDataUsageList.mLoadingViewController = mock(LoadingViewController.class);
|
||||
doNothing().when(mDataUsageList).updateSubscriptionInfoEntity();
|
||||
when(mBillingCycleRepository.isBandwidthControlEnabled()).thenReturn(true);
|
||||
mDataUsageList.mDataUsageListHeaderController = mDataUsageListHeaderController;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowDataUsageBaseFragment.class)
|
||||
public void onCreate_isNotGuestUser_shouldNotFinish() {
|
||||
mDataUsageList.mTemplate = mock(NetworkTemplate.class);
|
||||
doReturn(false).when(mUserManager).isGuestUser();
|
||||
doNothing().when(mDataUsageList).processArgument();
|
||||
|
||||
@@ -124,7 +126,6 @@ public class DataUsageListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowDataUsageBaseFragment.class)
|
||||
public void onCreate_isGuestUser_shouldFinish() {
|
||||
doReturn(true).when(mUserManager).isGuestUser();
|
||||
|
||||
@@ -135,6 +136,7 @@ public class DataUsageListTest {
|
||||
|
||||
@Test
|
||||
public void resume_shouldListenDataStateChange() {
|
||||
mDataUsageList.onCreate(null);
|
||||
ReflectionHelpers.setField(
|
||||
mDataUsageList, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class));
|
||||
ReflectionHelpers.setField(
|
||||
@@ -149,6 +151,7 @@ public class DataUsageListTest {
|
||||
|
||||
@Test
|
||||
public void pause_shouldUnlistenDataStateChange() {
|
||||
mDataUsageList.onCreate(null);
|
||||
ReflectionHelpers.setField(
|
||||
mDataUsageList, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class));
|
||||
ReflectionHelpers.setField(
|
||||
@@ -187,12 +190,10 @@ public class DataUsageListTest {
|
||||
|
||||
@Test
|
||||
public void processArgument_fromIntent_shouldGetTemplateFromIntent() {
|
||||
final FragmentActivity activity = mock(FragmentActivity.class);
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mock(NetworkTemplate.class));
|
||||
intent.putExtra(Settings.EXTRA_SUB_ID, 3);
|
||||
when(activity.getIntent()).thenReturn(intent);
|
||||
doReturn(activity).when(mDataUsageList).getActivity();
|
||||
doReturn(intent).when(mDataUsageList).getIntent();
|
||||
|
||||
mDataUsageList.processArgument();
|
||||
|
||||
@@ -200,31 +201,17 @@ public class DataUsageListTest {
|
||||
assertThat(mDataUsageList.mSubId).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onViewCreated_shouldHideCycleSpinner() {
|
||||
final View view = new View(mActivity);
|
||||
final View header = getHeader();
|
||||
final Spinner spinner = getSpinner(header);
|
||||
spinner.setVisibility(View.VISIBLE);
|
||||
doReturn(header).when(mDataUsageList).setPinnedHeaderView(anyInt());
|
||||
doReturn(view).when(mDataUsageList).getView();
|
||||
|
||||
mDataUsageList.onViewCreated(view, null);
|
||||
|
||||
assertThat(spinner.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLoadFinished_networkCycleDataCallback_shouldShowCycleSpinner() {
|
||||
final Spinner spinner = getSpinner(getHeader());
|
||||
spinner.setVisibility(View.INVISIBLE);
|
||||
mDataUsageList.mCycleSpinner = spinner;
|
||||
assertThat(spinner.getVisibility()).isEqualTo(View.INVISIBLE);
|
||||
doNothing().when(mDataUsageList).updatePolicy();
|
||||
mDataUsageList.mTemplate = mock(NetworkTemplate.class);
|
||||
mDataUsageList.onCreate(null);
|
||||
mDataUsageList.updatePolicy();
|
||||
List<NetworkCycleChartData> mockData = Collections.emptyList();
|
||||
|
||||
mDataUsageList.mNetworkCycleDataCallbacks.onLoadFinished(null, null);
|
||||
mDataUsageList.mNetworkCycleDataCallbacks.onLoadFinished(null, mockData);
|
||||
|
||||
assertThat(spinner.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
verify(mDataUsageListHeaderController).updateCycleData(mockData);
|
||||
verify(mDataUsageListHeaderController).setConfigButtonVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -234,19 +221,6 @@ public class DataUsageListTest {
|
||||
verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_CHART_DATA);
|
||||
}
|
||||
|
||||
private View getHeader() {
|
||||
final View rootView = LayoutInflater.from(mActivity)
|
||||
.inflate(R.layout.preference_list_fragment, null, false);
|
||||
final FrameLayout pinnedHeader = rootView.findViewById(R.id.pinned_header);
|
||||
|
||||
return mActivity.getLayoutInflater()
|
||||
.inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
|
||||
}
|
||||
|
||||
private Spinner getSpinner(View header) {
|
||||
return header.findViewById(R.id.filter_spinner);
|
||||
}
|
||||
|
||||
@Implements(DataUsageBaseFragment.class)
|
||||
public static class ShadowDataUsageBaseFragment {
|
||||
@Implementation
|
||||
@@ -261,10 +235,28 @@ public class DataUsageListTest {
|
||||
return mock(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Preference> T findPreference(CharSequence key) {
|
||||
if (key.toString().equals("chart_data")) {
|
||||
return (T) mock(ChartDataUsagePreference.class);
|
||||
}
|
||||
return (T) mock(Preference.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return new Intent();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
BillingCycleRepository createBillingCycleRepository() {
|
||||
return mBillingCycleRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isBillingCycleModifiable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Spinner
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doNothing
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DataUsageListHeaderControllerTest {
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
doNothing().whenever(mock).startActivity(any())
|
||||
}
|
||||
|
||||
private val header =
|
||||
LayoutInflater.from(context).inflate(R.layout.apps_filter_spinner, null, false)
|
||||
|
||||
private val configureButton: View = header.requireViewById(R.id.filter_settings)
|
||||
|
||||
private val spinner: Spinner = header.requireViewById(R.id.filter_spinner)
|
||||
|
||||
private val controller = DataUsageListHeaderController(
|
||||
header = header,
|
||||
template = mock<NetworkTemplate>(),
|
||||
sourceMetricsCategory = 0,
|
||||
onItemSelected = { _, _ -> },
|
||||
)
|
||||
|
||||
@Test
|
||||
fun onViewCreated_shouldHideCycleSpinner() {
|
||||
assertThat(spinner.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateCycleData_shouldShowCycleSpinner() {
|
||||
controller.updateCycleData(emptyList())
|
||||
|
||||
assertThat(spinner.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setConfigButtonVisible_setToTrue_shouldShowConfigureButton() {
|
||||
controller.setConfigButtonVisible(true)
|
||||
|
||||
assertThat(configureButton.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setConfigButtonVisible_setToFalse_shouldHideConfigureButton() {
|
||||
controller.setConfigButtonVisible(false)
|
||||
|
||||
assertThat(configureButton.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user