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:
Chaohui Wang
2023-09-22 17:02:12 +08:00
parent 41fab98b6a
commit 5ed30f4672
8 changed files with 265 additions and 164 deletions

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -47,7 +47,6 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne
notifyChanged();
}
@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mListener = listener;
}

View File

@@ -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;
}
}
}

View File

@@ -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)
}
}