Files
SicherheitssystemAndroid/app/src/main/java/com/example/oilcheckkotlin/BLEController.kt
2023-09-23 19:09:54 +02:00

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)
}
}