I am trying to test google nearby connections API by writing a simple, single class android program. Unfortunately I am not able to connect to physical devices, after starting discovering and advertising.
All code is located within single class. App is written with Kotlin by almost directly copying the code from API website and translating it from Java to Kotlin.
I don't know where to look for a problem. Logs doesn't seem to show any obvious results.
I kept one phone advertising for more than a couple of minutes and the other one discovering meanwhile. Bluetooth is on, and both devices are in one WiFi network with Internet access. I use Star Strategy.
EndpointDiscoveryCallback is never called.
Here are my implementations of startDiscovery and endpointCallback
private fun startClient() {
connClient.startDiscovery(deviceId, endpointDiscoveryCallback, discOptions)
.addOnSuccessListener {
Toast.makeText(
applicationContext,
"Discovering started",
Toast.LENGTH_SHORT
).show()
currentStateTextView.text = "discovering"
}.addOnFailureListener { exception ->
Toast.makeText(
applicationContext,
"Discovery failed, exception thrown: ${exception.message}.",
Toast.LENGTH_SHORT
).show()
}
}
private val endpointDiscoveryCallback = object : EndpointDiscoveryCallback() {
override fun onEndpointFound(endpointId: String, info: DiscoveredEndpointInfo) {
connClient.requestConnection(deviceId, endpointId, connectionLifecycleCallback)
.addOnSuccessListener {
}
.addOnFailureListener {
}
}
override fun onEndpointLost(endpointId: String) {
Toast.makeText(applicationContext, "Endpoint: $endpointId lost!", Toast.LENGTH_SHORT)
.show()
}
}
I succeeded to connect the same decives using sample app "Rock, Paper, Scissors" which is based on the same API. Unfortunately, till now i cannot find the clue why the devices wont find each other with my code, and where to look for a hint.
Related
I was having a problem implementing the Firebase anonymous sign-in function with Kotlin coroutine.
Following is the code for that:
Repository.kt
suspend fun getUserId(){
firebaseHelper.getUserId().collect{
if (it == "Successful"){
emit(it)
} else {
emit("Task unsuccessful")
}
}
}
FirebaseHelper.kt
fun getUserId() = flow {
val firebaseLoginAsync = Firebase.auth.signInAnonymously().await()
if (firebaseLoginAsync.user != null && !firebaseLoginAsync.user?.uid.isNullOrEmpty()) {
emit("Successful")
} else {
emit("Failed")
}
}
It works fine when the android device is connected to the internet.
But when I test this code without the internet it never completes, that is, the execution never reaches the if else block of FirebaseHelper.kt.
I was unable to find any resource that would help me understand the cause of this problem and any possible solution.
One idea that I can think of on the solution side is to forcefully cancel the await() functions execution after some time but I can't find anything related to implementation.
It works fine when the android device is connected to the internet.
Since an authentication operation requires an internet connection, then that's the expected behavior.
But when I test this code without the internet it never completes.
Without the internet, there is no way you can reach Firebase servers, hence that behavior. However, according to the official documentation of await() function:
This suspending function is cancellable. If the Job of the current coroutine is canceled or completed while this suspending function is waiting, this function immediately resumes with CancellationException.
Or you can simply check if the user is connected to the internet before performing the authentication.
The way that I made it work is with help of try catch block and withTimeout() function in FirebaseHelper.kt file. Following is the code of solution:
fun getUserID() = flow {
try {
val signInTask = Firebase.auth.signInAnonymously()
kotlinx.coroutines.withTimeout(5000) {
signInTask.await()
}
if (signInTask.isSuccessful){
emit("Successful")
} else {
emit("Failed")
}
} catch (e: Exception){
emit("Can't connect to the server\nPlease check your internet connection and retry")
}
}
withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T) runs the given suspend block for timeMillis milliseconds and throws TimeoutCancellationException if the timeout was exceeded.
I have the following code using an. RxAndroidBle Bluetooth Low Energy Connection:
private val connectionDisposable = CompositeDisposable()
private fun writeBle(writeCharacteristicUuid: UUID, command: ByteArray)
if (bleDevice.connectionState == RxBleConnection.RxBleConnectionState.CONNECTED) {
activeConnection
.flatMapSingle {
it.writeCharacteristic(writeCharacteristicUuid, command)
}
.subscribe({
Log.d(
TAG,
"${connectionDisposable.size()} - Command successful: ${it.toHexString()}"
)
})
{ Log.e(TAG, "Error executing command: $it") }
.let { connectionDisposable.add(it) }
} else {
Log.e(TAG, "You are not connected")
}
}
The connectionDisposable is .clear()ed when the connection to the device is closed.
But until then several hundreds, thousands or more disposable will land in the connectionDisposable.
I am not completely clear if this presents a Problem in regard to memory usage, or whether I am missing the right way to execute a lot of write commands (that should not be send simultaneously to the device).
i have an application, and my application can connect to a bluetooth device.
After that, i want to send message (Int) to my Blutooth Low Energy device.
I have this code, but i can't figure it out what is the problem.
If you want i have : Characteristic UUID, Service UUID.
Really, i need your help...
I've edited the question :
My code :
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
lateinit var bluetoothAdapter: BluetoothAdapter
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
settingViewModel.bluetooth(bluetoothAdapter = bluetoothAdapter)
val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
if (action == BluetoothAdapter.ACTION_STATE_CHANGED) {
val state = intent.getIntExtra(
BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR
)
when (state) {
BluetoothAdapter.STATE_OFF -> {
settingViewModel.setIsConnected(false)
//settingViewModel.stopScan()
settingViewModel.setListDevices(null)
}
BluetoothAdapter.STATE_ON -> {
settingViewModel.setIsConnected(true)
//scan()
settingViewModel.setListDevices(bluetoothAdapter.bondedDevices)
context!!.unregisterReceiver(this)
}
}
}
}
}
context.registerReceiver(mReceiver, filter)
val SERVICE_UUID = "00000000-0001-11e1-9ab4-0002a5d5c51c"
val ConfigCharacteristic = descriptorOf(
service = SERVICE_UUID,
characteristic = "00E00000-0001-11e1-ac36-0002a5d5c51b",
descriptor = "00000000-0000-0000-0000-000000000000",
)
Button(
onClick = {
if (settingViewModel.isConnected.value == true) {
coroutine.launch(Dispatchers.IO) {
try {
settingViewModel.peripheral.write(ConfigCharacteristic, byteArrayOf(1))
} catch (e: Exception) {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}
}
}
// try {
// val Service =
// settingViewModel.deviceSocket.value.get .getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"))
// val charac: BluetoothGattCharacteristic =
// Service.getCharacteristic(UUID.fromString("00E00000-0001-11e1-ac36-0002a5d5c51b"))
// settingViewModel.deviceSocket.value!!.outputStream.write("1".toByteArray())
// } catch (e: Exception) {
// Toast.makeText(context, e.message.toString(), Toast.LENGTH_LONG).show()
// }
}
) {
Text(text = "HelloWorld")
}
I Already have the mac adress, the caracteristic and the service UUID of the device i want to connect to.
Again, i really need your help
First of all:
When developing an app for a BLE device it is best to first use a generic BLE scanner app to test the connection and to find out which commands need to be sent. If you confirm that the BLE device works as expected you can continue with your own custom app. I would recommend nRF Connect for this task.
Regarding your problem:
There are still many things missing from your sourcecode. You said you can connect to the device but have problems sending a message. Your code does not contain anything related to a BLE connection so I can only assume that you connected to the device using the Bluetooth settings of your phone. This would be correct for Bluetooth Classic but BLE requires you to connect through your own custom app.
The Ultimate Guide to Android Bluetooth Low Energy explains all steps necessary for a successful BLE connection. These steps are:
Setting the correct permissions
Scan for nearby BLE devices
Connect to a BLE device of your choosing
Scan for Services
Read and Write a characteristic of your choosing
All these steps are explained in the Guide using Kotlin as programming language.
I am using BluetoothLeScanner to scan for BLE devices and get a list of objects representing the devices to show inside my app (not connecting to any of them).
I am interested in doing the same, but using the CompanionDeviceManager now. Its callback CompanionDeviceManager.Callback.onDeviceFound(chooserLauncher: IntentSender?) unfortunately does not return any human readable form of found devices... the closest it gets is the IntentSender.writeToParcel method, but I am not sure how to use it in this situation.
I am not constrained to use the CompanionDeviceManager but I wanted to follow the OS version specific guidelines, we are supposed to use CompanionDeviceManager for Bluetooth devices scanning starting from API 26, but it seems useless in my case... so is there any way to get devices data from that callback, or should I just ditch it and stay with BluetoothLeScanner for all OS versions?
Late answer but it might help someone else. You can create a bluetooth device picker in combination with ActivityResultContracts.StartIntentSenderForResult() in order to get the BluetoothDevice. From there you will have access to all the device info that you need. Recent changes added some Android 12 permissions like android.permission.BLUETOOTH_CONNECT. Your mileage may vary.
val context = LocalContext.current
// Get the device manager instance
val deviceManager: CompanionDeviceManager by lazy {
ContextCompat.getSystemService(
context,
CompanionDeviceManager::class.java
) as CompanionDeviceManager
}
// Create a filter of your choice. Here I just look for specific device names
val deviceFilter: BluetoothDeviceFilter by lazy {
BluetoothDeviceFilter.Builder()
.setNamePattern(Pattern.compile(supportedDevices))
.build()
}
// Create a pairing request with your filter from the last step
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.build()
// Create a picker for discovered bluetooth devices
val bluetoothDevicePicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult(),
onResult = {
val device: BluetoothDevice? =
it.data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
try {
// Now that you have the desired device, do what you need to with it
device?.apply {
when {
name?.matches(Regex(firstDevicePattern)) == true -> {
Log.i(TAG, "${this.name} connected")
onFirstDeviceDiscovered(device)
}
name?.matches(Regex(secondDevicePattern)) == true -> {
Log.i(TAG, "${this.name} connected")
onSecondDeviceDiscovered(device)
}
}
}
} catch (e: SecurityException) {
e.printStackTrace()
//TODO: handle the security exception (this is possibly a bug)
// https://issuetracker.google.com/issues/198986283
}
}
)
// A utility function to centralize calling associate (optional)
val associateDevice: (AssociationRequest) -> Unit = { request ->
// Attempt to associate device(s)
deviceManager.associate(
request,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
val sender = IntentSenderRequest.Builder(chooserLauncher)
.build()
bluetoothDevicePicker.launch(sender)
}
override fun onFailure(error: CharSequence?) {
//TODO: handle association failure
}
}, null
)
}
Nearby Message API Does not subscribe to Beacon Message. What am I supposed to do?
I finished testing whether "Nearby Message" works in Android 7.0 and Android 9.0. And I referenced this document(https://developers.google.com/nearby/messages/android/get-beacon-messages) but, it is still not working.
listener = object: MessageListener() {
override fun onFound(msg: Message) {
Log.i(TAG, "Found: $msg")
}
override fun onLost(msg: Message) {
Log.i(TAG, "Lost: $msg")
}
}
val options = SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build()
Nearby.getMessagesClient(this).subscribe(listener, options)
Nearby Message is subscribed to, but beacon message is not.
For messages, it needs both to be in Nearby with Manifest ...
<meta-data
android:name="com.google.android.nearby.messages.API_KEY"
android:value="Your-Key-from-Google Developer Console." />
Please follow this NearbyDevices getting-started .
or another from the same GitHub repo
Here, API Key is the "key"-thing !
e.g. Your-Key-from-Google Developer Console