Merge changes from topic "catalyst" into main

* changes:
  [Catalyst] Refine WifiHotspotSwitchPreference
  [Catalyst] Refine DataSaverMainSwitchPreference
This commit is contained in:
Jacky Wang
2024-12-09 06:26:53 +00:00
committed by Android (Google) Code Review
5 changed files with 121 additions and 132 deletions

View File

@@ -19,18 +19,14 @@ package com.android.settings.datausage
import android.content.Context
import com.android.settings.R
import com.android.settings.widget.MainSwitchBarMetadata
import com.android.settingslib.datastore.AbstractKeyedDataObservable
import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
class DataSaverMainSwitchPreference(context: Context) :
MainSwitchBarMetadata, PreferenceLifecycleProvider {
private val dataSaverBackend = DataSaverBackend(context)
private var dataSaverBackendListener: DataSaverBackend.Listener? = null
class DataSaverMainSwitchPreference : MainSwitchBarMetadata, PreferenceLifecycleProvider {
override val key
get() = KEY
@@ -38,7 +34,7 @@ class DataSaverMainSwitchPreference(context: Context) :
override val title
get() = R.string.data_saver_switch_title
override fun storage(context: Context): KeyValueStore = DataSaverStore(dataSaverBackend)
override fun storage(context: Context) = createDataStore(context)
override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
@@ -49,24 +45,11 @@ class DataSaverMainSwitchPreference(context: Context) :
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
override fun onStart(context: PreferenceLifecycleContext) {
val listener = DataSaverBackend.Listener { context.notifyPreferenceChange(KEY) }
dataSaverBackendListener = listener
dataSaverBackend.addListener(listener)
}
override fun onStop(context: PreferenceLifecycleContext) {
dataSaverBackendListener?.let {
dataSaverBackend.remListener(it)
dataSaverBackendListener = null
}
}
@Suppress("UNCHECKED_CAST")
private class DataSaverStore(private val dataSaverBackend: DataSaverBackend) :
NoOpKeyedObservable<String>(), KeyValueStore {
AbstractKeyedDataObservable<String>(), KeyValueStore, DataSaverBackend.Listener {
override fun contains(key: String) = true // just assume the datastore contains the value
override fun contains(key: String) = key == KEY
override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
dataSaverBackend.isDataSaverEnabled as T?
@@ -74,9 +57,20 @@ class DataSaverMainSwitchPreference(context: Context) :
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
dataSaverBackend.isDataSaverEnabled = value as Boolean
}
override fun onFirstObserverAdded() = dataSaverBackend.addListener(this)
override fun onLastObserverRemoved() = dataSaverBackend.remListener(this)
override fun onDataSaverChanged(isDataSaving: Boolean) =
notifyChange(KEY, DataChangeReason.UPDATE)
}
companion object {
const val KEY = "use_data_saver"
/** Creates [KeyValueStore] for data saver preference. */
fun createDataStore(context: Context): KeyValueStore =
DataSaverStore(DataSaverBackend(context))
}
}

View File

@@ -19,24 +19,30 @@ package com.android.settings.datausage
import android.content.Context
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settings.R
import com.android.settings.Settings.DataSaverSummaryActivity
import com.android.settings.flags.Flags
import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
import com.android.settings.datausage.DataSaverMainSwitchPreference.Companion.KEY as DATA_SAVER_KEY
@ProvidePreferenceScreen
class DataSaverScreen :
class DataSaverScreen(context: Context) :
PreferenceScreenCreator,
PreferenceAvailabilityProvider,
PreferenceSummaryProvider,
PreferenceLifecycleProvider {
private var dataSaverBackend: DataSaverBackend? = null
private var dataSaverBackendListener: DataSaverBackend.Listener? = null
private val dataSaverStore = DataSaverMainSwitchPreference.createDataStore(context)
private lateinit var keyedObserver: KeyedObserver<String>
override val key
get() = KEY
@@ -53,7 +59,7 @@ class DataSaverScreen :
override fun getSummary(context: Context): CharSequence? =
when {
DataSaverBackend(context).isDataSaverEnabled ->
dataSaverStore.getBoolean(DATA_SAVER_KEY) == true ->
context.getString(R.string.data_saver_on)
else -> context.getString(R.string.data_saver_off)
}
@@ -65,21 +71,21 @@ class DataSaverScreen :
override fun fragmentClass() = DataSaverSummary::class.java
override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
makeLaunchIntent(context, DataSaverSummaryActivity::class.java, metadata?.key)
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) { +DataSaverMainSwitchPreference(context) }
preferenceHierarchy(this) { +DataSaverMainSwitchPreference() }
override fun hasCompleteHierarchy() = false
override fun onStart(context: PreferenceLifecycleContext) {
val listener = DataSaverBackend.Listener { context.notifyPreferenceChange(KEY) }
dataSaverBackendListener = listener
dataSaverBackend = DataSaverBackend(context).apply { addListener(listener) }
override fun onCreate(context: PreferenceLifecycleContext) {
keyedObserver = KeyedObserver { _, _ -> context.notifyPreferenceChange(KEY) }
dataSaverStore.addObserver(DATA_SAVER_KEY, keyedObserver, HandlerExecutor.main)
}
override fun onStop(context: PreferenceLifecycleContext) {
dataSaverBackend?.remListener(dataSaverBackendListener)
dataSaverBackend = null
dataSaverBackendListener = null
override fun onDestroy(context: PreferenceLifecycleContext) {
dataSaverStore.removeObserver(DATA_SAVER_KEY, keyedObserver)
}
companion object {

View File

@@ -20,19 +20,27 @@ import android.net.TetheringManager
import android.os.UserManager
import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.Settings.TetherSettingsActivity
import com.android.settings.datausage.DataSaverMainSwitchPreference
import com.android.settings.flags.Flags
import com.android.settings.network.TetherPreferenceController
import com.android.settings.utils.makeLaunchIntent
import com.android.settings.wifi.tether.WifiHotspotSwitchPreference
import com.android.settingslib.TetherUtil
import com.android.settingslib.Utils
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceTitleProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@ProvidePreferenceScreen
class TetherScreen :
PreferenceScreenCreator, PreferenceAvailabilityProvider, PreferenceRestrictionMixin {
PreferenceScreenCreator,
PreferenceTitleProvider,
PreferenceAvailabilityProvider,
PreferenceRestrictionMixin {
override val key: String
get() = KEY
@@ -43,7 +51,7 @@ class TetherScreen :
override val keywords: Int
get() = R.string.keywords_hotspot_tethering
override fun getPreferenceTitle(context: Context): CharSequence? =
override fun getTitle(context: Context): CharSequence? =
if (TetherPreferenceController.isTetherConfigDisallowed(context)) {
context.getText(R.string.tether_settings_title_all)
} else {
@@ -64,9 +72,14 @@ class TetherScreen :
override fun fragmentClass() = TetherSettings::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
+WifiHotspotSwitchPreference(context)
}
override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
makeLaunchIntent(context, TetherSettingsActivity::class.java, metadata?.key)
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) {
val dataSaverStore = DataSaverMainSwitchPreference.createDataStore(context)
+WifiHotspotSwitchPreference(context, dataSaverStore)
}
companion object {
const val KEY = "tether_settings"

View File

@@ -20,6 +20,7 @@ import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.net.TetheringManager
import android.net.TetheringManager.TETHERING_WIFI
import android.net.wifi.WifiClient
import android.net.wifi.WifiManager
import android.os.UserManager
@@ -30,7 +31,7 @@ import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.core.SubSettingLauncher
import com.android.settings.datausage.DataSaverBackend
import com.android.settings.datausage.DataSaverMainSwitchPreference.Companion.KEY as DATA_SAVER_KEY
import com.android.settings.wifi.WifiUtils.canShowWifiHotspot
import com.android.settingslib.PrimarySwitchPreference
import com.android.settingslib.TetherUtil
@@ -38,9 +39,8 @@ import com.android.settingslib.datastore.AbstractKeyedDataObservable
import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.ReadWritePermit
@@ -50,20 +50,15 @@ import com.android.settingslib.preference.PreferenceBinding
import com.android.settingslib.wifi.WifiUtils.Companion.getWifiTetherSummaryForConnectedDevices
// LINT.IfChange
@Deprecated("Deprecated in Java")
@Suppress("MissingPermission", "NewApi", "UNCHECKED_CAST")
class WifiHotspotSwitchPreference(context: Context) :
class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStore) :
SwitchPreference(KEY, R.string.wifi_hotspot_checkbox_text),
PreferenceBinding,
PreferenceAvailabilityProvider,
PreferenceSummaryProvider,
PreferenceLifecycleProvider,
PreferenceRestrictionMixin {
private val wifiHotspotStore = WifiHotspotStore(context)
private val dataSaverBackend = DataSaverBackend(context)
private var dataSaverBackendListener: DataSaverBackend.Listener? = null
private val wifiHotspotStore = WifiHotspotStore(context, dataSaverStore)
override fun isAvailable(context: Context) =
canShowWifiHotspot(context) &&
@@ -71,22 +66,19 @@ class WifiHotspotSwitchPreference(context: Context) :
!Utils.isMonkeyRunning()
override fun getSummary(context: Context): CharSequence? =
when (wifiHotspotStore.sapState) {
when (context.wifiManager?.wifiApState) {
WifiManager.WIFI_AP_STATE_ENABLING -> context.getString(R.string.wifi_tether_starting)
WifiManager.WIFI_AP_STATE_ENABLED ->
when (wifiHotspotStore.sapClientsSize) {
null ->
context.getString(
R.string.wifi_tether_enabled_subtext,
BidiFormatter.getInstance()
.unicodeWrap(context.getSoftApConfiguration()?.ssid),
)
else ->
getWifiTetherSummaryForConnectedDevices(
context,
wifiHotspotStore.sapClientsSize!!,
)
WifiManager.WIFI_AP_STATE_ENABLED -> {
val sapClientsSize = wifiHotspotStore.sapClientsSize
if (sapClientsSize == null) {
context.getString(
R.string.wifi_tether_enabled_subtext,
BidiFormatter.getInstance().unicodeWrap(context.wifiSsid),
)
} else {
getWifiTetherSummaryForConnectedDevices(context, sapClientsSize)
}
}
WifiManager.WIFI_AP_STATE_DISABLING -> context.getString(R.string.wifi_tether_stopping)
WifiManager.WIFI_AP_STATE_DISABLED ->
context.getString(R.string.wifi_hotspot_off_subtext)
@@ -108,7 +100,8 @@ class WifiHotspotSwitchPreference(context: Context) :
.toIntent()
override fun isEnabled(context: Context) =
!dataSaverBackend.isDataSaverEnabled && super<PreferenceRestrictionMixin>.isEnabled(context)
wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) == true &&
super<PreferenceRestrictionMixin>.isEnabled(context)
override val restrictionKeys
get() = arrayOf(UserManager.DISALLOW_WIFI_TETHERING)
@@ -117,10 +110,7 @@ class WifiHotspotSwitchPreference(context: Context) :
ReadWritePermit.ALLOW
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
when {
dataSaverBackend.isDataSaverEnabled -> ReadWritePermit.DISALLOW
else -> ReadWritePermit.ALLOW
}
ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.HIGH_SENSITIVITY
@@ -129,11 +119,17 @@ class WifiHotspotSwitchPreference(context: Context) :
override fun storage(context: Context): KeyValueStore = wifiHotspotStore
private class WifiHotspotStore(private val context: Context) :
AbstractKeyedDataObservable<String>(), KeyValueStore {
private class WifiHotspotStore(
private val context: Context,
val dataSaverStore: KeyValueStore,
) :
AbstractKeyedDataObservable<String>(),
KeyValueStore,
WifiTetherSoftApManager.WifiTetherSoftApCallback,
TetheringManager.StartTetheringCallback,
KeyedObserver<String> {
private var wifiTetherSoftApManager: WifiTetherSoftApManager? = null
var sapState: Int = WifiManager.WIFI_AP_STATE_DISABLED
var sapFailureReason: Int? = null
var sapClientsSize: Int? = null
@@ -150,54 +146,47 @@ class WifiHotspotSwitchPreference(context: Context) :
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
if (value !is Boolean) return
context.tetheringManager?.let {
if (value) {
val startTetheringCallback =
object : TetheringManager.StartTetheringCallback {
override fun onTetheringStarted() {
Log.d(TAG, "onTetheringStarted()")
}
override fun onTetheringFailed(error: Int) {
Log.e(TAG, "onTetheringFailed(),error=$error")
}
}
it.startTethering(
TetheringManager.TETHERING_WIFI,
HandlerExecutor.main,
startTetheringCallback,
)
} else {
it.stopTethering(TetheringManager.TETHERING_WIFI)
}
val tetheringManager = context.tetheringManager ?: return
if (value) {
tetheringManager.startTethering(TETHERING_WIFI, HandlerExecutor.main, this)
} else {
tetheringManager.stopTethering(TETHERING_WIFI)
}
}
override fun onFirstObserverAdded() {
val wifiSoftApCallback =
object : WifiTetherSoftApManager.WifiTetherSoftApCallback {
override fun onStateChanged(state: Int, failureReason: Int) {
Log.d(TAG, "onStateChanged(),state=$state,failureReason=$failureReason")
sapState = state
sapFailureReason = failureReason
if (state == WifiManager.WIFI_AP_STATE_DISABLED) sapClientsSize = null
notifyChange(KEY, DataChangeReason.UPDATE)
}
override fun onConnectedClientsChanged(clients: List<WifiClient>?) {
sapClientsSize = clients?.size ?: 0
Log.d(TAG, "onConnectedClientsChanged(),sapClientsSize=$sapClientsSize")
notifyChange(KEY, DataChangeReason.UPDATE)
}
}
wifiTetherSoftApManager =
WifiTetherSoftApManager(context.wifiManager, wifiSoftApCallback)
wifiTetherSoftApManager?.registerSoftApCallback()
val apManager = WifiTetherSoftApManager(context.wifiManager, this)
wifiTetherSoftApManager = apManager
apManager.registerSoftApCallback()
dataSaverStore.addObserver(DATA_SAVER_KEY, this, HandlerExecutor.main)
}
override fun onLastObserverRemoved() {
dataSaverStore.removeObserver(DATA_SAVER_KEY, this)
wifiTetherSoftApManager?.unRegisterSoftApCallback()
}
override fun onStateChanged(state: Int, failureReason: Int) {
Log.d(TAG, "onStateChanged(),state=$state,failureReason=$failureReason")
sapFailureReason = failureReason
if (state == WifiManager.WIFI_AP_STATE_DISABLED) sapClientsSize = null
notifyChange(KEY, DataChangeReason.UPDATE)
}
override fun onConnectedClientsChanged(clients: List<WifiClient>?) {
sapClientsSize = clients?.size ?: 0
Log.d(TAG, "onConnectedClientsChanged(),sapClientsSize=$sapClientsSize")
notifyChange(KEY, DataChangeReason.UPDATE)
}
override fun onTetheringStarted() {}
override fun onTetheringFailed(error: Int) {
Log.e(TAG, "onTetheringFailed(),error=$error")
}
override fun onKeyChanged(key: String, reason: Int) =
notifyChange(KEY, DataChangeReason.UPDATE)
}
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
@@ -207,24 +196,6 @@ class WifiHotspotSwitchPreference(context: Context) :
}
}
override fun onStart(context: PreferenceLifecycleContext) {
val listener =
DataSaverBackend.Listener { isDataSaving: Boolean ->
context.findPreference<PrimarySwitchPreference>(KEY)?.isSwitchEnabled =
!isDataSaving
context.notifyPreferenceChange(KEY)
}
dataSaverBackendListener = listener
dataSaverBackend.addListener(listener)
}
override fun onStop(context: PreferenceLifecycleContext) {
dataSaverBackendListener?.let {
dataSaverBackend.remListener(it)
dataSaverBackendListener = null
}
}
companion object {
const val TAG = "WifiHotspotSwitchPreference"
const val KEY = "wifi_tether"
@@ -232,7 +203,9 @@ class WifiHotspotSwitchPreference(context: Context) :
private val Context.wifiManager: WifiManager?
get() = applicationContext.getSystemService(WifiManager::class.java)
private fun Context.getSoftApConfiguration() = wifiManager?.softApConfiguration
@Suppress("DEPRECATION")
private val Context.wifiSsid
get() = wifiManager?.softApConfiguration?.ssid
private val Context.tetheringManager: TetheringManager?
get() = applicationContext.getSystemService(TetheringManager::class.java)

View File

@@ -16,13 +16,16 @@
package com.android.settings.datausage
import androidx.test.core.app.ApplicationProvider
import com.android.settings.flags.Flags
import com.android.settingslib.preference.CatalystScreenTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class DataSaverScreenTest : CatalystScreenTestCase() {
override val preferenceScreenCreator = DataSaverScreen()
override val preferenceScreenCreator =
DataSaverScreen(ApplicationProvider.getApplicationContext())
override val flagName
get() = Flags.FLAG_CATALYST_RESTRICT_BACKGROUND_PARENT_ENTRY