MainActivity.kt 5.58 KB
Newer Older
Anon's avatar
Anon committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
package com.github.antweb.donkey

import android.bluetooth.*
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.widget.Button
import android.widget.TextView
import java.lang.NullPointerException

class MainActivity : AppCompatActivity() {

    private val TAG = "MainActivity"

    private val targetDeviceName = "card10"
    private val mtu = 128
    private val serviceUuid = "00422342-2342-2342-2342-234223422342"
    private val dataCharacteristicUuid = "01422342-2342-2342-2342-234223422342"

    private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
        val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        bluetoothManager.adapter
    }

    private var bluetoothGatt: BluetoothGatt? = null
    private var fileService: BluetoothGattService? = null
    private var dataCharacteristic: BluetoothGattCharacteristic? = null
    private var mGatt: BluetoothGatt? = null
    private var fileTransferService: FileTransfer? = null

    private var mScanning: Boolean = false
    private var connected = false

    private lateinit var tvConnection: TextView
    private lateinit var tvValue: TextView
    private lateinit var buttonConnect: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvValue = findViewById(R.id.text_value)
        tvConnection = findViewById(R.id.text_connection_status)
        tvConnection.text = "STATE_DISCONNECTED"

        buttonConnect = findViewById(R.id.button_connect)
        buttonConnect.setOnClickListener {
            scanLeDevice()
        }
    }

    private fun checkPermissions() {
//        if (bluetoothAdapter == null || !(bluetoothAdapter?.isEnabled)) {
//            val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
//            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
//        }
    }

    private fun scanLeDevice() {
        val gattCallback = object : BluetoothGattCallback() {
            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
                super.onServicesDiscovered(gatt, status)

                if (gatt == null) {
                    throw NullPointerException()
                }

                for (service in gatt.services) {
                    Log.d(TAG, "Found service: ${service.uuid}")

                    if (service.uuid.toString() == serviceUuid) {
                        fileService = service
                    }

                    for (characteristic in service.characteristics) {
                        Log.d(TAG, "Characteristic: ${characteristic.uuid}")

                        if (characteristic.uuid.toString() == dataCharacteristicUuid) {
                            dataCharacteristic = characteristic
                        }
                    }
                }

                if (fileService == null || dataCharacteristic == null) {
                    Log.e(TAG, "Could not find file transfer service")
                    return
                }

                gatt.requestMtu(mtu)
            }

            override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                super.onConnectionStateChange(gatt, status, newState)

                when (newState) {
                    BluetoothGatt.STATE_CONNECTED -> {
                        runOnUiThread {
                            tvConnection.text = "STATE_CONNECTED"
                        }

                        mGatt = gatt
                        gatt?.discoverServices()
                    }
                    BluetoothGatt.STATE_DISCONNECTED -> tvConnection.text = "STATE_DISCONNECTED"
                    BluetoothGatt.STATE_CONNECTING -> tvConnection.text = "STATE_CONNECTING"
                    BluetoothGatt.STATE_DISCONNECTING -> tvConnection.text = "STATE_DISCONNECTING"
                }
            }

            override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
                Log.d(TAG, "MTU changed to: $mtu")

                runOnUiThread {
                    tvValue.text = "MTU: $mtu"
                }

                val lData = dataCharacteristic
                if (gatt != null && lData != null) {
                    fileTransferService = FileTransfer(gatt, lData, mtu)
                    fileTransferService?.sendFile()
                }
            }

            override fun onReliableWriteCompleted(gatt: BluetoothGatt?, status: Int) {
                // Last chunk sent successfully. Send next chunk
                fileTransferService?.sendNext()
            }

            override fun onCharacteristicWrite(
                gatt: BluetoothGatt?,
                characteristic: BluetoothGattCharacteristic?,
                status: Int
            ) {
                gatt?.executeReliableWrite()
            }
        }

        val leScanCallback = BluetoothAdapter.LeScanCallback { device, _, _ ->

            if (device.name == targetDeviceName) {
                if (!connected) {
                    connected = true
                    bluetoothGatt = device.connectGatt(this, true, gattCallback, BluetoothDevice.TRANSPORT_LE)
                }
            }
        }

        // Stops scanning after a pre-defined period
        Handler().postDelayed({
            mScanning = false
            bluetoothAdapter?.stopLeScan(leScanCallback)
        }, 5000)
        mScanning = true
        bluetoothAdapter?.startLeScan(leScanCallback)
    }
}