Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Bluetooth Android Plugin Problem

Discussion in 'Android' started by MaxBa, May 1, 2020.

  1. MaxBa

    MaxBa

    Joined:
    Oct 27, 2018
    Posts:
    2
    Hi,

    I wrote my own Android plugin in order to make my Android Unity app able to make use of Bluetooth Low Energy.
    To do so, I simply created a Kotlin class that initializes everything and automatically connects to my desired remote device. I put this file into my Assets > Plugins > Android folder and load it in a C# script by using AndroidJavaObject. AndroidJavaObject also works with Kotlin files.

    Everything works fine which means that I can connect to my remote device. The problem is that my code starts scanning for devices but will only return with scan results once I pressed the home button. This seems pretty much like an issue with Lifecycle events but so far I was not able to figure out what exactly causes the problem.
    The only thing I noticed is that 'onScanResult' function runs in a different thread than 'scanLeDevice'. Is that causing the problem?
    I tried running the code within 'onScanResult' on the UI thread but for some reason the Thread ID was still different than the UI Thread ID (see code).

    Here's my scan function within my Kotlin file:

    Code (CSharp):
    1. class BluetoothService {
    2.  
    3.     private var context: Activity? = null
    4.     private var tagConnection: BluetoothGatt? = null
    5.     private var bluetoothAdapter: BluetoothAdapter? = null
    6.     private var tagIsConnected = true
    7.  
    8.     fun setContext(context: Activity){
    9.         println("Main Thread ID: ${Thread.currentThread().getId()}")     // ID: 149
    10.         println("Setting context")
    11.         this.context = context
    12.         val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    13.         bluetoothAdapter = bluetoothManager.adapter
    14.     }
    15.  
    16.     fun scanLeDevice() {
    17.         val scanner = bluetoothAdapter?.bluetoothLeScanner
    18.         println("Thread ID: ${Thread.currentThread().getId()}")    // ID: 149
    19.         println("Scan started")
    20. // UP UNTIL HERE EVERYTHING IS CALLED PROPERLY
    21.  
    22.         scanner?.startScan(object: ScanCallback(){
    23. // STARTING FROM HERE THIS CALLBACK FUNCTION IS ONLY CALLED AFTER THE HOMEBUTTON WAS PRESSED
    24.             override fun onScanResult(callbackType: Int, result: ScanResult?) {
    25.                 println("New scan result")
    26.                 println("Thread ID: ${Thread.currentThread().getId()}")    // ID: 2
    27.                 super.onScanResult(callbackType, result)
    28.                 val device = result?.device
    29.                 if (device?.address == TAG_MAC){
    30.                     println("Found tag")
    31.                     scanner.stopScan(this)
    32.                     device.connectGatt(context, false, gattCallback)
    33.                 }
    34.                 }
    35.  
    36.             override fun onScanFailed(errorCode: Int) {
    37.                 println("Scan failed")
    38.                 println(errorCode.toString())
    39.             }
    40.         })
    41.     }
    42. }
    Here's my C# class that loads the plugin:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Android;
    5.  
    6. public class UnityBluetoothLoader : MonoBehaviour
    7. {
    8.     private AndroidJavaObject bluetoothService;
    9.     private AndroidJavaObject activityContext;
    10.  
    11.     // Start is called before the first frame update
    12.     void Start()
    13.     {
    14.         Permission.RequestUserPermission("android.permission.BLUETOOTH");
    15.         Permission.RequestUserPermission("android.permission.BLUETOOTH_ADMIN");
    16.         Permission.RequestUserPermission("android.permission.ACCESS_FINE_LOCATION");
    17.         Permission.RequestUserPermission("android.permission.ACCESS_COARSE_LOCATION");
    18.  
    19.         Debug.Log("Starting ...");
    20.         using(AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
    21.             Debug.Log("Initializing context");
    22.             activityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
    23.         }
    24.  
    25.         using(bluetoothService = new AndroidJavaObject("XX.XX.XX")) {
    26.             Debug.Log("Initialized bluetooth service");
    27.             bluetoothService.Call("setContext", activityContext);
    28.             bluetoothService.Call("scanLeDevice");
    29.         }
    30.     }
    31. }
    Attached are two images in which you can see the outputs of adb logcat. The first image shows the output before I pressed the homebutton and the second image shows the output when I returned to homescreen.

    Before homebutton pressed:
    before_homebutton_pressed.png

    After homebutton pressed:
    after_homebutton_pressed.png

    Since my goal is to develop a VR app the user won't be able to press the homebutton and return to the app again. Apart from that, the user experience would obviously suck.
    Can somebody explain this?
    Thanks so much,
    Max
     
    Last edited: May 1, 2020
  2. MaxBa

    MaxBa

    Joined:
    Oct 27, 2018
    Posts:
    2
    Hello,
    I found a workaround for my problem. It works but it stills dissatisfies since I wasn't able to solve my original issue.
    Anyway, for anyone who's interested, this is my current function in order to establish a connection to a Bluetooth LE device within a self-written Android Plugin:

    Code (CSharp):
    1. // New function that works in a Unity Android Plugin.
    2.     // Instead of scanning nearby devices, iterate through the list of found devices
    3.     // and then connect to the desired one,
    4.     // the desired remote device is found with bluetoothAdapter.getRemoteDevice()
    5.     fun connectToTag(){
    6.         val device = bluetoothAdapter!!.getRemoteDevice(REMOTE_UUID)
    7.         if (device == null) {
    8.             println("Device not found")
    9.             return
    10.         }
    11.         // We want to directly connect to the device, so we are setting the autoConnect
    12.         // parameter to false.
    13.         tagConnection = device.connectGatt(context, false, gattCallback)
    14.     }
    Greetings,
    Max
     
    valdeezzee likes this.