From e8999712d288c22618ecd46feb07f3afefb4fbdc Mon Sep 17 00:00:00 2001 From: andrekir Date: Fri, 7 Jan 2022 18:51:20 -0300 Subject: [PATCH] fix companion device pairing --- app/src/main/AndroidManifest.xml | 3 + .../java/com/geeksville/mesh/MainActivity.kt | 70 ++++++++++++++----- .../geeksville/mesh/ui/SettingsFragment.kt | 70 ++----------------- 3 files changed, 61 insertions(+), 82 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 940195c5d..91cfdb93d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,6 +44,9 @@ + + diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index e380da5ae..dd91a3397 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -6,6 +6,8 @@ import android.app.Activity import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager +import android.companion.AssociationRequest +import android.companion.BluetoothDeviceFilter import android.companion.CompanionDeviceManager import android.content.* import android.content.pm.PackageInfo @@ -70,6 +72,7 @@ import java.lang.Runnable import java.nio.charset.Charset import java.text.DateFormat import java.util.* +import java.util.regex.Pattern import kotlin.math.roundToInt @@ -131,8 +134,7 @@ class MainActivity : AppCompatActivity(), Logging, const val REQUEST_ENABLE_BT = 10 const val DID_REQUEST_PERM = 11 const val RC_SIGN_IN = 12 // google signin completed - const val RC_SELECT_DEVICE = - 13 // seems to be hardwired in CompanionDeviceManager to add 65536 + const val SELECT_DEVICE_REQUEST_CODE = 13 const val CREATE_CSV_FILE = 14 } @@ -194,11 +196,52 @@ class MainActivity : AppCompatActivity(), Logging, } } - private val btStateReceiver = BluetoothStateReceiver { _ -> updateBluetoothEnabled() } + fun startCompanionScan() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val deviceManager: CompanionDeviceManager by lazy { + getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager + } + + // To skip filtering based on name and supported feature flags (UUIDs), + // don't include calls to setNamePattern() and addServiceUuid(), + // respectively. This example uses Bluetooth. + // We only look for Mesh (rather than the full name) because NRF52 uses a very short name + val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() + .setNamePattern(Pattern.compile("Mesh.*")) + // .addServiceUuid(ParcelUuid(RadioInterfaceService.BTM_SERVICE_UUID), null) + .build() + + // The argument provided in setSingleDevice() determines whether a single + // device name or a list of device names is presented to the user as + // pairing options. + val pairingRequest: AssociationRequest = AssociationRequest.Builder() + .addDeviceFilter(deviceFilter) + .setSingleDevice(false) + .build() + + // When the app tries to pair with the Bluetooth device, show the + // appropriate pairing request dialog to the user. + deviceManager.associate(pairingRequest, + object : CompanionDeviceManager.Callback() { + override fun onDeviceFound(chooserLauncher: IntentSender) { + startIntentSenderForResult(chooserLauncher, + SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) + } + + override fun onFailure(error: CharSequence?) { + warn("BLE selection service failed $error") + // changeDeviceSelection(mainActivity, null) // deselect any device + } + }, null + ) + } + else warn("startCompanionScan should not run on SDK < 26") + } + /** * Don't tell our app we have bluetooth until we have bluetooth _and_ location access */ @@ -410,7 +453,6 @@ class MainActivity : AppCompatActivity(), Logging, } } - /// Ask user to rate in play store private fun askToRate() { exceptionReporter { // Got one IllegalArgumentException from inside this lib, but we don't want to crash our app because of bugs in this optional feature @@ -497,7 +539,6 @@ class MainActivity : AppCompatActivity(), Logging, requestPermission() } - private fun initToolbar() { val toolbar = findViewById(R.id.toolbar) as Toolbar @@ -601,25 +642,18 @@ class MainActivity : AppCompatActivity(), Logging, GoogleSignIn.getSignedInAccountFromIntent(data) handleSignInResult(task) } - (65536 + RC_SELECT_DEVICE) -> when (resultCode) { + (SELECT_DEVICE_REQUEST_CODE) -> when (resultCode) { Activity.RESULT_OK -> { - // User has chosen to pair with the Bluetooth device. - val device: BluetoothDevice = + val deviceToPair: BluetoothDevice = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!! - debug("Received BLE pairing ${device.address}") - if (device.bondState != BluetoothDevice.BOND_BONDED) { - device.createBond() - // FIXME - wait for bond to complete + if (deviceToPair.bondState != BluetoothDevice.BOND_BONDED) { + deviceToPair.createBond() } - - // ... Continue interacting with the paired device. model.meshService?.let { service -> - MeshService.changeDeviceAddress(this@MainActivity, service, device.address) + MeshService.changeDeviceAddress(this@MainActivity, service, "x${deviceToPair.address}") } } - - else -> - warn("BLE device select intent failed") + else -> warn("BLE device select intent failed") } CREATE_CSV_FILE -> { if (resultCode == Activity.RESULT_OK) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index 3d9a4feb5..bb8f0a9ff 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -7,9 +7,6 @@ import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice.BOND_BONDED import android.bluetooth.BluetoothDevice.BOND_BONDING import android.bluetooth.le.* -import android.companion.AssociationRequest -import android.companion.BluetoothDeviceFilter -import android.companion.CompanionDeviceManager import android.content.* import android.content.pm.PackageManager import android.hardware.usb.UsbDevice @@ -56,7 +53,6 @@ import com.hoho.android.usbserial.driver.UsbSerialDriver import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import java.util.regex.Pattern object SLogging : Logging @@ -106,7 +102,6 @@ private fun requestBonding( device.createBond() } - class BTScanModel(app: Application) : AndroidViewModel(app), Logging { private val context: Context get() = getApplication().applicationContext @@ -123,7 +118,6 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { else null - override fun toString(): String { return "DeviceListEntry(name=${name.anonymize}, addr=${address.anonymize})" } @@ -187,7 +181,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { if ((result.device.name?.startsWith("Mesh") == true)) { val addr = result.device.address - val fullAddr = "x$addr" // full address with the bluetooh prefix + val fullAddr = "x$addr" // full address with the bluetooth prefix added // prevent logspam because weill get get lots of redundant scan results val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED val oldDevs = devices.value!! @@ -456,10 +450,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { BluetoothInterface.hasCompanionDeviceApi(requireContext()) } - private val deviceManager: CompanionDeviceManager by lazy { - requireContext().getSystemService(CompanionDeviceManager::class.java) - } - private val myActivity get() = requireActivity() as MainActivity override fun onDestroy() { @@ -540,6 +530,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { val isConnected = connected == MeshService.ConnectionState.CONNECTED binding.nodeSettings.visibility = if (isConnected) View.VISIBLE else View.GONE + binding.provideLocationCheckbox.visibility = if (isConnected) View.VISIBLE else View.GONE if (connected == MeshService.ConnectionState.DISCONNECTED) model.ownerName.value = "" @@ -720,7 +711,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { device.address == scanModel.selectedNotNull && device.bonded // Only show checkbox if device is still paired binding.deviceRadioGroup.addView(b) - // Once we have at least one device, don't show the "looking for" animation - it makes uers think + // Once we have at least one device, don't show the "looking for" animation - it makes users think // something is busted binding.scanProgressBar.visibility = View.INVISIBLE @@ -830,56 +821,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { { updateDevicesButtons(scanModel.devices.value) }) } - /// Start running the modern scan, once it has one result we enable the - private fun startBackgroundScan() { - // Disable the change button until our scan has some results - binding.changeRadioButton.isEnabled = false - - // To skip filtering based on name and supported feature flags (UUIDs), - // don't include calls to setNamePattern() and addServiceUuid(), - // respectively. This example uses Bluetooth. - // We only look for Mesh (rather than the full name) because NRF52 uses a very short name - val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() - .setNamePattern(Pattern.compile("Mesh.*")) - // .addServiceUuid(ParcelUuid(RadioInterfaceService.BTM_SERVICE_UUID), null) - .build() - - // The argument provided in setSingleDevice() determines whether a single - // device name or a list of device names is presented to the user as - // pairing options. - val pairingRequest: AssociationRequest = AssociationRequest.Builder() - .addDeviceFilter(deviceFilter) - .setSingleDevice(false) - .build() - - // When the app tries to pair with the Bluetooth device, show the - // appropriate pairing request dialog to the user. - deviceManager.associate( - pairingRequest, - object : CompanionDeviceManager.Callback() { - - override fun onDeviceFound(chooserLauncher: IntentSender) { - debug("Found one device - enabling button") - binding.changeRadioButton.isEnabled = true - binding.changeRadioButton.setOnClickListener { - debug("User clicked BLE change button") - - // Request code seems to be ignored anyways - startIntentSenderForResult( - chooserLauncher, - MainActivity.RC_SELECT_DEVICE, null, 0, 0, 0, null - ) - } - } - - override fun onFailure(error: CharSequence?) { - warn("BLE selection service failed $error") - // changeDeviceSelection(mainActivity, null) // deselect any device - } - }, null - ) - } - private fun initModernScan() { // Turn off the widgets for the classic API binding.scanProgressBar.visibility = View.GONE @@ -895,8 +836,9 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { binding.scanStatusText.text = getString(R.string.not_paired_yet) binding.changeRadioButton.setText(R.string.select_radio) } - - startBackgroundScan() + binding.changeRadioButton.setOnClickListener { + myActivity.startCompanionScan() + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) {