1 /*
<lambda>null2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.settings.wifi.details2
18
19 import android.content.Context
20 import android.net.wifi.WifiManager
21 import android.os.Bundle
22 import android.os.Handler
23 import android.os.HandlerThread
24 import android.os.Looper
25 import android.os.Process
26 import android.os.SimpleClock
27 import androidx.compose.foundation.layout.Column
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.getValue
30 import androidx.compose.runtime.mutableIntStateOf
31 import androidx.compose.runtime.mutableStateOf
32 import androidx.compose.runtime.remember
33 import androidx.compose.runtime.saveable.rememberSaveable
34 import androidx.compose.runtime.setValue
35 import androidx.compose.ui.platform.LocalContext
36 import androidx.compose.ui.platform.LocalLifecycleOwner
37 import androidx.compose.ui.res.stringArrayResource
38 import androidx.compose.ui.res.stringResource
39 import androidx.navigation.NavType
40 import androidx.navigation.navArgument
41 import com.android.settings.R
42 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
43 import com.android.settingslib.spa.framework.common.SettingsPageProvider
44 import com.android.settingslib.spa.widget.preference.ListPreferenceModel
45 import com.android.settingslib.spa.widget.preference.ListPreferenceOption
46 import com.android.settingslib.spa.widget.preference.RadioPreferences
47 import com.android.settingslib.spa.widget.preference.SwitchPreference
48 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
49 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
50 import com.android.settingslib.spa.widget.ui.Category
51 import com.android.wifitrackerlib.WifiEntry
52 import java.time.Clock
53 import java.time.ZoneOffset
54 import java.util.Base64
55
56 const val WIFI_ENTRY_KEY = "wifiEntryKey"
57
58 object WifiPrivacyPageProvider : SettingsPageProvider {
59 override val name = "WifiPrivacy"
60 const val TAG = "WifiPrivacyPageProvider"
61
62 override val parameter = listOf(
63 navArgument(WIFI_ENTRY_KEY) { type = NavType.StringType },
64 )
65
66 @Composable
67 override fun Page(arguments: Bundle?) {
68 val wifiEntryKey =
69 String(Base64.getUrlDecoder().decode(arguments!!.getString(WIFI_ENTRY_KEY)))
70 if (wifiEntryKey != null) {
71 val context = LocalContext.current
72 val lifecycle = LocalLifecycleOwner.current.lifecycle
73 val wifiEntry = remember {
74 getWifiEntry(context, wifiEntryKey, lifecycle)
75 }
76 WifiPrivacyPage(wifiEntry)
77 }
78 }
79
80 fun getRoute(
81 wifiEntryKey: String,
82 ): String = "${name}/${Base64.getUrlEncoder().encodeToString(wifiEntryKey.toByteArray())}"
83 }
84
85 @Composable
WifiPrivacyPagenull86 fun WifiPrivacyPage(wifiEntry: WifiEntry) {
87 val isSelectable: Boolean = wifiEntry.canSetPrivacy()
88 RegularScaffold(
89 title = stringResource(id = R.string.wifi_privacy_settings)
90 ) {
91 Column {
92 val title = stringResource(id = R.string.wifi_privacy_mac_settings)
93 val wifiPrivacyEntries = stringArrayResource(R.array.wifi_privacy_entries)
94 val wifiPrivacyValues = stringArrayResource(R.array.wifi_privacy_values)
95 val textsSelectedId = rememberSaveable { mutableIntStateOf(wifiEntry.privacy) }
96 val dataList = remember {
97 wifiPrivacyEntries.mapIndexed { index, text ->
98 ListPreferenceOption(id = wifiPrivacyValues[index].toInt(), text = text)
99 }
100 }
101 RadioPreferences(remember {
102 object : ListPreferenceModel {
103 override val title = title
104 override val options = dataList
105 override val selectedId = textsSelectedId
106 override val onIdSelected: (Int) -> Unit = {
107 textsSelectedId.intValue = it
108 onSelectedChange(wifiEntry, it)
109 }
110 override val enabled = { isSelectable }
111 }
112 })
113 wifiEntry.wifiConfiguration?.let {
114 DeviceNameSwitchPreference(wifiEntry)
115 }
116 }
117 }
118 }
119
120 @Composable
DeviceNameSwitchPreferencenull121 fun DeviceNameSwitchPreference(wifiEntry: WifiEntry) {
122 val title = stringResource(id = R.string.wifi_privacy_device_name_settings)
123 Category(title = title) {
124 var checked by remember {
125 mutableStateOf(wifiEntry.wifiConfiguration?.isSendDhcpHostnameEnabled)
126 }
127 val context = LocalContext.current
128 val wifiManager = context.getSystemService(WifiManager::class.java)!!
129 SwitchPreference(object : SwitchPreferenceModel {
130 override val title =
131 context.resources.getString(
132 R.string.wifi_privacy_send_device_name_toggle_title
133 )
134 override val summary =
135 {
136 context.resources.getString(
137 R.string.wifi_privacy_send_device_name_toggle_summary
138 )
139 }
140 override val checked = { checked }
141 override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
142 wifiEntry.wifiConfiguration?.let {
143 it.isSendDhcpHostnameEnabled = newChecked
144 wifiManager.save(it, null /* listener */)
145 checked = newChecked
146 }
147 }
148 })
149 }
150 }
151
onSelectedChangenull152 fun onSelectedChange(wifiEntry: WifiEntry, privacy: Int) {
153 if (wifiEntry.privacy == privacy) {
154 // Prevent disconnection + reconnection if settings not changed.
155 return
156 }
157 wifiEntry.setPrivacy(privacy)
158
159 // To activate changing, we need to reconnect network. WiFi will auto connect to
160 // current network after disconnect(). Only needed when this is connected network.
161
162 // To activate changing, we need to reconnect network. WiFi will auto connect to
163 // current network after disconnect(). Only needed when this is connected network.
164 if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
165 wifiEntry.disconnect(null /* callback */)
166 wifiEntry.connect(null /* callback */)
167 }
168 }
169
getWifiEntrynull170 fun getWifiEntry(
171 context: Context,
172 wifiEntryKey: String,
173 liftCycle: androidx.lifecycle.Lifecycle
174 ): WifiEntry {
175 // Max age of tracked WifiEntries
176 val MAX_SCAN_AGE_MILLIS: Long = 15000
177 // Interval between initiating SavedNetworkTracker scans
178 val SCAN_INTERVAL_MILLIS: Long = 10000
179 val mWorkerThread = HandlerThread(
180 WifiPrivacyPageProvider.TAG,
181 Process.THREAD_PRIORITY_BACKGROUND
182 )
183 mWorkerThread.start()
184 val elapsedRealtimeClock: Clock = object : SimpleClock(ZoneOffset.UTC) {
185 override fun millis(): Long {
186 return android.os.SystemClock.elapsedRealtime()
187 }
188 }
189 val mNetworkDetailsTracker = featureFactory
190 .wifiTrackerLibProvider
191 .createNetworkDetailsTracker(
192 liftCycle,
193 context,
194 Handler(Looper.getMainLooper()),
195 mWorkerThread.getThreadHandler(),
196 elapsedRealtimeClock,
197 MAX_SCAN_AGE_MILLIS,
198 SCAN_INTERVAL_MILLIS,
199 wifiEntryKey
200 )
201 return mNetworkDetailsTracker.wifiEntry
202 }
203