0

I'm trying to convert the following reflection into Kotlin. The following uses reflection to call an RFCOMMs function so it can take a port/channel as an input instead of UUID. I have all my program in Kotlin. Anyone know how to write this in Kotlin?

int bt_port_to_connect = 5;
BluetoothDevice device = mDevice;
BluetoothSocket deviceSocket = null;
...

// IMPORTANT: we create a reference to the 'createInsecureRfcommSocket' method
// and not(!) to the 'createInsecureRfcommSocketToServiceRecord' (which is what the 
// android SDK documentation publishes

Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});

deviceSocket = (BluetoothSocket) m.invoke(device,bt_port_to_connect);

Updating with recommendation:

class BluetoothClient(device: BluetoothDevice): Thread() {
    // https://stackoverflow.com/questions/9703779/connecting-to-a-specific-bluetooth-port-on-a-bluetooth-device-using-android
    // Need to reflection - create RFCOMM socket to a port number instead of UUID
    // Invoke btdevice as 1st parameter and then the port number

    var bt_port_to_connect = 5

    var deviceSocket: BluetoothSocket? = null


    private val socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
    val m = device::class.declaredFunctions.single { it.name == "createInsecureRfcommSocket" }
    
    m.call(device, bt_port_to_connect)


    override fun run() {
        try {
            Log.i("client", "Connecting")
            this.socket.connect()

            Log.i("client", "Sending")
            val outputStream = this.socket.outputStream
            val inputStream = this.socket.inputStream
            try {
                outputStream.write(message.toByteArray())
                outputStream.flush()
                Log.i("client", "Sent")
            } catch(e: Exception) {
                Log.e("client", "Cannot send", e)
            } finally {
                outputStream.close()
                inputStream.close()
                this.socket.close()
            }
        }
        catch (e: IOException) {
            println("Socket Failed")
        }

    }
}
Stigma
  • 331
  • 1
  • 4
  • 15

1 Answers1

1

You can really use exactly the same code, just convert it to Kotlin:

val m = device::class.java.getMethod("createInsecureRfcommSocket", Int::class.java)
m.invoke(device, bt_port_to_connect)

Or you can use Kotlin reflection:

val m = device::class.declaredFunctions.single { it.name == "createInsecureRfcommSocket" }
m.call(device, bt_port_to_connect)

I don't know if there is any better way to find a function with provided name. You can create an extension function to make it cleaner. You may also need to check parameters if this function has overrides.

broot
  • 21,588
  • 3
  • 30
  • 35
  • Thanks for the recommendation. I updated the code but now I'm getting a member declaration error with the "m.call(device, bt_port_to_connect)" line in both Java and Kotlin examples. Do you know why that is? – Stigma Jun 28 '21 at 19:24
  • You put this line of code directly into the body of a class. Class can't hold arbitrary code, but only definitions of properties, functions, etc. You need to put it into some function. When do you need to run this code? In a constructor? – broot Jun 28 '21 at 20:00
  • I am calling this class BluetoothClient(mmDevice).start() in a Composable Button onclick modifier hoping to start this bluetooth port connection. I updated the code above. Am I able to put the m.call into my BluetoothClient class's run() function? When I do this, the Run() function also cannot reference the bluetoothdevice parameter device from the class. – Stigma Jun 28 '21 at 20:07
  • If you need to put this code in `run()` then you need to also store `device` as an instance property like this: `class BluetoothClient(private val device: BluetoothDevice)`. – broot Jun 28 '21 at 20:11