diff --git a/res/xml/reset_dashboard_fragment.xml b/res/xml/reset_dashboard_fragment.xml
index 08852c92c86..cd1c671477b 100644
--- a/res/xml/reset_dashboard_fragment.xml
+++ b/res/xml/reset_dashboard_fragment.xml
@@ -30,11 +30,8 @@
android:fragment="com.android.settings.ResetNetwork" />
-
diff --git a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java
deleted file mode 100644
index f0f5d732c2a..00000000000
--- a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2022 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;
-
-import android.app.ProgressDialog;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.ResetNetworkRequest;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * This is to show a preference regarding resetting Bluetooth and Wi-Fi.
- */
-public class BluetoothWiFiResetPreferenceController extends BasePreferenceController
- implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
-
- private static final String TAG = "BtWiFiResetPreferenceController";
-
- private final NetworkResetRestrictionChecker mRestrictionChecker;
-
- private DialogInterface mResetDialog;
- private ProgressDialog mProgressDialog;
- private ExecutorService mExecutorService;
-
- /**
- * Constructer.
- * @param context Context
- * @param preferenceKey is the key for Preference
- */
- public BluetoothWiFiResetPreferenceController(Context context, String preferenceKey) {
- super(context, preferenceKey);
-
- // restriction check
- mRestrictionChecker = new NetworkResetRestrictionChecker(context);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return mRestrictionChecker.hasUserRestriction() ?
- CONDITIONALLY_UNAVAILABLE : AVAILABLE;
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
- return false;
- }
- buildResetDialog(preference);
- return true;
- }
-
- /**
- * This is a pop-up dialog showing detail of this reset option.
- */
- void buildResetDialog(Preference preference) {
- if (mResetDialog != null) {
- return;
- }
- mResetDialog = new AlertDialog.Builder(mContext)
- .setTitle(R.string.reset_bluetooth_wifi_title)
- .setMessage(R.string.reset_bluetooth_wifi_desc)
- .setPositiveButton(R.string.reset_bluetooth_wifi_button_text, this)
- .setNegativeButton(R.string.cancel, null /* OnClickListener */)
- .setOnDismissListener(this)
- .show();
- }
-
- public void onDismiss(DialogInterface dialog) {
- if (mResetDialog == dialog) {
- mResetDialog = null;
- }
- }
-
- /**
- * User pressed confirmation button, for starting reset operation.
- */
- public void onClick(DialogInterface dialog, int which) {
- if (mResetDialog != dialog) {
- return;
- }
-
- // User confirm the reset operation
- MetricsFeatureProvider provider = FeatureFactory.getFeatureFactory()
- .getMetricsFeatureProvider();
- provider.action(mContext, SettingsEnums.RESET_BLUETOOTH_WIFI_CONFIRM, true);
-
- // Non-cancelable progress dialog
- mProgressDialog = getProgressDialog(mContext);
- mProgressDialog.show();
-
- // Run reset in background thread
- mExecutorService = Executors.newSingleThreadExecutor();
- mExecutorService.execute(() -> {
- final AtomicReference exceptionDuringReset =
- new AtomicReference();
- try {
- resetOperation().run();
- } catch (Exception exception) {
- exceptionDuringReset.set(exception);
- }
- mContext.getMainExecutor().execute(() -> endOfReset(exceptionDuringReset.get()));
- });
- }
-
- @VisibleForTesting
- protected ProgressDialog getProgressDialog(Context context) {
- final ProgressDialog progressDialog = new ProgressDialog(context);
- progressDialog.setIndeterminate(true);
- progressDialog.setCancelable(false);
- progressDialog.setMessage(
- context.getString(R.string.main_clear_progress_text));
- return progressDialog;
- }
-
- @VisibleForTesting
- protected Runnable resetOperation() throws Exception {
- if (SubscriptionUtil.isSimHardwareVisible(mContext)) {
- return new ResetNetworkRequest(
- ResetNetworkRequest.RESET_WIFI_MANAGER |
- ResetNetworkRequest.RESET_WIFI_P2P_MANAGER |
- ResetNetworkRequest.RESET_BLUETOOTH_MANAGER)
- .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
- .build();
- }
-
- /**
- * For device without SIMs visible to the user
- */
- return new ResetNetworkRequest(
- ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER |
- ResetNetworkRequest.RESET_VPN_MANAGER |
- ResetNetworkRequest.RESET_WIFI_MANAGER |
- ResetNetworkRequest.RESET_WIFI_P2P_MANAGER |
- ResetNetworkRequest.RESET_BLUETOOTH_MANAGER)
- .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
- .resetTelephonyAndNetworkPolicyManager(ResetNetworkRequest.ALL_SUBSCRIPTION_ID)
- .build();
- }
-
- @VisibleForTesting
- protected void endOfReset(Exception exceptionDuringReset) {
- if (mExecutorService != null) {
- mExecutorService.shutdown();
- mExecutorService = null;
- }
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
- if (exceptionDuringReset == null) {
- Toast.makeText(mContext, R.string.reset_bluetooth_wifi_complete_toast,
- Toast.LENGTH_SHORT).show();
- } else {
- Log.e(TAG, "Exception during reset", exceptionDuringReset);
- }
- }
-}
diff --git a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt
new file mode 100644
index 00000000000..2047ed92e68
--- /dev/null
+++ b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt
@@ -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
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Looper
+import android.os.UserManager
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.ResetNetworkRequest
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settings.spa.preference.ComposePreferenceController
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * This is to show a preference regarding resetting Bluetooth and Wi-Fi.
+ */
+class BluetoothWiFiResetPreferenceController(context: Context, preferenceKey: String) :
+ ComposePreferenceController(context, preferenceKey) {
+
+ private val restrictionChecker = NetworkResetRestrictionChecker(context)
+
+ override fun getAvailabilityStatus() =
+ if (restrictionChecker.hasUserRestriction()) CONDITIONALLY_UNAVAILABLE else AVAILABLE
+
+ @Composable
+ override fun Content() {
+ val coroutineScope = rememberCoroutineScope()
+ val dialogPresenter = rememberAlertDialogPresenter(
+ confirmButton = AlertDialogButton(
+ text = stringResource(R.string.reset_bluetooth_wifi_button_text),
+ ) { reset(coroutineScope) },
+ dismissButton = AlertDialogButton(text = stringResource(R.string.cancel)),
+ title = stringResource(R.string.reset_bluetooth_wifi_title),
+ ) {
+ Text(stringResource(R.string.reset_bluetooth_wifi_desc))
+ }
+
+ RestrictedPreference(
+ model = object : PreferenceModel {
+ override val title = stringResource(R.string.reset_bluetooth_wifi_title)
+ override val onClick = dialogPresenter::open
+ },
+ restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_NETWORK_RESET)),
+ )
+ }
+
+ /**
+ * User pressed confirmation button, for starting reset operation.
+ */
+ private fun reset(coroutineScope: CoroutineScope) {
+ // User confirm the reset operation
+ featureFactory.metricsFeatureProvider
+ .action(mContext, SettingsEnums.RESET_BLUETOOTH_WIFI_CONFIRM, true)
+
+ // Run reset in background thread
+ coroutineScope.launch {
+ try {
+ withContext(Dispatchers.Default) {
+ resetOperation().run()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception during reset", e)
+ return@launch
+ }
+ Toast.makeText(
+ mContext,
+ R.string.reset_bluetooth_wifi_complete_toast,
+ Toast.LENGTH_SHORT,
+ ).show()
+ }
+ }
+
+ @VisibleForTesting
+ fun resetOperation(): Runnable = if (SubscriptionUtil.isSimHardwareVisible(mContext)) {
+ ResetNetworkRequest(
+ ResetNetworkRequest.RESET_WIFI_MANAGER or
+ ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
+ ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
+ )
+ .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
+ } else { // For device without SIMs visible to the user
+ ResetNetworkRequest(
+ ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER or
+ ResetNetworkRequest.RESET_VPN_MANAGER or
+ ResetNetworkRequest.RESET_WIFI_MANAGER or
+ ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
+ ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
+ )
+ .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
+ .resetTelephonyAndNetworkPolicyManager(ResetNetworkRequest.ALL_SUBSCRIPTION_ID)
+ }.build()
+
+ private companion object {
+ private const val TAG = "BluetoothWiFiResetPref"
+ }
+}
diff --git a/src/com/android/settings/spa/app/ResetAppPreferences.kt b/src/com/android/settings/spa/app/ResetAppPreferences.kt
index 12dd7090ea1..34c4145e03f 100644
--- a/src/com/android/settings/spa/app/ResetAppPreferences.kt
+++ b/src/com/android/settings/spa/app/ResetAppPreferences.kt
@@ -16,7 +16,6 @@
package com.android.settings.spa.app
-import android.os.UserHandle
import android.os.UserManager
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -37,10 +36,7 @@ fun MoreOptionsScope.ResetAppPreferences(onClick: () -> Unit) {
RestrictedMenuItem(
text = stringResource(R.string.reset_app_preferences),
restrictions = remember {
- Restrictions(
- userId = UserHandle.myUserId(),
- keys = listOf(UserManager.DISALLOW_APPS_CONTROL),
- )
+ Restrictions(keys = listOf(UserManager.DISALLOW_APPS_CONTROL))
},
onClick = onClick,
)
diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt
index 0c9abad2d26..aec85a9b324 100644
--- a/src/com/android/settings/spa/preference/ComposePreference.kt
+++ b/src/com/android/settings/spa/preference/ComposePreference.kt
@@ -40,6 +40,8 @@ class ComposePreference @JvmOverloads constructor(
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
+ holder.isDividerAllowedAbove = false
+ holder.isDividerAllowedBelow = false
(holder.itemView as ComposeView).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
diff --git a/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java
deleted file mode 100644
index 3aea4a87cb7..00000000000
--- a/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 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;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.os.UserManager;
-import android.telephony.TelephonyManager;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowToast;
-
-@RunWith(RobolectricTestRunner.class)
-public class BluetoothWiFiResetPreferenceControllerTest {
-
- private static final String PREFERENCE_KEY = "network_reset_bluetooth_wifi_pref";
-
- @Mock
- private UserManager mUserManager;
- @Mock
- private Resources mResources;
- @Mock
- private ConnectivityManager mConnectivityManager;
- @Mock
- private TelephonyManager mTelephonyManager;
-
- private Context mContext;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
- when(mContext.getResources()).thenReturn(mResources);
-
- mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
- mConnectivityManager);
- mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager);
- }
-
- @Test
- public void getAvailabilityStatus_returnAvailable_asOwnerUser() {
- mockService(Context.USER_SERVICE, UserManager.class, mUserManager);
- doReturn(true).when(mUserManager).isAdminUser();
-
- BluetoothWiFiResetPreferenceController target =
- new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
- assertThat(target.getAvailabilityStatus()).isEqualTo(
- BluetoothWiFiResetPreferenceController.AVAILABLE);
- }
-
- @Test
- public void resetOperation_notResetConnectivity_onDeviceWithSimVisible() {
- mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
- mConnectivityManager);
- when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
- BluetoothWiFiResetPreferenceController target =
- new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
- try {
- target.resetOperation().run();
- } catch (Exception exception) {}
- verify(mConnectivityManager, never()).factoryReset();
- }
-
- @Test
- public void endOfReset_toastMessage_whenSuccess() {
- String testText = "reset_bluetooth_wifi_complete_toast";
- when(mResources.getString(R.string.reset_bluetooth_wifi_complete_toast)).thenReturn(testText);
- BluetoothWiFiResetPreferenceController target =
- new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
- target.endOfReset(null);
-
- assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(testText);
- }
-
- private void mockService(String serviceName, Class serviceClass, T service) {
- when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
- when(mContext.getSystemService(serviceName)).thenReturn(service);
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt
new file mode 100644
index 00000000000..210a0c7457c
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager
+import android.net.NetworkPolicyManager
+import android.net.VpnManager
+import android.os.UserManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class BluetoothWiFiResetPreferenceControllerTest {
+
+ private val mockUserManager = mock()
+ private val mockBluetoothAdapter = mock()
+ private val mockBluetoothManager = mock {
+ on { adapter } doReturn mockBluetoothAdapter
+ }
+ private val mockConnectivityManager = mock()
+ private val mockNetworkPolicyManager = mock()
+ private val mockVpnManager = mock()
+ private val mockResources = mock()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(Context.USER_SERVICE) } doReturn mockUserManager
+ on { getSystemService(Context.BLUETOOTH_SERVICE) } doReturn mockBluetoothManager
+ on { getSystemService(Context.CONNECTIVITY_SERVICE) } doReturn mockConnectivityManager
+ on { getSystemService(Context.NETWORK_POLICY_SERVICE) } doReturn mockNetworkPolicyManager
+ on { getSystemService(Context.VPN_MANAGEMENT_SERVICE) } doReturn mockVpnManager
+ on { resources } doReturn mockResources
+ }
+
+ private val controller = BluetoothWiFiResetPreferenceController(context, TEST_KEY)
+
+ @Test
+ fun getAvailabilityStatus_isAdminUser_returnAvailable() {
+ mockUserManager.stub {
+ on { isAdminUser } doReturn true
+ }
+
+ val availabilityStatus = controller.getAvailabilityStatus()
+
+ assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+ }
+
+ @Test
+ fun getAvailabilityStatus_notAdminUser_returnConditionallyUnavailable() {
+ mockUserManager.stub {
+ on { isAdminUser } doReturn false
+ }
+
+ val availabilityStatus = controller.getAvailabilityStatus()
+
+ assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+ }
+
+ @Test
+ fun resetOperation_resetBluetooth() {
+ controller.resetOperation().run()
+
+ verify(mockBluetoothAdapter).clearBluetooth()
+ }
+
+ @Test
+ fun resetOperation_onDeviceWithSimVisible_notResetConnectivity() {
+ mockResources.stub {
+ on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+ }
+
+ controller.resetOperation().run()
+
+ verify(mockConnectivityManager, never()).factoryReset()
+ }
+
+ @Test
+ fun resetOperation_onDeviceWithSimInvisible_resetVpn() {
+ mockResources.stub {
+ on { getBoolean(R.bool.config_show_sim_info) } doReturn false
+ }
+
+ controller.resetOperation().run()
+
+ verify(mockVpnManager).factoryReset()
+ }
+
+ private companion object {
+ const val TEST_KEY = "test_key"
+ }
+}