kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
add bluetooth_connect permission checks
rodzic
a2f5d74bfc
commit
dc852b97ba
|
@ -43,11 +43,7 @@ import com.geeksville.android.GeeksvilleApplication
|
||||||
import com.geeksville.android.Logging
|
import com.geeksville.android.Logging
|
||||||
import com.geeksville.android.ServiceClient
|
import com.geeksville.android.ServiceClient
|
||||||
import com.geeksville.concurrent.handledLaunch
|
import com.geeksville.concurrent.handledLaunch
|
||||||
import com.geeksville.mesh.android.getLocationPermissions
|
import com.geeksville.mesh.android.*
|
||||||
import com.geeksville.mesh.android.getBackgroundPermissions
|
|
||||||
import com.geeksville.mesh.android.getCameraPermissions
|
|
||||||
import com.geeksville.mesh.android.getMissingPermissions
|
|
||||||
import com.geeksville.mesh.android.getScanPermissions
|
|
||||||
import com.geeksville.mesh.database.entity.Packet
|
import com.geeksville.mesh.database.entity.Packet
|
||||||
import com.geeksville.mesh.databinding.ActivityMainBinding
|
import com.geeksville.mesh.databinding.ActivityMainBinding
|
||||||
import com.geeksville.mesh.model.ChannelSet
|
import com.geeksville.mesh.model.ChannelSet
|
||||||
|
@ -249,15 +245,8 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
*/
|
*/
|
||||||
private fun updateBluetoothEnabled() {
|
private fun updateBluetoothEnabled() {
|
||||||
var enabled = false // assume failure
|
var enabled = false // assume failure
|
||||||
val requiredPerms: MutableList<String> = mutableListOf()
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (hasConnectPermission()) {
|
||||||
requiredPerms.add(Manifest.permission.BLUETOOTH_CONNECT)
|
|
||||||
} else {
|
|
||||||
requiredPerms.add(Manifest.permission.BLUETOOTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getMissingPermissions(requiredPerms).isEmpty()) {
|
|
||||||
/// ask the adapter if we have access
|
/// ask the adapter if we have access
|
||||||
bluetoothAdapter?.apply {
|
bluetoothAdapter?.apply {
|
||||||
enabled = isEnabled
|
enabled = isEnabled
|
||||||
|
@ -309,6 +298,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
/**
|
/**
|
||||||
* @return a localized string warning user about missing permissions. Or null if everything is find
|
* @return a localized string warning user about missing permissions. Or null if everything is find
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
fun getMissingMessage(
|
fun getMissingMessage(
|
||||||
missingPerms: List<String> = getMinimumPermissions()
|
missingPerms: List<String> = getMinimumPermissions()
|
||||||
): String? {
|
): String? {
|
||||||
|
@ -338,7 +328,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Possibly prompt user to grant permissions
|
/** Possibly prompt user to grant permissions
|
||||||
* @param shouldShowDialog usually true, but in cases where we've already shown a dialog elsewhere we skip it.
|
* @param shouldShowDialog usually false in cases where we've already shown a dialog elsewhere we skip it.
|
||||||
*
|
*
|
||||||
* @return true if we already have the needed permissions
|
* @return true if we already have the needed permissions
|
||||||
*/
|
*/
|
||||||
|
@ -640,7 +630,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
/**
|
/**
|
||||||
* Dispatch incoming result to the correct fragment.
|
* Dispatch incoming result to the correct fragment.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi", "MissingPermission")
|
||||||
override fun onActivityResult(
|
override fun onActivityResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
resultCode: Int,
|
resultCode: Int,
|
||||||
|
@ -1075,18 +1065,21 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
||||||
// Ask to start bluetooth if no USB devices are visible
|
// Ask to start bluetooth if no USB devices are visible
|
||||||
val hasUSB = SerialInterface.findDrivers(this).isNotEmpty()
|
val hasUSB = SerialInterface.findDrivers(this).isNotEmpty()
|
||||||
if (!isInTestLab && !hasUSB) {
|
if (!isInTestLab && !hasUSB) {
|
||||||
|
if (hasConnectPermission()) {
|
||||||
bluetoothAdapter?.let {
|
bluetoothAdapter?.let {
|
||||||
if (!it.isEnabled) {
|
if (!it.isEnabled) {
|
||||||
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||||
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
|
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else requestPermission()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -29,6 +29,24 @@ fun Context.getMissingPermissions(perms: List<String>) = perms.filter {
|
||||||
) != PackageManager.PERMISSION_GRANTED
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bluetooth connect permissions (or empty if we already have what we need)
|
||||||
|
*/
|
||||||
|
fun Context.getConnectPermissions(): List<String> {
|
||||||
|
val perms = mutableListOf<String>()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
perms.add(Manifest.permission.BLUETOOTH_CONNECT)
|
||||||
|
} else {
|
||||||
|
perms.add(Manifest.permission.BLUETOOTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMissingPermissions(perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true if the user already has Bluetooth connect permission */
|
||||||
|
fun Context.hasConnectPermission() = getConnectPermissions().isEmpty()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bluetooth scan/discovery permissions (or empty if we already have what we need)
|
* Bluetooth scan/discovery permissions (or empty if we already have what we need)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,11 +33,7 @@ import com.geeksville.android.isGooglePlayAvailable
|
||||||
import com.geeksville.mesh.MainActivity
|
import com.geeksville.mesh.MainActivity
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.RadioConfigProtos
|
import com.geeksville.mesh.RadioConfigProtos
|
||||||
import com.geeksville.mesh.android.bluetoothManager
|
import com.geeksville.mesh.android.*
|
||||||
import com.geeksville.mesh.android.hasScanPermission
|
|
||||||
import com.geeksville.mesh.android.hasLocationPermission
|
|
||||||
import com.geeksville.mesh.android.hasBackgroundPermission
|
|
||||||
import com.geeksville.mesh.android.usbManager
|
|
||||||
import com.geeksville.mesh.databinding.SettingsFragmentBinding
|
import com.geeksville.mesh.databinding.SettingsFragmentBinding
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.mesh.service.*
|
import com.geeksville.mesh.service.*
|
||||||
|
@ -68,6 +64,7 @@ fun changeDeviceSelection(context: MainActivity, newAddr: String?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the UI asking the user to bond with a device, call changeSelection() if/when bonding completes
|
/// Show the UI asking the user to bond with a device, call changeSelection() if/when bonding completes
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
private fun requestBonding(
|
private fun requestBonding(
|
||||||
activity: MainActivity,
|
activity: MainActivity,
|
||||||
device: BluetoothDevice,
|
device: BluetoothDevice,
|
||||||
|
@ -102,7 +99,11 @@ private fun requestBonding(
|
||||||
activity.registerReceiver(bondChangedReceiver, filter)
|
activity.registerReceiver(bondChangedReceiver, filter)
|
||||||
|
|
||||||
// We ignore missing BT adapters, because it lets us run on the emulator
|
// We ignore missing BT adapters, because it lets us run on the emulator
|
||||||
|
try {
|
||||||
device.createBond()
|
device.createBond()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
SLogging.warn("Failed creating Bluetooth bond: ${ex.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
|
@ -180,13 +181,14 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
// For each device that appears in our scan, ask for its GATT, when the gatt arrives,
|
// For each device that appears in our scan, ask for its GATT, when the gatt arrives,
|
||||||
// check if it is an eligable device and store it in our list of candidates
|
// check if it is an eligable device and store it in our list of candidates
|
||||||
// if that device later disconnects remove it as a candidate
|
// if that device later disconnects remove it as a candidate
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||||
|
|
||||||
if ((result.device.name?.startsWith("Mesh") == true)) {
|
if ((result.device.name?.startsWith("Mesh") == true)) {
|
||||||
val addr = result.device.address
|
val addr = result.device.address
|
||||||
val fullAddr = "x$addr" // full address with the bluetooth prefix added
|
val fullAddr = "x$addr" // full address with the bluetooth prefix added
|
||||||
// prevent logspam because weill get get lots of redundant scan results
|
// prevent logspam because weill get get lots of redundant scan results
|
||||||
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
|
val isBonded = result.device.bondState == BOND_BONDED
|
||||||
val oldDevs = devices.value!!
|
val oldDevs = devices.value!!
|
||||||
val oldEntry = oldDevs[fullAddr]
|
val oldEntry = oldDevs[fullAddr]
|
||||||
if (oldEntry == null || oldEntry.bonded != isBonded) { // Don't spam the GUI with endless updates for non changing nodes
|
if (oldEntry == null || oldEntry.bonded != isBonded) { // Don't spam the GUI with endless updates for non changing nodes
|
||||||
|
@ -222,6 +224,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
devices.value = oldDevs // trigger gui updates
|
devices.value = oldDevs // trigger gui updates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
fun stopScan() {
|
fun stopScan() {
|
||||||
if (scanner != null) {
|
if (scanner != null) {
|
||||||
debug("stopping scan")
|
debug("stopping scan")
|
||||||
|
@ -296,6 +299,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
fun startScan() {
|
fun startScan() {
|
||||||
/// The following call might return null if the user doesn't have bluetooth access permissions
|
/// The following call might return null if the user doesn't have bluetooth access permissions
|
||||||
val bluetoothLeScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner
|
val bluetoothLeScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner
|
||||||
|
@ -745,6 +749,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
private fun updateDevicesButtons(devices: MutableMap<String, BTScanModel.DeviceListEntry>?) {
|
private fun updateDevicesButtons(devices: MutableMap<String, BTScanModel.DeviceListEntry>?) {
|
||||||
// Remove the old radio buttons and repopulate
|
// Remove the old radio buttons and repopulate
|
||||||
binding.deviceRadioGroup.removeAllViews()
|
binding.deviceRadioGroup.removeAllViews()
|
||||||
|
@ -767,7 +772,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
// and before use
|
// and before use
|
||||||
val bleAddr = scanModel.selectedBluetooth
|
val bleAddr = scanModel.selectedBluetooth
|
||||||
|
|
||||||
if (bleAddr != null && adapter != null) {
|
if (bleAddr != null && adapter != null && myActivity.hasConnectPermission()) {
|
||||||
val bDevice =
|
val bDevice =
|
||||||
adapter.getRemoteDevice(bleAddr)
|
adapter.getRemoteDevice(bleAddr)
|
||||||
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
|
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
|
||||||
|
|
Ładowanie…
Reference in New Issue