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.concurrent.atomic.AtomicReference;
|
||||
|
||||
// LINT.IfChange
|
||||
/**
|
||||
* Displays preferences for Tethering.
|
||||
*/
|
||||
@@ -154,6 +155,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
return R.xml.tether_prefs;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@@ -205,16 +207,19 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
|
||||
mWifiTetherPreferenceController.displayPreference(getPreferenceScreen());
|
||||
|
||||
if (!bluetoothAvailable) {
|
||||
getPreferenceScreen().removePreference(mBluetoothTether);
|
||||
} else {
|
||||
BluetoothPan pan = mBluetoothPan.get();
|
||||
if (pan != null && pan.isTetheringOn()) {
|
||||
mBluetoothTether.setChecked(true);
|
||||
if (!isCatalystEnabled()) {
|
||||
if (!bluetoothAvailable) {
|
||||
mBluetoothTether.setVisible(false);
|
||||
} 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);
|
||||
// Set initial state based on Data Saver mode.
|
||||
onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
|
||||
@@ -263,7 +268,9 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
mDataSaverEnabled = isDataSaving;
|
||||
mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled);
|
||||
mUsbTether.setEnabled(!mDataSaverEnabled);
|
||||
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
||||
if (!isCatalystEnabled()) {
|
||||
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
||||
}
|
||||
mEthernetTether.setEnabled(!mDataSaverEnabled);
|
||||
mDataSaverFooter.setVisible(mDataSaverEnabled);
|
||||
}
|
||||
@@ -513,6 +520,8 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
}
|
||||
|
||||
private void updateBluetoothState() {
|
||||
if (isCatalystEnabled()) return;
|
||||
|
||||
final int btState = getBluetoothState();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateBluetoothState() btState : " + btState);
|
||||
@@ -568,7 +577,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
}
|
||||
|
||||
private void startTethering(int choice) {
|
||||
if (choice == TETHERING_BLUETOOTH) {
|
||||
if (choice == TETHERING_BLUETOOTH && !isCatalystEnabled()) {
|
||||
// Turn on Bluetooth first.
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
|
||||
@@ -590,7 +599,7 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
} else {
|
||||
mCm.stopTethering(TETHERING_USB);
|
||||
}
|
||||
} else if (preference == mBluetoothTether) {
|
||||
} else if (preference == mBluetoothTether && !isCatalystEnabled()) {
|
||||
if (mBluetoothTether.isChecked()) {
|
||||
startTethering(TETHERING_BLUETOOTH);
|
||||
} else {
|
||||
@@ -738,3 +747,4 @@ public class TetherSettings extends RestrictedDashboardFragment
|
||||
return TetherScreen.KEY;
|
||||
}
|
||||
}
|
||||
// LINT.ThenChange(BluetoothTetherSwitchPreference.kt)
|
||||
|
Reference in New Issue
Block a user