android:title="@string/roaming"
android:persistent="false"
android:summaryOn="@string/roaming_enable"
- android:summaryOff="@string/roaming_disable"/>
+ android:summaryOff="@string/roaming_disable"
+ settings:userRestriction="no_data_roaming"
+ settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
<Preference
android:key="data_usage_summary"
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class MobileNetworkFragment extends DashboardFragment implements
- Preference.OnPreferenceChangeListener, RoamingDialogFragment.RoamingDialogListener {
+ Preference.OnPreferenceChangeListener {
// debug data
private static final String LOG_TAG = "NetworkSettings";
//UI objects
private ListPreference mButtonPreferredNetworkMode;
private ListPreference mButtonEnabledNetworks;
- private RestrictedSwitchPreference mButtonDataRoam;
private SwitchPreference mButton4glte;
private Preference mLteDataServicePref;
private Preference mEuiccSettingsPref;
return 0;
}
- @Override
- public void onPositiveButtonClick(androidx.fragment.app.DialogFragment dialog) {
- mTelephonyManager.setDataRoamingEnabled(true);
- mButtonDataRoam.setChecked(true);
- MetricsLogger.action(getContext(),
- getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
- true);
- }
-
/**
* Invoked on each preference click in this hierarchy, overrides
* PreferenceActivity's implementation. Used to make sure we track the
preferredNetworkMode);
mButtonEnabledNetworks.setValue(Integer.toString(settingsNetworkMode));
return true;
- } else if (preference == mButtonDataRoam) {
- // Do not disable the preference screen if the user clicks Data roaming.
- return true;
} else if (preference == mEuiccSettingsPref) {
Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS);
startActivity(intent);
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
+ use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId);
use(CdmaApnPreferenceController.class).init(mSubId);
use(CarrierPreferenceController.class).init(mSubId);
use(DataUsagePreferenceController.class).init(mSubId);
//get UI object references
PreferenceScreen prefSet = getPreferenceScreen();
- mButtonDataRoam = (RestrictedSwitchPreference) prefSet.findPreference(
- BUTTON_ROAMING_KEY);
mButtonPreferredNetworkMode = (ListPreference) prefSet.findPreference(
BUTTON_PREFERED_NETWORK_MODE);
mButtonEnabledNetworks = (ListPreference) prefSet.findPreference(
BUTTON_ENABLED_NETWORKS_KEY);
- mButtonDataRoam.setOnPreferenceChangeListener(this);
mLteDataServicePref = prefSet.findPreference(BUTTON_CDMA_LTE_DATA_SERVICE_KEY);
// preferences.
getPreferenceScreen().setEnabled(true);
- // Set UI state in onResume because a user could go home, launch some
- // app to change this setting's backend, and re-launch this settings app
- // and the UI state would be inconsistent with actual state
- mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled());
-
if (getPreferenceScreen().findPreference(BUTTON_PREFERED_NETWORK_MODE) != null
|| getPreferenceScreen().findPreference(BUTTON_ENABLED_NETWORKS_KEY) != null) {
updatePreferredNetworkUIFromDb();
// android.R.id.home will be triggered in onOptionsItemSelected()
actionBar.setDisplayHomeAsUpEnabled(true);
}
-
- prefSet.addPreference(mButtonDataRoam);
-
- mButtonDataRoam.setEnabled(hasActiveSubscriptions);
-
- if (hasActiveSubscriptions) {
-
- // Initialize states of mButtonDataRoam.
- mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled());
- if (mButtonDataRoam.isEnabled()) {
- if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context,
- UserManager.DISALLOW_DATA_ROAMING, UserHandle.myUserId())) {
- mButtonDataRoam.setEnabled(false);
- } else {
- mButtonDataRoam.checkRestrictionAndSetDisabled(
- UserManager.DISALLOW_DATA_ROAMING);
- }
- }
- }
}
private void updateBody() {
boolean enhanced4gMode = !mButton4glte.isChecked();
mButton4glte.setChecked(enhanced4gMode);
mImsMgr.setEnhanced4gLteModeSetting(mButton4glte.isChecked());
- } else if (preference == mButtonDataRoam) {
- if (DBG) log("onPreferenceTreeClick: preference == mButtonDataRoam.");
-
- //normally called on the toggle click
- if (!mButtonDataRoam.isChecked()) {
- PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
- mSubId);
- if (carrierConfig != null && carrierConfig.getBoolean(
- CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)) {
- mTelephonyManager.setDataRoamingEnabled(true);
- MetricsLogger.action(getContext(),
- getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
- true);
- } else {
- // MetricsEvent with no value update.
- MetricsLogger.action(getContext(),
- getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam));
- // First confirm with a warning dialog about charges
- mOkClicked = false;
- RoamingDialogFragment
- fragment = new RoamingDialogFragment();
- Bundle b = new Bundle();
- b.putInt(RoamingDialogFragment.SUB_ID_KEY, mSubId);
- fragment.setArguments(b);
- fragment.setTargetFragment(this, 0 /* requestCode */);
- fragment.show(getFragmentManager(), ROAMING_TAG);
- // Don't update the toggle unless the confirm button is actually pressed.
- return false;
- }
- } else {
- mTelephonyManager.setDataRoamingEnabled(false);
- MetricsLogger.action(getContext(),
- getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
- false);
- return true;
- }
} else if (preference == mVideoCallingPref) {
// If mButton4glte is not checked, mVideoCallingPref should be disabled.
// So it only makes sense to call phoneMgr.enableVideoCalling if it's checked.
if (preference == null) {
return MetricsProto.MetricsEvent.VIEW_UNKNOWN;
- } else if (preference == mButtonDataRoam) {
- return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE;
} else if (preference == mLteDataServicePref) {
return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE;
} else if (preference == mButton4glte) {
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
-
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
private CarrierConfigManager mCarrierConfigManager;
private int mSubId;
- /**
- * The interface we expect a host activity to implement.
- */
- public interface RoamingDialogListener {
- void onPositiveButtonClick(DialogFragment dialog);
- }
+ public static RoamingDialogFragment newInstance(int subId) {
+ final RoamingDialogFragment dialogFragment = new RoamingDialogFragment();
+ Bundle args = new Bundle();
+ args.putInt(SUB_ID_KEY, subId);
+ dialogFragment.setArguments(args);
- // the host activity which implements the listening interface
- private RoamingDialogListener mListener;
+ return dialogFragment;
+ }
@Override
public void onAttach(Context context) {
Bundle args = getArguments();
mSubId = args.getInt(SUB_ID_KEY);
mCarrierConfigManager = new CarrierConfigManager(context);
-
- Fragment fragment = getTargetFragment();
- try {
- mListener = (RoamingDialogListener) fragment;
- } catch (ClassCastException e) {
- throw new ClassCastException(fragment.toString() +
- "must implement RoamingDialogListener");
- }
}
@Override
public void onClick(DialogInterface dialog, int which) {
// let the host know that the positive button has been clicked
if (which == dialog.BUTTON_POSITIVE) {
- mListener.onPositiveButtonClick(this);
+ TelephonyManager.from(getContext()).createForSubscriptionId(
+ mSubId).setDataRoamingEnabled(true);
}
}
}
--- /dev/null
+/*
+ * 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.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Preference controller for "Roaming"
+ */
+public class RoamingPreferenceController extends TogglePreferenceController implements
+ LifecycleObserver, OnStart, OnStop {
+
+ private static final String DIALOG_TAG = "MobileDataDialog";
+
+ private RestrictedSwitchPreference mSwitchPreference;
+ private TelephonyManager mTelephonyManager;
+ private CarrierConfigManager mCarrierConfigManager;
+ private int mSubId;
+ private DataContentObserver mDataContentObserver;
+ @VisibleForTesting
+ boolean mNeedDialog;
+ @VisibleForTesting
+ FragmentManager mFragmentManager;
+
+ public RoamingPreferenceController(Context context, String key) {
+ super(context, key);
+ mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
+ mDataContentObserver = new DataContentObserver(new Handler(Looper.getMainLooper()));
+ }
+
+ @Override
+ public void onStart() {
+ mDataContentObserver.register(mContext, mSubId);
+ }
+
+ @Override
+ public void onStop() {
+ mDataContentObserver.unRegister(mContext);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mSwitchPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ ? AVAILABLE
+ : AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ if (mNeedDialog) {
+ showDialog();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ mNeedDialog = isDialogNeeded();
+
+ if (!mNeedDialog) {
+ // Update data directly if we don't need dialog
+ mTelephonyManager.setDataRoamingEnabled(isChecked);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final RestrictedSwitchPreference switchPreference = (RestrictedSwitchPreference) preference;
+ switchPreference.setEnabled(mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ switchPreference.setChecked(isChecked());
+ }
+
+ @VisibleForTesting
+ boolean isDialogNeeded() {
+ final boolean isRoamingEnabled = mTelephonyManager.isDataRoamingEnabled();
+ final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
+ mSubId);
+
+ // Need dialog if we need to turn on roaming and the roaming charge indication is allowed
+ if (!isRoamingEnabled && (carrierConfig == null || !carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mTelephonyManager.isDataRoamingEnabled();
+ }
+
+ public void init(FragmentManager fragmentManager, int subId) {
+ mFragmentManager = fragmentManager;
+ mSubId = subId;
+ mTelephonyManager = TelephonyManager.from(mContext).createForSubscriptionId(mSubId);
+ }
+
+ private void showDialog() {
+ final RoamingDialogFragment dialogFragment = RoamingDialogFragment.newInstance(mSubId);
+
+ dialogFragment.show(mFragmentManager, DIALOG_TAG);
+ }
+
+ /**
+ * Listener that listens data roaming change
+ */
+ public class DataContentObserver extends ContentObserver {
+
+ public DataContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ updateState(mSwitchPreference);
+ }
+
+ public void register(Context context, int subId) {
+ Uri uri = Settings.Global.getUriFor(Settings.Global.DATA_ROAMING);
+ if (TelephonyManager.getDefault().getSimCount() != 1) {
+ uri = Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + subId);
+ }
+ context.getContentResolver().registerContentObserver(uri, false, this);
+
+ }
+
+ public void unRegister(Context context) {
+ context.getContentResolver().unregisterContentObserver(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class RoamingPreferenceControllerTest {
+ private static final int SUB_ID = 2;
+
+ @Mock
+ private FragmentManager mFragmentManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private TelephonyManager mInvalidTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private FragmentTransaction mFragmentTransaction;
+ @Mock
+ private CarrierConfigManager mCarrierConfigManager;
+
+ private RoamingPreferenceController mController;
+ private RestrictedSwitchPreference mPreference;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+ doReturn(mSubscriptionManager).when(mContext).getSystemService(SubscriptionManager.class);
+ doReturn(mCarrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+ doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
+
+ mPreference = new RestrictedSwitchPreference(mContext);
+ mController = new RoamingPreferenceController(mContext, "roaming");
+ mController.init(mFragmentManager, 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(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE_UNSEARCHABLE);
+ }
+
+ @Test
+ public void isDialogNeeded_roamingDisabledWithoutFlag_returnTrue() {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
+ doReturn(false).when(mTelephonyManager).isDataRoamingEnabled();
+
+ assertThat(mController.isDialogNeeded()).isTrue();
+ }
+
+ @Test
+ public void isDialogNeeded_roamingEnabled_returnFalse() {
+ doReturn(true).when(mTelephonyManager).isDataRoamingEnabled();
+
+ assertThat(mController.isDialogNeeded()).isFalse();
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_needDialog_showDialog() {
+ mController.mNeedDialog = true;
+
+ mController.handlePreferenceTreeClick(mPreference);
+
+ verify(mFragmentManager).beginTransaction();
+ }
+
+ @Test
+ public void updateState_invalidSubId_disabled() {
+ mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void updateState_validSubId_enabled() {
+ doReturn(true).when(mTelephonyManager).isDataRoamingEnabled();
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.isChecked()).isTrue();
+ }
+
+}