Improve AutoSelectPreferenceController

Deprecate ServiceStateStatus (which could produce wrong value),
and replaced with serviceStateFlow.

Fix: 299068234
Test: manual - turn auto select off and on
Test: unit test
Change-Id: I42fe160500c68cc9ee0fe383121f64146ddbb7f2
This commit is contained in:
Chaohui Wang
2023-12-28 15:57:46 +08:00
parent 87f76d1c32
commit 2ec3840987
6 changed files with 423 additions and 527 deletions

View File

@@ -210,7 +210,7 @@
android:title="@string/network_operator_category"
settings:controller="com.android.settings.network.telephony.NetworkPreferenceCategoryController">
<SwitchPreferenceCompat
<com.android.settings.spa.preference.ComposePreference
android:key="auto_select_key"
android:title="@string/select_automatically"
settings:controller="com.android.settings.network.telephony.gsm.AutoSelectPreferenceController"/>

View File

@@ -276,7 +276,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
use(OpenNetworkSelectPagePreferenceController.class).init(mSubId);
final AutoSelectPreferenceController autoSelectPreferenceController =
use(AutoSelectPreferenceController.class)
.init(getLifecycle(), mSubId)
.init(mSubId)
.addListener(openNetworkSelectPagePreferenceController);
use(NetworkPreferenceCategoryController.class).init(mSubId)
.setChildren(Arrays.asList(autoSelectPreferenceController));

View File

@@ -1,339 +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.gsm;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.network.AllowedNetworkTypesListener;
import com.android.settings.network.CarrierConfigCache;
import com.android.settings.network.helper.ServiceStateStatus;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.TelephonyTogglePreferenceController;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
/**
* Preference controller for "Auto Select Network"
*/
public class AutoSelectPreferenceController extends TelephonyTogglePreferenceController
implements LifecycleEventObserver{
private static final long MINIMUM_DIALOG_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1);
private static final String LOG_TAG = "AutoSelectPreferenceController";
private static final String INTERNAL_LOG_TAG_ONRESUME = "OnResume";
private static final String INTERNAL_LOG_TAG_AFTERSET = "AfterSet";
private final Handler mUiHandler;
private PreferenceScreen mPreferenceScreen;
private AllowedNetworkTypesListener mAllowedNetworkTypesListener;
private TelephonyManager mTelephonyManager;
private boolean mOnlyAutoSelectInHome;
private List<OnNetworkSelectModeListener> mListeners;
@VisibleForTesting
ProgressDialog mProgressDialog;
@VisibleForTesting
TwoStatePreference mSwitchPreference;
private AtomicBoolean mUpdatingConfig;
private int mCacheOfModeStatus;
private AtomicLong mRecursiveUpdate;
ServiceStateStatus mServiceStateStatus;
public AutoSelectPreferenceController(Context context, String key) {
super(context, key);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mRecursiveUpdate = new AtomicLong();
mUpdatingConfig = new AtomicBoolean();
mCacheOfModeStatus = TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
mListeners = new ArrayList<>();
mUiHandler = new Handler(Looper.getMainLooper());
mAllowedNetworkTypesListener = new AllowedNetworkTypesListener(
new HandlerExecutor(mUiHandler));
mAllowedNetworkTypesListener.setAllowedNetworkTypesListener(
() -> updatePreference());
}
private void updatePreference() {
if (mPreferenceScreen != null) {
displayPreference(mPreferenceScreen);
}
if (mSwitchPreference != null) {
mRecursiveUpdate.getAndIncrement();
updateState(mSwitchPreference);
mRecursiveUpdate.decrementAndGet();
}
}
/**
* Implementation of LifecycleEventObserver.
*/
@SuppressWarnings("FutureReturnValueIgnored")
public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
switch (event) {
case ON_START:
mAllowedNetworkTypesListener.register(mContext, mSubId);
break;
case ON_RESUME:
ThreadUtils.postOnBackgroundThread(() -> {
queryNetworkSelectionMode(INTERNAL_LOG_TAG_ONRESUME);
//Update UI in UI thread
mUiHandler.post(() -> {
if (mSwitchPreference != null) {
mRecursiveUpdate.getAndIncrement();
mSwitchPreference.setChecked(isChecked());
mRecursiveUpdate.decrementAndGet();
updateListenerValue();
}
});
});
break;
case ON_STOP:
mAllowedNetworkTypesListener.unregister(mContext, mSubId);
break;
default:
// Do nothing
break;
}
}
@Override
public int getAvailabilityStatus(int subId) {
return MobileNetworkUtils.shouldDisplayNetworkSelectOptions(mContext, subId)
? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceScreen = screen;
mSwitchPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean isChecked() {
return mCacheOfModeStatus == TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
preference.setSummary(null);
final ServiceState serviceState = mTelephonyManager.getServiceState();
if (serviceState == null) {
preference.setEnabled(false);
return;
}
if (serviceState.getRoaming()) {
preference.setEnabled(true);
} else {
preference.setEnabled(!mOnlyAutoSelectInHome);
if (mOnlyAutoSelectInHome) {
preference.setSummary(mContext.getString(
R.string.manual_mode_disallowed_summary,
mTelephonyManager.getSimOperatorName()));
}
}
}
@Override
public boolean setChecked(boolean isChecked) {
if (mRecursiveUpdate.get() != 0) {
// Changing from software are allowed and changing presentation only.
return true;
}
if (isChecked) {
setAutomaticSelectionMode();
} else {
if (mSwitchPreference != null) {
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME,
SETTINGS_PACKAGE_NAME + ".Settings$NetworkSelectActivity");
intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
mSwitchPreference.setIntent(intent);
}
}
return false;
}
@VisibleForTesting
Future setAutomaticSelectionMode() {
final long startMillis = SystemClock.elapsedRealtime();
showAutoSelectProgressBar();
if (mSwitchPreference != null) {
mSwitchPreference.setIntent(null);
mSwitchPreference.setEnabled(false);
}
return ThreadUtils.postOnBackgroundThread(() -> {
// set network selection mode in background
mUpdatingConfig.set(true);
mTelephonyManager.setNetworkSelectionModeAutomatic();
mUpdatingConfig.set(false);
//Update UI in UI thread
final long durationMillis = SystemClock.elapsedRealtime() - startMillis;
mUiHandler.postDelayed(() -> {
ThreadUtils.postOnBackgroundThread(() -> {
queryNetworkSelectionMode(INTERNAL_LOG_TAG_AFTERSET);
//Update UI in UI thread
mUiHandler.post(() -> {
mRecursiveUpdate.getAndIncrement();
if (mSwitchPreference != null) {
mSwitchPreference.setEnabled(true);
mSwitchPreference.setChecked(isChecked());
}
mRecursiveUpdate.decrementAndGet();
updateListenerValue();
dismissProgressBar();
});
});
}, Math.max(MINIMUM_DIALOG_TIME_MILLIS - durationMillis, 0));
});
}
/**
* Initialization based on given subscription id.
**/
public AutoSelectPreferenceController init(Lifecycle lifecycle, int subId) {
mSubId = subId;
mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
.createForSubscriptionId(mSubId);
final PersistableBundle carrierConfig =
CarrierConfigCache.getInstance(mContext).getConfigForSubId(mSubId);
mOnlyAutoSelectInHome = carrierConfig != null
? carrierConfig.getBoolean(
CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL)
: false;
mServiceStateStatus = new ServiceStateStatus(lifecycle, mTelephonyManager,
new HandlerExecutor(mUiHandler)) {
@Override
protected void setValue(ServiceState status) {
if (status == null) {
return;
}
updateUiAutoSelectValue(status);
}
};
return this;
}
public AutoSelectPreferenceController addListener(OnNetworkSelectModeListener lsn) {
mListeners.add(lsn);
return this;
}
private void queryNetworkSelectionMode(String tag) {
mCacheOfModeStatus = mTelephonyManager.getNetworkSelectionMode();
Log.d(LOG_TAG, tag + ": query command done. mCacheOfModeStatus: " + mCacheOfModeStatus);
}
@VisibleForTesting
void updateUiAutoSelectValue(ServiceState status) {
if (status == null) {
return;
}
if (!mUpdatingConfig.get()) {
int networkSelectionMode = status.getIsManualSelection()
? TelephonyManager.NETWORK_SELECTION_MODE_MANUAL
: TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
if (mCacheOfModeStatus == networkSelectionMode) {
return;
}
mCacheOfModeStatus = networkSelectionMode;
Log.d(LOG_TAG, "updateUiAutoSelectValue: mCacheOfModeStatus: " + mCacheOfModeStatus);
mRecursiveUpdate.getAndIncrement();
updateState(mSwitchPreference);
mRecursiveUpdate.decrementAndGet();
updateListenerValue();
}
}
private void updateListenerValue() {
for (OnNetworkSelectModeListener lsn : mListeners) {
lsn.onNetworkSelectModeUpdated(mCacheOfModeStatus);
}
}
private void showAutoSelectProgressBar() {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setMessage(
mContext.getResources().getString(R.string.register_automatically));
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.setCancelable(false);
mProgressDialog.setIndeterminate(true);
}
mProgressDialog.show();
}
private void dismissProgressBar() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
try {
mProgressDialog.dismiss();
} catch (IllegalArgumentException e) {
// Ignore exception since the dialog will be gone anyway.
}
}
}
/**
* Callback when network select mode might get updated
*
* @see TelephonyManager#getNetworkSelectionMode()
*/
public interface OnNetworkSelectModeListener {
void onNetworkSelectModeUpdated(int mode);
}
}

View File

@@ -0,0 +1,224 @@
/*
* 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.gsm
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.os.PersistableBundle
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.Settings.NetworkSelectActivity
import com.android.settings.network.CarrierConfigCache
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.allowedNetworkTypesFlow
import com.android.settings.network.telephony.serviceStateFlow
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.framework.compose.OverridableFlow
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlin.properties.Delegates.notNull
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Preference controller for "Auto Select Network"
*/
class AutoSelectPreferenceController @JvmOverloads constructor(
context: Context,
key: String,
private val allowedNetworkTypesFlowFactory: (subId: Int) -> Flow<Long> =
context::allowedNetworkTypesFlow,
private val serviceStateFlowFactory: (subId: Int) -> Flow<ServiceState> =
context::serviceStateFlow,
private val getConfigForSubId: (subId: Int) -> PersistableBundle = { subId ->
CarrierConfigCache.getInstance(context).getConfigForSubId(subId)
},
) : ComposePreferenceController(context, key) {
private lateinit var telephonyManager: TelephonyManager
private val listeners = mutableListOf<OnNetworkSelectModeListener>()
@VisibleForTesting
var progressDialog: ProgressDialog? = null
private lateinit var preference: Preference
private var subId by notNull<Int>()
/**
* Initialization based on given subscription id.
*/
fun init(subId: Int): AutoSelectPreferenceController {
this.subId = subId
telephonyManager = mContext.getSystemService(TelephonyManager::class.java)!!
.createForSubscriptionId(subId)
return this
}
override fun getAvailabilityStatus() =
if (MobileNetworkUtils.shouldDisplayNetworkSelectOptions(mContext, subId)) AVAILABLE
else CONDITIONALLY_UNAVAILABLE
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)!!
}
@Composable
override fun Content() {
val coroutineScope = rememberCoroutineScope()
val serviceStateFlow = remember {
serviceStateFlowFactory(subId)
.stateIn(coroutineScope, SharingStarted.Lazily, null)
.filterNotNull()
}
val isAutoOverridableFlow = remember {
OverridableFlow(serviceStateFlow.map { !it.isManualSelection })
}
val isAuto by isAutoOverridableFlow.flow
.onEach(::updateListenerValue)
.collectAsStateWithLifecycle(initialValue = null)
val disallowedSummary by serviceStateFlow.map(::getDisallowedSummary)
.collectAsStateWithLifecycle(initialValue = "")
SwitchPreference(object : SwitchPreferenceModel {
override val title = stringResource(R.string.select_automatically)
override val summary = { disallowedSummary }
override val changeable = { disallowedSummary.isEmpty() }
override val checked = { isAuto }
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
if (newChecked) {
coroutineScope.launch { setAutomaticSelectionMode(isAutoOverridableFlow) }
} else {
mContext.startActivity(Intent().apply {
setClass(mContext, NetworkSelectActivity::class.java)
putExtra(Settings.EXTRA_SUB_ID, subId)
})
}
}
})
}
private suspend fun getDisallowedSummary(serviceState: ServiceState): String =
withContext(Dispatchers.Default) {
if (!serviceState.roaming && onlyAutoSelectInHome()) {
mContext.getString(
R.string.manual_mode_disallowed_summary,
telephonyManager.simOperatorName
)
} else ""
}
private fun onlyAutoSelectInHome(): Boolean =
getConfigForSubId(subId)
.getBoolean(CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL)
private suspend fun setAutomaticSelectionMode(overrideChannel: OverridableFlow<Boolean>) {
showAutoSelectProgressBar()
withContext(Dispatchers.Default) {
val minimumDialogTimeDeferred = async { delay(MINIMUM_DIALOG_TIME) }
telephonyManager.setNetworkSelectionModeAutomatic()
minimumDialogTimeDeferred.await()
}
overrideChannel.override(true)
dismissProgressBar()
}
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
allowedNetworkTypesFlowFactory(subId).collectLatestWithLifecycle(viewLifecycleOwner) {
preference.isVisible = withContext(Dispatchers.Default) {
MobileNetworkUtils.shouldDisplayNetworkSelectOptions(mContext, subId)
}
}
}
fun addListener(listener: OnNetworkSelectModeListener): AutoSelectPreferenceController {
listeners.add(listener)
return this
}
private fun updateListenerValue(isAuto: Boolean) {
for (listener in listeners) {
listener.onNetworkSelectModeUpdated(
if (isAuto) TelephonyManager.NETWORK_SELECTION_MODE_AUTO
else TelephonyManager.NETWORK_SELECTION_MODE_MANUAL
)
}
}
private fun showAutoSelectProgressBar() {
if (progressDialog == null) {
progressDialog = ProgressDialog(mContext).apply {
setMessage(mContext.resources.getString(R.string.register_automatically))
setCanceledOnTouchOutside(false)
setCancelable(false)
isIndeterminate = true
}
}
progressDialog?.show()
}
private fun dismissProgressBar() {
if (progressDialog?.isShowing == true) {
try {
progressDialog?.dismiss()
} catch (e: IllegalArgumentException) {
// Ignore exception since the dialog will be gone anyway.
}
}
}
/**
* Callback when network select mode might get updated
*
* @see TelephonyManager.getNetworkSelectionMode
*/
interface OnNetworkSelectModeListener {
fun onNetworkSelectModeUpdated(mode: Int)
}
companion object {
private val MINIMUM_DIALOG_TIME = 1.seconds
}
}

View File

@@ -0,0 +1,197 @@
/*
* 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.gsm
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.TelephonyManager
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.core.os.persistableBundleOf
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.Settings.NetworkSelectActivity
import com.android.settings.spa.preference.ComposePreference
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AutoSelectPreferenceControllerTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { simOperatorName } doReturn OPERATOR_NAME
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
doNothing().whenever(mock).startActivity(any())
}
private val preference = ComposePreference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val serviceState = ServiceState()
private val carrierConfig = persistableBundleOf()
private val controller = AutoSelectPreferenceController(
context = context,
key = TEST_KEY,
allowedNetworkTypesFlowFactory = { emptyFlow() },
serviceStateFlowFactory = { flowOf(serviceState) },
getConfigForSubId = { carrierConfig },
).init(subId = SUB_ID)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@Test
fun isChecked_isAutoSelection_on() {
serviceState.isManualSelection = false
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsOn()
}
@Test
fun isChecked_isManualSelection_off() {
serviceState.isManualSelection = true
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsOff()
}
@Test
fun isEnabled_isRoaming_enabled() {
serviceState.roaming = true
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsEnabled()
}
@Test
fun isEnabled_notOnlyAutoSelectInHome_enabled() {
serviceState.roaming = false
carrierConfig.putBoolean(
CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false
)
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsEnabled()
}
@Test
fun isEnabled_onlyAutoSelectInHome_notEnabled() {
serviceState.roaming = false
carrierConfig.putBoolean(
CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, true
)
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText("Unavailable when connected to T-mobile")
.assertIsNotEnabled()
}
@Test
fun onClick_turnOff_startNetworkSelectActivity() {
serviceState.isManualSelection = false
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onRoot().performClick()
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
assertThat(intent.component!!.className).isEqualTo(NetworkSelectActivity::class.java.name)
assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
}
@Test
fun onClick_turnOn_setNetworkSelectionModeAutomatic() = runBlocking {
serviceState.isManualSelection = true
controller.progressDialog = mock()
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onRoot().performClick()
delay(100)
verify(controller.progressDialog!!).show()
verify(mockTelephonyManager).setNetworkSelectionModeAutomatic()
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
const val OPERATOR_NAME = "T-mobile"
}
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright (C) 2020 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.gsm;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.network.CarrierConfigCache;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@RunWith(AndroidJUnit4.class)
public class AutoSelectPreferenceControllerTest {
private static final int SUB_ID = 2;
private static final String OPERATOR_NAME = "T-mobile";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private TelephonyManager mTelephonyManager;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private CarrierConfigCache mCarrierConfigCache;
@Mock
private ProgressDialog mProgressDialog;
@Mock
private ServiceState mTestServiceState;
@Mock
private Lifecycle mLifecycle;
private PersistableBundle mCarrierConfig;
private AutoSelectPreferenceController mController;
private SwitchPreference mSwitchPreference;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
CarrierConfigCache.setTestInstance(mContext, mCarrierConfigCache);
when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
mCarrierConfig = new PersistableBundle();
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL,
true);
when(mCarrierConfigCache.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfig);
mSwitchPreference = new SwitchPreference(mContext);
mController = new AutoSelectPreferenceController(mContext, "auto_select");
mController.mProgressDialog = mProgressDialog;
mController.mSwitchPreference = mSwitchPreference;
mController.init(mLifecycle, SUB_ID);
sleepAfterInit();
}
private void sleepAfterInit() {
try {
Thread.sleep(2000);
} catch (Exception e) {
fail("Sleep timeout " + e);
}
}
@Test
public void setChecked_isChecked_showProgressDialog() {
when(mTelephonyManager.getNetworkSelectionMode()).thenReturn(
TelephonyManager.NETWORK_SELECTION_MODE_AUTO);
// Wait for asynchronous thread to finish, otherwise test will flake.
Future thread = mController.setAutomaticSelectionMode();
try {
thread.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
fail("Exception during automatic selection");
}
verify(mProgressDialog).show();
verify(mTelephonyManager).setNetworkSelectionModeAutomatic();
}
@Test
public void updateState_isRoaming_enabled() {
when(mTelephonyManager.getServiceState()).thenReturn(mTestServiceState);
when(mTestServiceState.getRoaming()).thenReturn(true);
mController.updateState(mSwitchPreference);
assertThat(mSwitchPreference.isEnabled()).isTrue();
}
@Test
public void updateState_notRoamingWithAutoSelectOn_disabled() {
when(mTelephonyManager.getServiceState()).thenReturn(mTestServiceState);
when(mTestServiceState.getRoaming()).thenReturn(false);
doReturn(OPERATOR_NAME).when(mTelephonyManager).getSimOperatorName();
mController.updateState(mSwitchPreference);
assertThat(mSwitchPreference.isEnabled()).isFalse();
assertThat(mSwitchPreference.getSummary()).isEqualTo(
ResourcesUtils.getResourcesString(mContext, "manual_mode_disallowed_summary",
mTelephonyManager.getSimOperatorName()));
}
@Test
public void init_carrierConfigNull_shouldNotCrash() {
when(mCarrierConfigCache.getConfigForSubId(SUB_ID)).thenReturn(null);
// Should not crash
mController.init(mLifecycle, SUB_ID);
}
@Test
public void updateUiAutoSelectValue_serviceStateGetIsManualSelection_isCheckedFalse() {
when(mTelephonyManager.getNetworkSelectionMode()).thenReturn(
TelephonyManager.NETWORK_SELECTION_MODE_AUTO);
when(mTestServiceState.getIsManualSelection()).thenReturn(true);
mController.init(mLifecycle, SUB_ID);
sleepAfterInit();
mController.updateUiAutoSelectValue(mTestServiceState);
assertThat(mController.isChecked()).isFalse();
assertThat(mSwitchPreference.isChecked()).isFalse();
}
@Test
public void updateUiAutoSelectValue_serviceStateGetIsAutoSelection_isCheckedTrue() {
when(mTelephonyManager.getNetworkSelectionMode()).thenReturn(
TelephonyManager.NETWORK_SELECTION_MODE_MANUAL);
when(mTestServiceState.getIsManualSelection()).thenReturn(false);
mController.init(mLifecycle, SUB_ID);
sleepAfterInit();
mController.updateUiAutoSelectValue(mTestServiceState);
assertThat(mController.isChecked()).isTrue();
assertThat(mSwitchPreference.isChecked()).isTrue();
}
}