338 lines
14 KiB
Kotlin
338 lines
14 KiB
Kotlin
package com.example.oilcheckkotlin
|
|
|
|
import android.Manifest
|
|
import android.bluetooth.BluetoothDevice
|
|
import android.bluetooth.BluetoothGatt
|
|
import android.bluetooth.BluetoothGattCallback
|
|
import android.bluetooth.BluetoothGattCharacteristic
|
|
import android.bluetooth.BluetoothManager
|
|
import android.bluetooth.BluetoothProfile
|
|
import android.bluetooth.le.BluetoothLeScanner
|
|
import android.bluetooth.le.ScanCallback
|
|
import android.bluetooth.le.ScanResult
|
|
import android.content.Context
|
|
import android.content.pm.PackageManager
|
|
import android.os.Build
|
|
import android.util.Log
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.core.app.ActivityCompat
|
|
import java.util.Locale
|
|
|
|
class BLEController(ctx: Context) : BLEControllerListener{
|
|
companion object{
|
|
var instance: BLEController? = null
|
|
fun getInstance(ctx: Context?): BLEController? {
|
|
Log.d("BLEController", "called")
|
|
if (null == instance){
|
|
instance = BLEController(ctx!!)
|
|
}
|
|
return instance
|
|
}
|
|
}
|
|
private val SCAN_PERIOD: Long = 10000
|
|
|
|
private var deviceAddress: String? = null
|
|
|
|
var ctx: Context
|
|
private var devices = hashMapOf<String, BluetoothDevice>()
|
|
private var scanner: BluetoothLeScanner? = null
|
|
var bluetoothManager: BluetoothManager? = null
|
|
private var btGattChar: BluetoothGattCharacteristic? = null
|
|
private lateinit var bluetoothGatt: BluetoothGatt
|
|
private var device: BluetoothDevice? = null
|
|
private val listeners: ArrayList<BLEControllerListener> = ArrayList<BLEControllerListener>()
|
|
private var connectionState = false
|
|
|
|
fun stopScanner(){
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_SCAN
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
try {
|
|
scanner!!.stopScan(bleCallback)
|
|
}
|
|
catch (e: Exception){
|
|
Log.d("Debug", "Scanner seems to run, stopping")
|
|
}
|
|
}
|
|
|
|
// constructor
|
|
init {
|
|
Log.d("Debug", "constructor called")
|
|
this.bluetoothManager = ctx.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
|
this.ctx = ctx
|
|
}
|
|
|
|
fun init(){
|
|
Log.d("Debug", "init")
|
|
this.devices?.clear()
|
|
Log.d("Debug", "get scanner")
|
|
this.scanner = this.bluetoothManager?.adapter?.bluetoothLeScanner
|
|
Log.d("Debug", "got scanner")
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_SCAN
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
|
// requestBluetooth.launch(enableBtIntent)
|
|
Log.d("Debug", "permission needed")
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
} else{
|
|
Log.d("Debug", "permission granted")
|
|
}
|
|
}
|
|
|
|
Log.d("Debug", "try to scan")
|
|
|
|
this.scanner?.startScan(bleCallback)
|
|
|
|
}
|
|
var bleCallback = object: ScanCallback(){
|
|
|
|
override fun onScanResult(callbackType: Int, result: ScanResult?) {
|
|
// Log.d("Debug", "onScanResult called")
|
|
val device = result?.device
|
|
// Log.d("Debug", "device result exists")
|
|
if (!devices?.containsKey(device?.address)!! && isThisTheDevice(device)) {
|
|
Log.d("Debug", "found the device")
|
|
deviceFound(device!!)
|
|
Log.d("Debug", device.address)
|
|
|
|
|
|
}
|
|
}
|
|
|
|
override fun onBatchScanResults(results: List<ScanResult?>?) {
|
|
Log.d("Debug", "onBatchScanResults called")
|
|
for (sr in results!!) {
|
|
val device = sr?.device
|
|
if (!devices!!.containsKey(device!!.address) && isThisTheDevice(device)) {
|
|
deviceFound(device!!)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onScanFailed(errorCode: Int) {
|
|
Log.i("[BLE]", "scan failed with errorcode: $errorCode")
|
|
}
|
|
}
|
|
private fun isThisTheDevice(device: BluetoothDevice?): Boolean {
|
|
try {
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_CONNECT
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return false
|
|
}
|
|
Log.d("BLE", device!!.name)
|
|
} catch (e: Exception) {
|
|
Log.d("BLE", "Device with null-Name found")
|
|
}
|
|
return null != device!!.name && device.name.startsWith("OilCheck")
|
|
}
|
|
private fun deviceFound(device: BluetoothDevice) {
|
|
Log.d("Debug", "deviceFound called")
|
|
// connectToDevice(device.address)
|
|
devices.put(device.address, device)
|
|
fireDeviceFound(device)
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_SCAN
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
scanner!!.stopScan(bleCallback)
|
|
}
|
|
private fun fireDeviceFound(device: BluetoothDevice) {
|
|
// for (l in listeners) {
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_CONNECT
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
Log.d("Debug", "BLEDeviceFound called")
|
|
BLEDeviceFound(device.name.trim { it <= ' ' }, device.address)
|
|
// }
|
|
}
|
|
fun connectToDevice(address: String?) {
|
|
Log.d("Debug", "connectToDevice")
|
|
this.device = devices[address!!]
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_SCAN
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
scanner!!.stopScan(bleCallback)
|
|
try {
|
|
Log.i("[BLE]", "connect to device " + device!!.getAddress())
|
|
} catch (e: java.lang.Exception) {
|
|
Log.d("Debug", "Connection to device failed :(")
|
|
}
|
|
this.bluetoothGatt = device!!.connectGatt(null, true, this.bleConnectCallback)
|
|
}
|
|
|
|
private val bleConnectCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
|
|
val CHANNEL_ID = "ForegroundServiceChannel"
|
|
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
|
connectionState = true
|
|
Log.d("Debug", "connection state changed to connected")
|
|
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_CONNECT
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
Log.i("[BLE]", "start service discovery " + bluetoothGatt.discoverServices())
|
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
|
connectionState = false
|
|
|
|
Log.d("Debug", "connection state changed to not connected")
|
|
btGattChar = null
|
|
Log.w("[BLE]", "DISCONNECTED with status $status")
|
|
fireDisconnected()
|
|
} else {
|
|
Log.i("[BLE]", "unknown state $newState and status $status")
|
|
}
|
|
}
|
|
|
|
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
|
if (null == btGattChar) {
|
|
for (service in gatt.services) {
|
|
if (service.uuid.toString().uppercase(Locale.getDefault())
|
|
.startsWith("00001811")
|
|
) {
|
|
val gattCharacteristics = service.characteristics
|
|
for (bgc in gattCharacteristics) {
|
|
if (bgc.uuid.toString().uppercase(Locale.getDefault())
|
|
.startsWith("00002A46")
|
|
) {
|
|
val chprop = bgc.properties
|
|
if (chprop and BluetoothGattCharacteristic.PROPERTY_WRITE or (chprop and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
|
|
btGattChar = bgc
|
|
Log.i("[BLE]", "CONNECTED and ready to send")
|
|
fireConnected()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private fun fireDisconnected() {
|
|
for (l in listeners){
|
|
l.BLEControllerDisconnected()
|
|
}
|
|
device = null
|
|
}
|
|
fun isConnected():Boolean {
|
|
if(!connectionState){
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
fun sendData(data: ByteArray?) {
|
|
btGattChar!!.value = data
|
|
if (ActivityCompat.checkSelfPermission(
|
|
ctx,
|
|
Manifest.permission.BLUETOOTH_CONNECT
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
) {
|
|
// TODO: Consider calling
|
|
// ActivityCompat#requestPermissions
|
|
// here to request the missing permissions, and then overriding
|
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
// int[] grantResults)
|
|
// to handle the case where the user grants the permission. See the documentation
|
|
// for ActivityCompat#requestPermissions for more details.
|
|
return
|
|
}
|
|
bluetoothGatt.writeCharacteristic(btGattChar)
|
|
}
|
|
private fun fireConnected() {
|
|
for (l in listeners) {
|
|
l.BLEControllerConnected()
|
|
}
|
|
}
|
|
|
|
override fun BLEControllerConnected() {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun BLEControllerDisconnected() {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun BLEDeviceFound(name: String?, address: String?) {
|
|
// log("Device $name found with address $address")
|
|
deviceAddress = address
|
|
Log.d("Debug", "connectToDevice called")
|
|
// btnConnect.setEnabled(true)
|
|
connectToDevice(deviceAddress)
|
|
}
|
|
|
|
|
|
} |