Add toggle for enabling/diabling mobile data
Bug: 117229760
Test: Build, Robolectric
Change-Id: I04b92b1f36e3fe92fb92851b2ed17e3cd7dcefb8
diff --git a/res/values/preference_keys.xml b/res/values/preference_keys.xml
index 8c7ffa6..b5e0651 100644
--- a/res/values/preference_keys.xml
+++ b/res/values/preference_keys.xml
@@ -43,6 +43,7 @@
<!-- Network -->
<string name="pk_mobile_network_settings_entry" translatable="false">mobile_network_settings_entry</string>
+ <string name="pk_mobile_data_toggle" translatable="false">mobile_data_toggle</string>
<string name="pk_data_usage_settings_entry" translatable="false">data_usage_settings_entry</string>
<!-- WIFI -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0ed3be3..10a5349 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -39,6 +39,12 @@
<string name="network_and_internet">Network & internet</string>
<!-- Mobile network settings [CHAR LIMIT=30] -->
<string name="mobile_network_settings">Mobile network</string>
+ <!-- Mobile network screen, toggle mobile data title [CHAR LIMIT=30] -->
+ <string name="mobile_network_toggle_title">Mobile data</string>
+ <!-- Mobile network screen, toggle mobile data summary [CHAR LIMIT=50] -->
+ <string name="mobile_network_toggle_summary">Access data using mobile network</string>
+ <!-- Mobile network screen, confirmation dialog to turn off mobile data [CHAR LIMIT=50] -->
+ <string name="confirm_mobile_data_disable">Turn off mobile data?</string>
<!-- Data usage settings [CHAR LIMIT=30] -->
<string name="data_usage_settings">Data usage</string>
diff --git a/res/xml/mobile_network_fragment.xml b/res/xml/mobile_network_fragment.xml
index 77253a7..675ecb0 100644
--- a/res/xml/mobile_network_fragment.xml
+++ b/res/xml/mobile_network_fragment.xml
@@ -17,4 +17,11 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/mobile_network_settings"/>
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/mobile_network_settings">
+ <SwitchPreference
+ android:key="@string/pk_mobile_data_toggle"
+ android:summary="@string/mobile_network_toggle_summary"
+ android:title="@string/mobile_network_toggle_title"
+ settings:controller="com.android.car.settings.network.MobileDataTogglePreferenceController"/>
+</PreferenceScreen>
diff --git a/src/com/android/car/settings/network/ConfirmMobileDataDisableDialog.java b/src/com/android/car/settings/network/ConfirmMobileDataDisableDialog.java
new file mode 100644
index 0000000..f0898f8
--- /dev/null
+++ b/src/com/android/car/settings/network/ConfirmMobileDataDisableDialog.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 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.car.settings.network;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+
+import com.android.car.settings.R;
+
+/** Dialog to confirm disabling mobile data. */
+public class ConfirmMobileDataDisableDialog extends DialogFragment implements
+ DialogInterface.OnClickListener {
+
+ /**
+ * Tag used to open and identify the dialog fragment from the FragmentManager or
+ * FragmentController.
+ */
+ public static final String TAG = "ConfirmMobileDataDisableDialog";
+
+ private ConfirmMobileDataDisableListener mListener;
+
+ /**
+ * Sets a listener which will determine how to handle the user action when they press ok or
+ * cancel.
+ */
+ public void setListener(ConfirmMobileDataDisableListener listener) {
+ mListener = listener;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getContext())
+ .setMessage(R.string.confirm_mobile_data_disable)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mListener != null) {
+ mListener.onMobileDataDisableConfirmed();
+ }
+ }
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ if (mListener != null) {
+ mListener.onMobileDataDisableRejected();
+ }
+ }
+ }
+
+ /**
+ * Interface for listeners that want to receive a callback when user confirms to disable mobile
+ * data.
+ */
+ public interface ConfirmMobileDataDisableListener {
+ /**
+ * Method called only when user presses confirm button.
+ */
+ void onMobileDataDisableConfirmed();
+
+ /**
+ * Method called only when user presses cancel button.
+ */
+ void onMobileDataDisableRejected();
+ }
+}
diff --git a/src/com/android/car/settings/network/MobileDataTogglePreferenceController.java b/src/com/android/car/settings/network/MobileDataTogglePreferenceController.java
new file mode 100644
index 0000000..ebb9f36
--- /dev/null
+++ b/src/com/android/car/settings/network/MobileDataTogglePreferenceController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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.car.settings.network;
+
+import android.car.drivingstate.CarUxRestrictions;
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.TwoStatePreference;
+
+import com.android.car.settings.common.FragmentController;
+import com.android.car.settings.common.PreferenceController;
+
+/**
+ * Business logic to control the toggle that enables/disables usage of mobile data. Does not have
+ * support for multi-sim.
+ */
+public class MobileDataTogglePreferenceController extends
+ PreferenceController<TwoStatePreference> implements
+ ConfirmMobileDataDisableDialog.ConfirmMobileDataDisableListener {
+
+ private TelephonyManager mTelephonyManager;
+
+ public MobileDataTogglePreferenceController(Context context, String preferenceKey,
+ FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
+ super(context, preferenceKey, fragmentController, uxRestrictions);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @Override
+ protected Class<TwoStatePreference> getPreferenceType() {
+ return TwoStatePreference.class;
+ }
+
+ @Override
+ protected void onCreateInternal() {
+ ConfirmMobileDataDisableDialog dialog =
+ (ConfirmMobileDataDisableDialog) getFragmentController().findDialogByTag(
+ ConfirmMobileDataDisableDialog.TAG);
+ if (dialog != null) {
+ dialog.setListener(this);
+ }
+ }
+
+ @Override
+ protected void updateState(TwoStatePreference preference) {
+ preference.setChecked(mTelephonyManager.isDataEnabled());
+ }
+
+ @Override
+ protected boolean handlePreferenceChanged(TwoStatePreference preference, Object newValue) {
+ boolean newToggleValue = (Boolean) newValue;
+ if (!newToggleValue) {
+ ConfirmMobileDataDisableDialog dialog = new ConfirmMobileDataDisableDialog();
+ dialog.setListener(this);
+ getFragmentController().showDialog(dialog, ConfirmMobileDataDisableDialog.TAG);
+ } else {
+ setMobileDataEnabled(true);
+ }
+ return false;
+ }
+
+ @Override
+ public void onMobileDataDisableConfirmed() {
+ setMobileDataEnabled(false);
+ }
+
+ @Override
+ public void onMobileDataDisableRejected() {
+ refreshUi();
+ }
+
+ private void setMobileDataEnabled(boolean enabled) {
+ mTelephonyManager.setDataEnabled(enabled);
+ refreshUi();
+ }
+}
diff --git a/tests/robotests/src/com/android/car/settings/network/ConfirmMobileDataDisableDialogTest.java b/tests/robotests/src/com/android/car/settings/network/ConfirmMobileDataDisableDialogTest.java
new file mode 100644
index 0000000..8cd7370
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/network/ConfirmMobileDataDisableDialogTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.car.settings.network;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+import com.android.car.settings.testutils.BaseTestActivity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.shadows.ShadowDialog;
+
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class ConfirmMobileDataDisableDialogTest {
+
+ private ConfirmMobileDataDisableDialog mDialog;
+ @Mock
+ private ConfirmMobileDataDisableDialog.ConfirmMobileDataDisableListener mListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDialog = new ConfirmMobileDataDisableDialog();
+ mDialog.setListener(mListener);
+ }
+
+ @Test
+ public void confirmDisable_disableCallbackCalled() {
+ AlertDialog dialog = showDialog(mDialog);
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+
+ verify(mListener).onMobileDataDisableConfirmed();
+ }
+
+ @Test
+ public void rejectDisable_rejectCallbackCalled() {
+ AlertDialog dialog = showDialog(mDialog);
+ dialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
+
+ verify(mListener).onMobileDataDisableRejected();
+ }
+
+ private AlertDialog showDialog(ConfirmMobileDataDisableDialog fragment) {
+ BaseTestActivity activity = Robolectric.setupActivity(BaseTestActivity.class);
+ activity.showDialog(fragment, /* tag= */ null);
+ return (AlertDialog) ShadowDialog.getLatestDialog();
+ }
+}
diff --git a/tests/robotests/src/com/android/car/settings/network/MobileDataTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/car/settings/network/MobileDataTogglePreferenceControllerTest.java
new file mode 100644
index 0000000..7329d79
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/network/MobileDataTogglePreferenceControllerTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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.car.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.preference.SwitchPreference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+import com.android.car.settings.common.PreferenceControllerTestHelper;
+import com.android.car.settings.testutils.ShadowTelephonyManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(CarSettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowTelephonyManager.class})
+public class MobileDataTogglePreferenceControllerTest {
+
+ private Context mContext;
+ private TwoStatePreference mPreference;
+ private PreferenceControllerTestHelper<MobileDataTogglePreferenceController>
+ mControllerHelper;
+ private MobileDataTogglePreferenceController mController;
+ private TelephonyManager mTelephonyManager;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mPreference = new SwitchPreference(mContext);
+ mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
+ MobileDataTogglePreferenceController.class, mPreference);
+ mController = mControllerHelper.getController();
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+
+ mTelephonyManager.setDataEnabled(false);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowTelephonyManager.reset();
+ }
+
+ @Test
+ public void refreshUi_dataEnabled_setChecked() {
+ mTelephonyManager.setDataEnabled(true);
+ mController.refreshUi();
+
+ assertThat(mPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void refreshUi_dataDisabled_setUnchecked() {
+ mTelephonyManager.setDataEnabled(false);
+ mController.refreshUi();
+
+ assertThat(mPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void handlePreferenceChanged_setFalse_opensDialog() {
+ mPreference.callChangeListener(false);
+
+ verify(mControllerHelper.getMockFragmentController()).showDialog(
+ any(ConfirmMobileDataDisableDialog.class), eq(ConfirmMobileDataDisableDialog.TAG));
+ }
+
+ @Test
+ public void handlePreferenceChanged_setTrue_enablesData() {
+ mPreference.callChangeListener(true);
+
+ assertThat(mTelephonyManager.isDataEnabled()).isTrue();
+ }
+
+ @Test
+ public void handlePreferenceChanged_setTrue_checksSwitch() {
+ mPreference.setChecked(false);
+ mPreference.callChangeListener(true);
+
+ assertThat(mPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void onMobileDataDisableConfirmed_unchecksSwitch() {
+ mTelephonyManager.setDataEnabled(true);
+ mPreference.setChecked(true);
+
+ mController.onMobileDataDisableConfirmed();
+
+ assertThat(mPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void onMobileDataDisableConfirmed_disablesMobileData() {
+ mTelephonyManager.setDataEnabled(true);
+
+ mController.onMobileDataDisableConfirmed();
+
+ assertThat(mTelephonyManager.isDataEnabled()).isFalse();
+ }
+
+ @Test
+ public void onMobileDataDisableRejected_checksSwitch() {
+ mTelephonyManager.setDataEnabled(true);
+ mPreference.setChecked(false);
+
+ mController.onMobileDataDisableRejected();
+
+ assertThat(mPreference.isChecked()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/car/settings/testutils/ShadowTelephonyManager.java b/tests/robotests/src/com/android/car/settings/testutils/ShadowTelephonyManager.java
index 60bd993..7e9decd 100644
--- a/tests/robotests/src/com/android/car/settings/testutils/ShadowTelephonyManager.java
+++ b/tests/robotests/src/com/android/car/settings/testutils/ShadowTelephonyManager.java
@@ -37,6 +37,7 @@
public static final String SUBSCRIBER_ID = "test_id";
private static Map<Integer, Integer> sSubIdsWithResetCalledCount = new HashMap<>();
private final Map<PhoneStateListener, Integer> mPhoneStateRegistrations = new HashMap<>();
+ private boolean mIsDataEnabled = false;
public static boolean verifyFactoryResetCalled(int subId, int numTimes) {
if (!sSubIdsWithResetCalledCount.containsKey(subId)) return false;
@@ -65,6 +66,16 @@
}
@Implementation
+ protected void setDataEnabled(boolean enable) {
+ mIsDataEnabled = enable;
+ }
+
+ @Implementation
+ protected boolean isDataEnabled() {
+ return mIsDataEnabled;
+ }
+
+ @Implementation
protected void factoryReset(int subId) {
sSubIdsWithResetCalledCount.put(subId,
sSubIdsWithResetCalledCount.getOrDefault(subId, 0) + 1);