Merge "Set preferred network types on background thread" into main
diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig
index 84bb578..600a0af 100644
--- a/aconfig/settings_connecteddevice_flag_declarations.aconfig
+++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig
@@ -9,20 +9,6 @@
}
flag {
- name: "enable_le_audio_sharing"
- namespace: "pixel_cross_device_control"
- description: "Gates whether to enable LE audio sharing"
- bug: "305620450"
-}
-
-flag {
- name: "enable_le_audio_qr_code_private_broadcast_sharing"
- namespace: "pixel_cross_device_control"
- description: "Gates whether to enable LE audio private broadcast sharing via QR code"
- bug: "308368124"
-}
-
-flag {
name: "enable_auth_challenge_for_usb_preferences"
namespace: "safety_center"
description: "Gates whether to require an auth challenge for changing USB preferences"
@@ -41,4 +27,4 @@
namespace: "dck_framework"
description: "Hide exclusively managed Bluetooth devices in BT settings menu."
bug: "322285078"
-}
\ No newline at end of file
+}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 107423f..46b7e86 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2110,6 +2110,14 @@
<string name="wifi_ip_settings">IP settings</string>
<!-- Label for the spinner to show Wifi MAC randomization [CHAR LIMIT=25] -->
<string name="wifi_privacy_settings">Privacy</string>
+ <!-- Category title for the spinner to show Wifi MAC randomization [CHAR LIMIT=25] -->
+ <string name="wifi_privacy_mac_settings">MAC</string>
+ <!-- Category title for Device name [CHAR LIMIT=25] -->
+ <string name="wifi_privacy_device_name_settings">Device name</string>
+ <!-- Toggle button title for allowing/disallowing sending device name to DHCP [CHAR LIMIT=50] -->
+ <string name="wifi_privacy_send_device_name_toggle_title">Send device name</string>
+ <!-- Toggle button title for allowing/disallowing sending device name to DHCP [CHAR LIMIT=50] -->
+ <string name="wifi_privacy_send_device_name_toggle_summary">Share this device\u0027s name with the network</string>
<!-- Label for the subscription preference. [CHAR LIMIT=32] -->
<string name="wifi_subscription">Subscription</string>
<!-- Summary text for the subscription preference. [CHAR LIMIT=NONE] -->
diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml
index daff20f..598f9d8 100644
--- a/res/xml/wifi_network_details_fragment2.xml
+++ b/res/xml/wifi_network_details_fragment2.xml
@@ -97,6 +97,11 @@
android:entries="@array/wifi_privacy_entries"
android:entryValues="@array/wifi_privacy_values"/>
+ <com.android.settings.spa.preference.ComposePreference
+ android:key="privacy_settings"
+ android:title="@string/wifi_privacy_settings"
+ settings:controller="com.android.settings.wifi.details2.WifiPrivacyPreferenceController"/>
+
<Preference
android:key="subscription_detail"
android:title="@string/wifi_subscription"
diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt
index 1ed9d9a..d709574 100644
--- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt
@@ -80,8 +80,6 @@
@VisibleForTesting
var progressDialog: ProgressDialog? = null
- private lateinit var preference: Preference
-
private var subId by notNull<Int>()
/**
@@ -99,11 +97,6 @@
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()
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 7a1d915..568188f 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -56,6 +56,7 @@
import com.android.settings.spa.system.AppLanguagesPageProvider
import com.android.settings.spa.system.LanguageAndInputPageProvider
import com.android.settings.spa.system.SystemMainPageProvider
+import com.android.settings.wifi.details2.WifiPrivacyPageProvider
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.SpaLogger
@@ -122,6 +123,7 @@
SimOnboardingPageProvider,
BatteryOptimizationModeAppListPageProvider,
NetworkCellularGroupProvider,
+ WifiPrivacyPageProvider,
)
override val logger = if (FeatureFlagUtils.isEnabled(
diff --git a/src/com/android/settings/spa/preference/ComposePreferenceController.kt b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
index 9dd8282..5ba1d24 100644
--- a/src/com/android/settings/spa/preference/ComposePreferenceController.kt
+++ b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
@@ -24,7 +24,7 @@
abstract class ComposePreferenceController(context: Context, preferenceKey: String) :
BasePreferenceController(context, preferenceKey) {
- private lateinit var preference: ComposePreference
+ protected lateinit var preference: ComposePreference
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
index 6263bfd..c84e79a 100644
--- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
+++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
@@ -39,15 +39,8 @@
class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
ComposePreferenceController(context, preferenceKey) {
- private lateinit var preference: Preference
-
var wifiManager = context.getSystemService(WifiManager::class.java)!!
- override fun displayPreference(screen: PreferenceScreen) {
- super.displayPreference(screen)
- preference = screen.findPreference(preferenceKey)!!
- }
-
override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE
else UNSUPPORTED_ON_DEVICE
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index 0384f0d..e1774e3 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -58,6 +58,7 @@
import com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2;
import com.android.settings.wifi.details2.WifiDetailPreferenceController2;
import com.android.settings.wifi.details2.WifiMeteredPreferenceController2;
+import com.android.settings.wifi.details2.WifiPrivacyPreferenceController;
import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
import com.android.settings.wifi.details2.WifiSecondSummaryController2;
import com.android.settings.wifi.details2.WifiSubscriptionDetailPreferenceController2;
@@ -119,6 +120,13 @@
}
@Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ use(WifiPrivacyPreferenceController.class)
+ .setWifiEntryKey(getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
+ }
+
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setIfOnlyAvailableForAdmins(true);
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
new file mode 100644
index 0000000..e41863c
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2024 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.wifi.details2
+
+import android.content.Context
+import android.net.wifi.WifiConfiguration
+import android.net.wifi.WifiManager
+import android.os.Bundle
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Process
+import android.os.SimpleClock
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.res.stringArrayResource
+import androidx.compose.ui.res.stringResource
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.ListPreferenceModel
+import com.android.settingslib.spa.widget.preference.ListPreferenceOption
+import com.android.settingslib.spa.widget.preference.RadioPreferences
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.CategoryTitle
+import com.android.wifitrackerlib.WifiEntry
+import java.time.Clock
+import java.time.ZoneOffset
+
+const val WIFI_ENTRY_KEY = "wifiEntryKey"
+
+object WifiPrivacyPageProvider : SettingsPageProvider {
+ override val name = "WifiPrivacy"
+ const val TAG = "WifiPrivacyPageProvider"
+
+ override val parameter = listOf(
+ navArgument(WIFI_ENTRY_KEY) { type = NavType.StringType },
+ )
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ val wifiEntryKey = arguments!!.getString(WIFI_ENTRY_KEY)
+ if (wifiEntryKey != null) {
+ val context = LocalContext.current
+ val lifecycle = LocalLifecycleOwner.current.lifecycle
+ val wifiEntry = remember {
+ getWifiEntry(context, wifiEntryKey, lifecycle)
+ }
+ WifiPrivacyPage(wifiEntry)
+ }
+ }
+
+ fun getRoute(
+ wifiEntryKey: String,
+ ): String = "${name}/$wifiEntryKey"
+}
+
+@Composable
+fun WifiPrivacyPage(wifiEntry: WifiEntry) {
+ val isSelectable: Boolean = wifiEntry.canSetPrivacy()
+ RegularScaffold(
+ title = stringResource(id = R.string.wifi_privacy_settings)
+ ) {
+ Column {
+ val title = stringResource(id = R.string.wifi_privacy_mac_settings)
+ val wifiPrivacyEntries = stringArrayResource(R.array.wifi_privacy_entries)
+ val wifiPrivacyValues = stringArrayResource(R.array.wifi_privacy_values)
+ val textsSelectedId = rememberSaveable { mutableIntStateOf(wifiEntry.privacy) }
+ val dataList = remember {
+ wifiPrivacyEntries.mapIndexed { index, text ->
+ ListPreferenceOption(id = wifiPrivacyValues[index].toInt(), text = text)
+ }
+ }
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = title
+ override val options = dataList
+ override val selectedId = textsSelectedId
+ override val onIdSelected: (Int) -> Unit = {
+ textsSelectedId.intValue = it
+ onSelectedChange(wifiEntry, it)
+ }
+ override val enabled = { isSelectable }
+ }
+ })
+ wifiEntry.wifiConfiguration?.let {
+ DeviceNameSwitchPreference(it)
+ }
+ }
+ }
+}
+
+@Composable
+fun DeviceNameSwitchPreference(wifiConfiguration: WifiConfiguration){
+ Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
+ CategoryTitle(title = stringResource(R.string.wifi_privacy_device_name_settings))
+ Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
+ var checked by remember {
+ mutableStateOf(wifiConfiguration.isSendDhcpHostnameEnabled)
+ }
+ val context = LocalContext.current
+ val wifiManager = context.getSystemService(WifiManager::class.java)!!
+ SwitchPreference(object : SwitchPreferenceModel {
+ override val title =
+ context.resources.getString(
+ R.string.wifi_privacy_send_device_name_toggle_title
+ )
+ override val summary =
+ {
+ context.resources.getString(
+ R.string.wifi_privacy_send_device_name_toggle_summary
+ )
+ }
+ override val checked = { checked }
+ override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+ wifiConfiguration.isSendDhcpHostnameEnabled = newChecked
+ wifiManager.save(wifiConfiguration, null /* listener */)
+ checked = newChecked
+ }
+ })
+}
+
+fun onSelectedChange(wifiEntry: WifiEntry, privacy: Int) {
+ if (wifiEntry.privacy == privacy) {
+ // Prevent disconnection + reconnection if settings not changed.
+ return
+ }
+ wifiEntry.setPrivacy(privacy)
+
+ // To activate changing, we need to reconnect network. WiFi will auto connect to
+ // current network after disconnect(). Only needed when this is connected network.
+
+ // To activate changing, we need to reconnect network. WiFi will auto connect to
+ // current network after disconnect(). Only needed when this is connected network.
+ if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
+ wifiEntry.disconnect(null /* callback */)
+ wifiEntry.connect(null /* callback */)
+ }
+}
+
+fun getWifiEntry(
+ context: Context,
+ wifiEntryKey: String,
+ liftCycle: androidx.lifecycle.Lifecycle
+): WifiEntry {
+ // Max age of tracked WifiEntries
+ val MAX_SCAN_AGE_MILLIS: Long = 15000
+ // Interval between initiating SavedNetworkTracker scans
+ val SCAN_INTERVAL_MILLIS: Long = 10000
+ val mWorkerThread = HandlerThread(
+ WifiPrivacyPageProvider.TAG,
+ Process.THREAD_PRIORITY_BACKGROUND
+ )
+ mWorkerThread.start()
+ val elapsedRealtimeClock: Clock = object : SimpleClock(ZoneOffset.UTC) {
+ override fun millis(): Long {
+ return android.os.SystemClock.elapsedRealtime()
+ }
+ }
+ val mNetworkDetailsTracker = featureFactory
+ .wifiTrackerLibProvider
+ .createNetworkDetailsTracker(
+ liftCycle,
+ context,
+ Handler(Looper.getMainLooper()),
+ mWorkerThread.getThreadHandler(),
+ elapsedRealtimeClock,
+ MAX_SCAN_AGE_MILLIS,
+ SCAN_INTERVAL_MILLIS,
+ wifiEntryKey
+ )
+ return mNetworkDetailsTracker.wifiEntry
+}
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController.kt b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController.kt
new file mode 100644
index 0000000..42741e3
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 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.wifi.details2
+
+import android.content.Context
+import android.net.wifi.WifiManager
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.res.vectorResource
+import com.android.settings.R
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+import com.android.settings.spa.preference.ComposePreferenceController
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.wifi.flags.Flags
+
+class WifiPrivacyPreferenceController(context: Context, preferenceKey: String) :
+ ComposePreferenceController(context, preferenceKey) {
+
+ private var wifiEntryKey: String? = null
+
+ var wifiManager = context.getSystemService(WifiManager::class.java)!!
+
+ fun setWifiEntryKey(key: String?) {
+ wifiEntryKey = key
+ }
+
+ override fun getAvailabilityStatus() =
+ if (Flags.androidVWifiApi() && wifiManager.isConnectedMacRandomizationSupported) AVAILABLE
+ else CONDITIONALLY_UNAVAILABLE
+
+ @Composable
+ override fun Content() {
+ Preference(object : PreferenceModel {
+ override val title = stringResource(R.string.wifi_privacy_settings)
+ override val icon = @Composable {
+ Icon(
+ ImageVector.vectorResource(R.drawable.ic_wifi_privacy_24dp),
+ contentDescription = null
+ )
+ }
+ override val onClick: () -> Unit =
+ {
+ wifiEntryKey?.let {
+ mContext.startSpaActivity(WifiPrivacyPageProvider.getRoute(it))
+ }
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
index 8c78e80..5d393e5 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
@@ -26,6 +26,7 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.wifi.flags.Flags;
import com.android.wifitrackerlib.WifiEntry;
/**
@@ -50,7 +51,7 @@
@Override
public int getAvailabilityStatus() {
- return mWifiManager.isConnectedMacRandomizationSupported()
+ return (!Flags.androidVWifiApi() && mWifiManager.isConnectedMacRandomizationSupported())
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt
new file mode 100644
index 0000000..5c9a1a4
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2024 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.wifi.details2
+
+import android.content.Context
+import android.net.wifi.WifiConfiguration
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsSelectable
+import androidx.compose.ui.test.assertIsSelected
+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.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.wifitrackerlib.WifiEntry
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class WifiPrivacyPageProviderTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private var mockWifiConfiguration = mock<WifiConfiguration>() {
+ on { isSendDhcpHostnameEnabled } doReturn true
+ }
+ private var mockWifiEntry = mock<WifiEntry>() {
+ on { canSetPrivacy() } doReturn true
+ on { privacy } doReturn 0
+ on { wifiConfiguration } doReturn mockWifiConfiguration
+ }
+
+ @Test
+ fun apnEditPageProvider_name() {
+ Truth.assertThat(WifiPrivacyPageProvider.name).isEqualTo("WifiPrivacy")
+ }
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_settings)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun category_mac_title_displayed() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_mac_settings)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun category_mac_list_displayed() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ for (entry in wifiPrivacyEntries) {
+ composeTestRule.onNodeWithText(
+ entry
+ ).assertIsDisplayed()
+ }
+ }
+
+ @Test
+ fun category_mac_list_selectable() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ for (entry in wifiPrivacyEntries) {
+ composeTestRule.onNodeWithText(
+ entry
+ ).assertIsSelectable()
+ }
+ }
+
+ @Test
+ fun category_mac_list_default_selected() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ val wifiPrivacyValues = context.resources.getStringArray(R.array.wifi_privacy_values)
+ composeTestRule.onNodeWithText(
+ wifiPrivacyEntries[wifiPrivacyValues.indexOf("0")]
+ ).assertIsSelected()
+ }
+
+ @Test
+ fun category_mac_list_not_enabled() {
+ mockWifiEntry.stub {
+ on { canSetPrivacy() } doReturn false
+ }
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ for (entry in wifiPrivacyEntries) {
+ composeTestRule.onNodeWithText(entry).assertIsNotEnabled()
+ }
+ }
+
+ @Test
+ fun category_send_device_name_title_displayed() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_device_name_settings)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun toggle_send_device_name_title_displayed() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_send_device_name_toggle_title)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun send_device_name_turnOn() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_send_device_name_toggle_title)
+ ).assertIsOn()
+ }
+
+ @Test
+ fun onClick_turnOff() {
+ composeTestRule.setContent {
+ WifiPrivacyPage(mockWifiEntry)
+ }
+
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_send_device_name_toggle_title)
+ ).performClick()
+
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.wifi_privacy_send_device_name_toggle_title)
+ ).assertIsOff()
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceControllerTest.kt
new file mode 100644
index 0000000..98997e4
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceControllerTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 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.wifi.details2
+
+import android.content.Context
+import android.content.Intent
+import android.net.wifi.WifiManager
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.core.os.bundleOf
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.framework.util.KEY_DESTINATION
+import com.google.common.truth.Truth
+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 WifiPrivacyPreferenceControllerTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val mockWifiManager = mock<WifiManager> {
+ on { isConnectedMacRandomizationSupported } doReturn true
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(WifiManager::class.java) } doReturn mockWifiManager
+ doNothing().whenever(mock).startActivity(any())
+ }
+
+ private val controller = WifiPrivacyPreferenceController(context, TEST_KEY)
+
+ @Test
+ fun title_isDisplayed() {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ controller.Content()
+ }
+ }
+
+ composeTestRule.onNodeWithText(context.getString(R.string.wifi_privacy_settings))
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun onClick_startWifiPrivacyPage() {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ controller.setWifiEntryKey("")
+ controller.Content()
+ }
+ }
+
+ composeTestRule.onNodeWithText(context.getString(R.string.wifi_privacy_settings))
+ .performClick()
+
+ val intent = argumentCaptor<Intent> {
+ verify(context).startActivity(capture())
+ }.firstValue
+ Truth.assertThat(intent.getStringExtra(KEY_DESTINATION))
+ .isEqualTo(WifiPrivacyPageProvider.getRoute(""))
+ }
+
+ private companion object {
+ const val TEST_KEY = "test_key"
+ }
+}
\ No newline at end of file