diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 81aa3a42a..ae08caa88 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -4,11 +4,7 @@ import android.Manifest import android.annotation.SuppressLint 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 import android.content.pm.PackageManager @@ -68,8 +64,6 @@ import kotlinx.coroutines.cancel import java.nio.charset.Charset import java.text.DateFormat import java.util.* -import java.util.regex.Pattern - /* UI design @@ -195,48 +189,6 @@ class MainActivity : AppCompatActivity(), Logging, 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 */ @@ -496,8 +448,6 @@ class MainActivity : AppCompatActivity(), Logging, /* not yet working // Configure sign-in to request the user's ID, email address, and basic -// profile. ID and basic profile are included in DEFAULT_SIGN_IN. - // Configure sign-in to request the user's ID, email address, and basic // profile. ID and basic profile are included in DEFAULT_SIGN_IN. val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) @@ -623,7 +573,6 @@ class MainActivity : AppCompatActivity(), Logging, /** * Dispatch incoming result to the correct fragment. */ - @SuppressLint("InlinedApi", "MissingPermission") override fun onActivityResult( requestCode: Int, resultCode: Int, @@ -631,7 +580,6 @@ class MainActivity : AppCompatActivity(), Logging, ) { super.onActivityResult(requestCode, resultCode, data) - // Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...); // Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...); when (requestCode) { RC_SIGN_IN -> { @@ -641,19 +589,6 @@ class MainActivity : AppCompatActivity(), Logging, GoogleSignIn.getSignedInAccountFromIntent(data) handleSignInResult(task) } - (SELECT_DEVICE_REQUEST_CODE) -> when (resultCode) { - Activity.RESULT_OK -> { - val deviceToPair: BluetoothDevice = - data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!! - if (deviceToPair.bondState != BluetoothDevice.BOND_BONDED) { - deviceToPair.createBond() - } - model.meshService?.let { service -> - MeshService.changeDeviceAddress(this@MainActivity, service, "x${deviceToPair.address}") - } - } - else -> warn("BLE device select intent failed") - } CREATE_CSV_FILE -> { if (resultCode == Activity.RESULT_OK) { data?.data?.let { file_uri -> model.saveMessagesCSV(file_uri) } 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 d016dc227..fcca3b178 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -1,28 +1,26 @@ package com.geeksville.mesh.ui import android.annotation.SuppressLint +import android.app.Activity import android.app.Application import android.app.PendingIntent 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 import android.hardware.usb.UsbManager -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.os.RemoteException +import android.os.* import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo -import android.widget.AdapterView -import android.widget.ArrayAdapter -import android.widget.RadioButton -import android.widget.TextView +import android.widget.* import androidx.fragment.app.activityViewModels import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData @@ -52,6 +50,7 @@ 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 @@ -456,6 +455,10 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { BluetoothInterface.hasCompanionDeviceApi(requireContext()) } + private val deviceManager: CompanionDeviceManager by lazy { + requireContext().getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager + } + private val myActivity get() = requireActivity() as MainActivity override fun onDestroy() { @@ -851,6 +854,49 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { } } + private fun startCompanionScan() { + // 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) { + try { + startIntentSenderForResult( + chooserLauncher, + MainActivity.SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0, null + ) + } catch (ex: Throwable) { + errormsg("CompanionDevice startIntentSenderForResult error") + } + } + + override fun onFailure(error: CharSequence?) { + warn("BLE selection service failed $error") + // changeDeviceSelection(myActivity, null) // deselect any device + } + }, null + ) + } + private fun initModernScan() { binding.changeRadioButton.setOnClickListener { @@ -859,7 +905,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { myActivity.requestScanPermission() } else { // checkLocationEnabled() // ? some phones still need location turned on - myActivity.startCompanionScan() + startCompanionScan() } } } @@ -983,4 +1029,20 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { } } } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && requestCode == MainActivity.SELECT_DEVICE_REQUEST_CODE + && resultCode == Activity.RESULT_OK + ) { + val deviceToPair: BluetoothDevice = + data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!! + if (deviceToPair.bondState != BOND_BONDED) { + deviceToPair.createBond() + } + changeDeviceSelection(myActivity, "x${deviceToPair.address}") + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } }