Merge "[Catalyst] Migrate BluetoothTetherSwitchPreference." into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
3859a8adaa
@@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.tether
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
import android.bluetooth.BluetoothPan
|
||||||
|
import android.bluetooth.BluetoothProfile
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.TetheringManager
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.datausage.DataSaverBackend
|
||||||
|
import com.android.settingslib.datastore.KeyValueStore
|
||||||
|
import com.android.settingslib.datastore.KeyedDataObservable
|
||||||
|
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||||
|
import com.android.settingslib.metadata.PreferenceLifecycleContext
|
||||||
|
import com.android.settingslib.metadata.PreferenceLifecycleProvider
|
||||||
|
import com.android.settingslib.metadata.ReadWritePermit
|
||||||
|
import com.android.settingslib.metadata.SensitivityLevel
|
||||||
|
import com.android.settingslib.metadata.SwitchPreference
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
class BluetoothTetherSwitchPreference :
|
||||||
|
SwitchPreference(KEY, R.string.bluetooth_tether_checkbox_text),
|
||||||
|
PreferenceAvailabilityProvider,
|
||||||
|
PreferenceLifecycleProvider {
|
||||||
|
|
||||||
|
private var tetherChangeReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
override val summary: Int
|
||||||
|
get() = R.string.bluetooth_tethering_subtext
|
||||||
|
|
||||||
|
override val keywords: Int
|
||||||
|
get() = R.string.keywords_hotspot_tethering
|
||||||
|
|
||||||
|
override fun storage(context: Context): KeyValueStore = BluetoothTetherStore(context)
|
||||||
|
|
||||||
|
override fun isAvailable(context: Context): Boolean {
|
||||||
|
BluetoothAdapter.getDefaultAdapter() ?: return false
|
||||||
|
val tetheringManager = context.getSystemService(TetheringManager::class.java)
|
||||||
|
val bluetoothRegexs = tetheringManager?.tetherableBluetoothRegexs
|
||||||
|
return bluetoothRegexs?.isNotEmpty() == true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEnabled(context: Context): Boolean {
|
||||||
|
val adapter = BluetoothAdapter.getDefaultAdapter() ?: return false
|
||||||
|
val btState = adapter.state
|
||||||
|
/* TODO: when bluetooth is off, btstate will be `state_turning_on` -> `state_off` ->
|
||||||
|
`state_turning_on` -> `state_on`, causing preference enable status incorrect. */
|
||||||
|
when (btState) {
|
||||||
|
BluetoothAdapter.STATE_TURNING_OFF,
|
||||||
|
BluetoothAdapter.STATE_TURNING_ON -> return false
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
val dataSaverBackend = DataSaverBackend(context)
|
||||||
|
return !dataSaverBackend.isDataSaverEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
|
||||||
|
ReadWritePermit.ALLOW
|
||||||
|
|
||||||
|
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
|
||||||
|
ReadWritePermit.ALLOW
|
||||||
|
|
||||||
|
override val sensitivityLevel: Int
|
||||||
|
get() = SensitivityLevel.LOW_SENSITIVITY
|
||||||
|
|
||||||
|
override fun onCreate(context: PreferenceLifecycleContext) {
|
||||||
|
val receiver =
|
||||||
|
object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(content: Context, intent: Intent) {
|
||||||
|
when (intent.action) {
|
||||||
|
TetheringManager.ACTION_TETHER_STATE_CHANGED,
|
||||||
|
Intent.ACTION_MEDIA_SHARED,
|
||||||
|
Intent.ACTION_MEDIA_UNSHARED,
|
||||||
|
BluetoothAdapter.ACTION_STATE_CHANGED,
|
||||||
|
BluetoothPan.ACTION_TETHERING_STATE_CHANGED ->
|
||||||
|
context.notifyPreferenceChange(KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tetherChangeReceiver = receiver
|
||||||
|
var filter = IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)
|
||||||
|
val intent = context.registerReceiver(receiver, filter)
|
||||||
|
|
||||||
|
filter = IntentFilter()
|
||||||
|
filter.addAction(Intent.ACTION_MEDIA_SHARED)
|
||||||
|
filter.addAction(Intent.ACTION_MEDIA_UNSHARED)
|
||||||
|
filter.addDataScheme("file")
|
||||||
|
context.registerReceiver(receiver, filter)
|
||||||
|
|
||||||
|
filter = IntentFilter()
|
||||||
|
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||||
|
filter.addAction(BluetoothPan.ACTION_TETHERING_STATE_CHANGED)
|
||||||
|
context.registerReceiver(receiver, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy(context: PreferenceLifecycleContext) {
|
||||||
|
tetherChangeReceiver?.let {
|
||||||
|
context.unregisterReceiver(it)
|
||||||
|
tetherChangeReceiver = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private class BluetoothTetherStore(private val context: Context) :
|
||||||
|
KeyedDataObservable<String>(), KeyValueStore {
|
||||||
|
|
||||||
|
val bluetoothPan = AtomicReference<BluetoothPan>()
|
||||||
|
|
||||||
|
override fun contains(key: String) = key == KEY
|
||||||
|
|
||||||
|
override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
|
||||||
|
// TODO: support async operation in background thread
|
||||||
|
val adapter = BluetoothAdapter.getDefaultAdapter() ?: return false as T
|
||||||
|
if (bluetoothPan.get() == null) {
|
||||||
|
val profileServiceListener: BluetoothProfile.ServiceListener =
|
||||||
|
object : BluetoothProfile.ServiceListener {
|
||||||
|
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
|
||||||
|
if (bluetoothPan.get() == null) {
|
||||||
|
bluetoothPan.set(proxy as BluetoothPan)
|
||||||
|
notifyChange(KEY, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(profile: Int) {
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: adapter.closeProfileProxy(bluetoothPan.get())
|
||||||
|
adapter.getProfileProxy(
|
||||||
|
context.applicationContext,
|
||||||
|
profileServiceListener,
|
||||||
|
BluetoothProfile.PAN,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val btState = adapter.state
|
||||||
|
val pan = bluetoothPan.get()
|
||||||
|
return ((btState == BluetoothAdapter.STATE_ON ||
|
||||||
|
btState == BluetoothAdapter.STATE_TURNING_OFF) && pan != null && pan.isTetheringOn)
|
||||||
|
as T?
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
|
||||||
|
if (value == null) return
|
||||||
|
val connectivityManager =
|
||||||
|
context.getSystemService(ConnectivityManager::class.java) ?: return
|
||||||
|
if (value as Boolean) {
|
||||||
|
val handler by lazy { Handler(Looper.getMainLooper()) }
|
||||||
|
val startTetheringCallback = OnStartTetheringCallback()
|
||||||
|
fun startTethering() {
|
||||||
|
connectivityManager.startTethering(
|
||||||
|
ConnectivityManager.TETHERING_BLUETOOTH,
|
||||||
|
true,
|
||||||
|
startTetheringCallback,
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||||
|
if (adapter.state == BluetoothAdapter.STATE_OFF) {
|
||||||
|
adapter.enable()
|
||||||
|
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||||
|
val tetherChangeReceiver =
|
||||||
|
object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (
|
||||||
|
intent.getIntExtra(
|
||||||
|
BluetoothAdapter.EXTRA_STATE,
|
||||||
|
BluetoothAdapter.ERROR,
|
||||||
|
) == BluetoothAdapter.STATE_ON
|
||||||
|
) {
|
||||||
|
startTethering()
|
||||||
|
context.unregisterReceiver(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val intent = context.registerReceiver(tetherChangeReceiver, filter)
|
||||||
|
if (intent != null) tetherChangeReceiver.onReceive(context, intent)
|
||||||
|
} else {
|
||||||
|
startTethering()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connectivityManager.stopTethering(ConnectivityManager.TETHERING_BLUETOOTH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class OnStartTetheringCallback :
|
||||||
|
ConnectivityManager.OnStartTetheringCallback() {
|
||||||
|
override fun onTetheringStarted() {
|
||||||
|
notifyChange(KEY, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTetheringFailed() {
|
||||||
|
notifyChange(KEY, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY = "enable_bluetooth_tethering"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LINT.ThenChange(TetherSettings.java)
|
@@ -72,6 +72,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
/**
|
/**
|
||||||
* Displays preferences for Tethering.
|
* Displays preferences for Tethering.
|
||||||
*/
|
*/
|
||||||
@@ -154,6 +155,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
return R.xml.tether_prefs;
|
return R.xml.tether_prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
@@ -205,16 +207,19 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
|
|
||||||
mWifiTetherPreferenceController.displayPreference(getPreferenceScreen());
|
mWifiTetherPreferenceController.displayPreference(getPreferenceScreen());
|
||||||
|
|
||||||
if (!bluetoothAvailable) {
|
if (!isCatalystEnabled()) {
|
||||||
getPreferenceScreen().removePreference(mBluetoothTether);
|
if (!bluetoothAvailable) {
|
||||||
} else {
|
mBluetoothTether.setVisible(false);
|
||||||
BluetoothPan pan = mBluetoothPan.get();
|
|
||||||
if (pan != null && pan.isTetheringOn()) {
|
|
||||||
mBluetoothTether.setChecked(true);
|
|
||||||
} else {
|
} else {
|
||||||
mBluetoothTether.setChecked(false);
|
BluetoothPan pan = mBluetoothPan.get();
|
||||||
|
if (pan != null && pan.isTetheringOn()) {
|
||||||
|
mBluetoothTether.setChecked(true);
|
||||||
|
} else {
|
||||||
|
mBluetoothTether.setChecked(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ethernetAvailable) getPreferenceScreen().removePreference(mEthernetTether);
|
if (!ethernetAvailable) getPreferenceScreen().removePreference(mEthernetTether);
|
||||||
// Set initial state based on Data Saver mode.
|
// Set initial state based on Data Saver mode.
|
||||||
onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
|
onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
|
||||||
@@ -263,7 +268,9 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
mDataSaverEnabled = isDataSaving;
|
mDataSaverEnabled = isDataSaving;
|
||||||
mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled);
|
mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled);
|
||||||
mUsbTether.setEnabled(!mDataSaverEnabled);
|
mUsbTether.setEnabled(!mDataSaverEnabled);
|
||||||
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
if (!isCatalystEnabled()) {
|
||||||
|
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
||||||
|
}
|
||||||
mEthernetTether.setEnabled(!mDataSaverEnabled);
|
mEthernetTether.setEnabled(!mDataSaverEnabled);
|
||||||
mDataSaverFooter.setVisible(mDataSaverEnabled);
|
mDataSaverFooter.setVisible(mDataSaverEnabled);
|
||||||
}
|
}
|
||||||
@@ -513,6 +520,8 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateBluetoothState() {
|
private void updateBluetoothState() {
|
||||||
|
if (isCatalystEnabled()) return;
|
||||||
|
|
||||||
final int btState = getBluetoothState();
|
final int btState = getBluetoothState();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "updateBluetoothState() btState : " + btState);
|
Log.d(TAG, "updateBluetoothState() btState : " + btState);
|
||||||
@@ -568,7 +577,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startTethering(int choice) {
|
private void startTethering(int choice) {
|
||||||
if (choice == TETHERING_BLUETOOTH) {
|
if (choice == TETHERING_BLUETOOTH && !isCatalystEnabled()) {
|
||||||
// Turn on Bluetooth first.
|
// Turn on Bluetooth first.
|
||||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
|
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
|
||||||
@@ -590,7 +599,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
} else {
|
} else {
|
||||||
mCm.stopTethering(TETHERING_USB);
|
mCm.stopTethering(TETHERING_USB);
|
||||||
}
|
}
|
||||||
} else if (preference == mBluetoothTether) {
|
} else if (preference == mBluetoothTether && !isCatalystEnabled()) {
|
||||||
if (mBluetoothTether.isChecked()) {
|
if (mBluetoothTether.isChecked()) {
|
||||||
startTethering(TETHERING_BLUETOOTH);
|
startTethering(TETHERING_BLUETOOTH);
|
||||||
} else {
|
} else {
|
||||||
@@ -738,3 +747,4 @@ public class TetherSettings extends RestrictedDashboardFragment
|
|||||||
return TetherScreen.KEY;
|
return TetherScreen.KEY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// LINT.ThenChange(BluetoothTetherSwitchPreference.kt)
|
||||||
|
Reference in New Issue
Block a user