Fix DataUsagePreferenceController ANR am: d42cc8e5e2
am: eb8948e309
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23896225 Change-Id: I3e6b70cb33a51348b9504226787de2e149af5870 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 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.network.telephony;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.NetworkTemplate;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.telephony.SubscriptionManager;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.datausage.DataUsageUtils;
|
|
||||||
import com.android.settings.datausage.lib.DataUsageLib;
|
|
||||||
import com.android.settingslib.net.DataUsageController;
|
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference controller for "Data usage"
|
|
||||||
*/
|
|
||||||
public class DataUsagePreferenceController extends TelephonyBasePreferenceController {
|
|
||||||
|
|
||||||
private static final String LOG_TAG = "DataUsagePreferCtrl";
|
|
||||||
|
|
||||||
private Future<NetworkTemplate> mTemplateFuture;
|
|
||||||
private AtomicReference<NetworkTemplate> mTemplate;
|
|
||||||
private Future<Long> mHistoricalUsageLevel;
|
|
||||||
|
|
||||||
public DataUsagePreferenceController(Context context, String key) {
|
|
||||||
super(context, key);
|
|
||||||
mTemplate = new AtomicReference<NetworkTemplate>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAvailabilityStatus(int subId) {
|
|
||||||
return (SubscriptionManager.isValidSubscriptionId(subId))
|
|
||||||
&& DataUsageUtils.hasMobileData(mContext)
|
|
||||||
? AVAILABLE
|
|
||||||
: AVAILABLE_UNSEARCHABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
||||||
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Intent intent = new Intent(Settings.ACTION_MOBILE_DATA_USAGE);
|
|
||||||
intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, getNetworkTemplate());
|
|
||||||
intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
|
|
||||||
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateState(Preference preference) {
|
|
||||||
super.updateState(preference);
|
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
|
|
||||||
preference.setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final CharSequence summary = getDataUsageSummary(mContext, mSubId);
|
|
||||||
if (summary == null) {
|
|
||||||
preference.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
preference.setEnabled(true);
|
|
||||||
preference.setSummary(summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(int subId) {
|
|
||||||
mSubId = subId;
|
|
||||||
mTemplate.set(null);
|
|
||||||
mTemplateFuture = ThreadUtils.postOnBackgroundThread(()
|
|
||||||
-> fetchMobileTemplate(mContext, mSubId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private NetworkTemplate fetchMobileTemplate(Context context, int subId) {
|
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DataUsageLib.getMobileTemplate(context, subId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private NetworkTemplate getNetworkTemplate() {
|
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
NetworkTemplate template = mTemplate.get();
|
|
||||||
if (template != null) {
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
template = mTemplateFuture.get();
|
|
||||||
mTemplate.set(template);
|
|
||||||
} catch (ExecutionException | InterruptedException | NullPointerException exception) {
|
|
||||||
Log.e(LOG_TAG, "Fail to get data usage template", exception);
|
|
||||||
}
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
DataUsageController.DataUsageInfo getDataUsageInfo(DataUsageController controller) {
|
|
||||||
return controller.getDataUsageInfo(getNetworkTemplate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CharSequence getDataUsageSummary(Context context, int subId) {
|
|
||||||
final DataUsageController controller = new DataUsageController(context);
|
|
||||||
controller.setSubscriptionId(subId);
|
|
||||||
|
|
||||||
mHistoricalUsageLevel = ThreadUtils.postOnBackgroundThread(() ->
|
|
||||||
controller.getHistoricalUsageLevel(getNetworkTemplate()));
|
|
||||||
|
|
||||||
final DataUsageController.DataUsageInfo usageInfo = getDataUsageInfo(controller);
|
|
||||||
|
|
||||||
long usageLevel = usageInfo.usageLevel;
|
|
||||||
if (usageLevel <= 0L) {
|
|
||||||
try {
|
|
||||||
usageLevel = mHistoricalUsageLevel.get();
|
|
||||||
} catch (Exception exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (usageLevel <= 0L) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return context.getString(R.string.data_usage_template,
|
|
||||||
DataUsageUtils.formatDataUsage(context, usageLevel), usageInfo.period);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.NetworkTemplate
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.datausage.DataUsageUtils
|
||||||
|
import com.android.settingslib.net.DataUsageController
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller for "Data usage"
|
||||||
|
*/
|
||||||
|
class DataUsagePreferenceController(context: Context, key: String) :
|
||||||
|
TelephonyBasePreferenceController(context, key) {
|
||||||
|
|
||||||
|
private lateinit var preference: Preference
|
||||||
|
private var networkTemplate: NetworkTemplate? = null
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
var dataUsageControllerFactory: (Context) -> DataUsageController = { DataUsageController(it) }
|
||||||
|
|
||||||
|
fun init(subId: Int) {
|
||||||
|
mSubId = subId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(subId: Int): Int = when {
|
||||||
|
SubscriptionManager.isValidSubscriptionId(subId) &&
|
||||||
|
DataUsageUtils.hasMobileData(mContext) -> AVAILABLE
|
||||||
|
|
||||||
|
else -> AVAILABLE_UNSEARCHABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun displayPreference(screen: PreferenceScreen) {
|
||||||
|
super.displayPreference(screen)
|
||||||
|
preference = screen.findPreference(preferenceKey)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whenViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
|
||||||
|
if (preference.key != preferenceKey || networkTemplate == null) return false
|
||||||
|
val intent = Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply {
|
||||||
|
putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate)
|
||||||
|
putExtra(Settings.EXTRA_SUB_ID, mSubId)
|
||||||
|
}
|
||||||
|
mContext.startActivity(intent)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun update() {
|
||||||
|
val summary = withContext(Dispatchers.Default) {
|
||||||
|
networkTemplate = getNetworkTemplate()
|
||||||
|
getDataUsageSummary()
|
||||||
|
}
|
||||||
|
if (summary == null) {
|
||||||
|
preference.isEnabled = false
|
||||||
|
} else {
|
||||||
|
preference.isEnabled = true
|
||||||
|
preference.summary = summary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNetworkTemplate(): NetworkTemplate? = when {
|
||||||
|
SubscriptionManager.isValidSubscriptionId(mSubId) -> {
|
||||||
|
DataUsageUtils.getMobileTemplate(mContext, mSubId)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDataUsageSummary(): String? {
|
||||||
|
val networkTemplate = networkTemplate ?: return null
|
||||||
|
val controller = dataUsageControllerFactory(mContext).apply {
|
||||||
|
setSubscriptionId(mSubId)
|
||||||
|
}
|
||||||
|
val usageInfo = controller.getDataUsageInfo(networkTemplate)
|
||||||
|
if (usageInfo != null && usageInfo.usageLevel > 0) {
|
||||||
|
return mContext.getString(
|
||||||
|
R.string.data_usage_template,
|
||||||
|
DataUsageUtils.formatDataUsage(mContext, usageInfo.usageLevel),
|
||||||
|
usageInfo.period,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller.getHistoricalUsageLevel(networkTemplate).takeIf { it > 0 }?.let {
|
||||||
|
mContext.getString(
|
||||||
|
R.string.data_used_template,
|
||||||
|
DataUsageUtils.formatDataUsage(mContext, it),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -31,7 +31,10 @@ import android.util.Log;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
@@ -327,6 +330,12 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
|
|||||||
onRestoreInstance(icicle);
|
onRestoreInstance(icicle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
use(DataUsagePreferenceController.class).whenViewCreated(getViewLifecycleOwner());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
@@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 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.network.telephony;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.doNothing;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
|
|
||||||
import android.app.usage.NetworkStatsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.telephony.SubscriptionManager;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.util.DataUnit;
|
|
||||||
|
|
||||||
import androidx.preference.SwitchPreference;
|
|
||||||
|
|
||||||
import com.android.settings.core.BasePreferenceController;
|
|
||||||
import com.android.settingslib.net.DataUsageController;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.Shadows;
|
|
||||||
import org.robolectric.shadows.ShadowTelephonyManager;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public class DataUsagePreferenceControllerTest {
|
|
||||||
private static final int SUB_ID = 2;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private NetworkStatsManager mNetworkStatsManager;
|
|
||||||
private DataUsagePreferenceController mController;
|
|
||||||
private SwitchPreference mPreference;
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
|
|
||||||
final TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
|
|
||||||
final ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf(telephonyManager);
|
|
||||||
shadowTelephonyManager.setTelephonyManagerForSubscriptionId(SUB_ID, telephonyManager);
|
|
||||||
shadowTelephonyManager.setTelephonyManagerForSubscriptionId(
|
|
||||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID, telephonyManager);
|
|
||||||
|
|
||||||
doReturn(mNetworkStatsManager).when(mContext).getSystemService(NetworkStatsManager.class);
|
|
||||||
|
|
||||||
mPreference = new SwitchPreference(mContext);
|
|
||||||
mController = spy(new DataUsagePreferenceController(mContext, "data_usage"));
|
|
||||||
mController.init(SUB_ID);
|
|
||||||
mPreference.setKey(mController.getPreferenceKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAvailabilityStatus_validSubId_returnAvailable() {
|
|
||||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
|
||||||
BasePreferenceController.AVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAvailabilityStatus_invalidSubId_returnUnsearchable() {
|
|
||||||
mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
|
||||||
|
|
||||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
|
||||||
BasePreferenceController.AVAILABLE_UNSEARCHABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_needDialog_showDialog() {
|
|
||||||
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
|
||||||
doNothing().when(mContext).startActivity(captor.capture());
|
|
||||||
|
|
||||||
mController.handlePreferenceTreeClick(mPreference);
|
|
||||||
|
|
||||||
final Intent intent = captor.getValue();
|
|
||||||
|
|
||||||
assertThat(intent.getAction()).isEqualTo(Settings.ACTION_MOBILE_DATA_USAGE);
|
|
||||||
assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateState_invalidSubId_disabled() {
|
|
||||||
mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
|
||||||
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
|
|
||||||
assertThat(mPreference.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateState_noUsageData_shouldDisablePreference() {
|
|
||||||
final DataUsageController.DataUsageInfo usageInfo =
|
|
||||||
new DataUsageController.DataUsageInfo();
|
|
||||||
doReturn(usageInfo).when(mController).getDataUsageInfo(any());
|
|
||||||
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
|
|
||||||
assertThat(mPreference.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateState_shouldUseIECUnit() {
|
|
||||||
final DataUsageController.DataUsageInfo usageInfo =
|
|
||||||
new DataUsageController.DataUsageInfo();
|
|
||||||
usageInfo.usageLevel = DataUnit.MEBIBYTES.toBytes(1);
|
|
||||||
doReturn(usageInfo).when(mController).getDataUsageInfo(any());
|
|
||||||
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
|
|
||||||
assertThat(mPreference.getSummary().toString())
|
|
||||||
.contains("1.00 MB");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.NetworkTemplate
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.util.DataUnit
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.testing.TestLifecycleOwner
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||||
|
import com.android.settings.core.BasePreferenceController.AVAILABLE
|
||||||
|
import com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE
|
||||||
|
import com.android.settings.datausage.DataUsageUtils
|
||||||
|
import com.android.settingslib.net.DataUsageController
|
||||||
|
import com.android.settingslib.net.DataUsageController.DataUsageInfo
|
||||||
|
import com.android.settingslib.spa.testutils.waitUntil
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.doNothing
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoSession
|
||||||
|
import org.mockito.Spy
|
||||||
|
import org.mockito.quality.Strictness
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class DataUsagePreferenceControllerTest {
|
||||||
|
|
||||||
|
private lateinit var mockSession: MockitoSession
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
|
||||||
|
private lateinit var controller: DataUsagePreferenceController
|
||||||
|
|
||||||
|
private val preference = Preference(context)
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var networkTemplate: NetworkTemplate
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var dataUsageController: DataUsageController
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var preferenceScreen: PreferenceScreen
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockSession = ExtendedMockito.mockitoSession()
|
||||||
|
.initMocks(this)
|
||||||
|
.mockStatic(SubscriptionManager::class.java)
|
||||||
|
.spyStatic(DataUsageUtils::class.java)
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.startMocking()
|
||||||
|
|
||||||
|
whenever(SubscriptionManager.isValidSubscriptionId(SUB_ID)).thenReturn(true)
|
||||||
|
ExtendedMockito.doReturn(true).`when` { DataUsageUtils.hasMobileData(context) }
|
||||||
|
ExtendedMockito.doReturn(networkTemplate)
|
||||||
|
.`when` { DataUsageUtils.getMobileTemplate(context, SUB_ID) }
|
||||||
|
preference.key = TEST_KEY
|
||||||
|
whenever(preferenceScreen.findPreference<Preference>(TEST_KEY)).thenReturn(preference)
|
||||||
|
|
||||||
|
controller =
|
||||||
|
DataUsagePreferenceController(context, TEST_KEY).apply {
|
||||||
|
init(SUB_ID)
|
||||||
|
displayPreference(preferenceScreen)
|
||||||
|
dataUsageControllerFactory = { dataUsageController }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockSession.finishMocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_validSubId_returnAvailable() {
|
||||||
|
assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_invalidSubId_returnUnsearchable() {
|
||||||
|
controller.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||||
|
|
||||||
|
assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE_UNSEARCHABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlePreferenceTreeClick_startActivity() = runTest {
|
||||||
|
val usageInfo = DataUsageInfo().apply {
|
||||||
|
usageLevel = DataUnit.MEBIBYTES.toBytes(1)
|
||||||
|
}
|
||||||
|
whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
|
||||||
|
doNothing().`when`(context).startActivity(any())
|
||||||
|
controller.whenViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
|
||||||
|
waitUntil { preference.summary != null }
|
||||||
|
|
||||||
|
controller.handlePreferenceTreeClick(preference)
|
||||||
|
|
||||||
|
val captor = ArgumentCaptor.forClass(Intent::class.java)
|
||||||
|
verify(context).startActivity(captor.capture())
|
||||||
|
val intent = captor.value
|
||||||
|
assertThat(intent.action).isEqualTo(Settings.ACTION_MOBILE_DATA_USAGE)
|
||||||
|
assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateState_invalidSubId_disabled() = runTest {
|
||||||
|
controller.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||||
|
|
||||||
|
controller.whenViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
|
||||||
|
|
||||||
|
waitUntil { !preference.isEnabled }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateState_noUsageData_shouldDisablePreference() = runTest {
|
||||||
|
val usageInfo = DataUsageInfo()
|
||||||
|
whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
|
||||||
|
|
||||||
|
controller.whenViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
|
||||||
|
|
||||||
|
waitUntil { !preference.isEnabled }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateState_shouldUseIecUnit() = runTest {
|
||||||
|
val usageInfo = DataUsageInfo().apply {
|
||||||
|
usageLevel = DataUnit.MEBIBYTES.toBytes(1)
|
||||||
|
}
|
||||||
|
whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
|
||||||
|
|
||||||
|
controller.whenViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
|
||||||
|
|
||||||
|
waitUntil { preference.summary?.contains("1.00 MB") == true }
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
const val SUB_ID = 2
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user