diff --git a/app/src/main/java/com.alphasync/bluetooth/ConnectionEventListener.kt b/app/src/main/java/com.alphasync/bluetooth/ConnectionEventListener.kt index 80dc66d..e5712d7 100644 --- a/app/src/main/java/com.alphasync/bluetooth/ConnectionEventListener.kt +++ b/app/src/main/java/com.alphasync/bluetooth/ConnectionEventListener.kt @@ -8,5 +8,5 @@ class ConnectionEventListener { var onConnectionSetupComplete: ((BluetoothGatt) -> Unit)? = null var onDisconnect: (() -> Unit)? = null var onCharacteristicWrite: ((BluetoothDevice, BluetoothGattCharacteristic) -> Unit)? = null - + var onBluetoothStatusChange: ((Boolean) -> Unit)? = null } diff --git a/app/src/main/java/com.alphasync/bluetooth/ConnectionManager.kt b/app/src/main/java/com.alphasync/bluetooth/ConnectionManager.kt index 7938187..35b1d08 100644 --- a/app/src/main/java/com.alphasync/bluetooth/ConnectionManager.kt +++ b/app/src/main/java/com.alphasync/bluetooth/ConnectionManager.kt @@ -7,8 +7,13 @@ import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.util.Log +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.RECEIVER_EXPORTED import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import java.lang.ref.WeakReference @@ -21,6 +26,7 @@ class ConnectionManager(context: Context) { context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager } private var btAdapter: BluetoothAdapter = btManager.adapter + private val btAdapterFilter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) private var btGatt: BluetoothGatt? = null private var btGattIsConnecting: Boolean = false private lateinit var callerContext: Context @@ -58,6 +64,7 @@ class ConnectionManager(context: Context) { btAddress = address callerContext = context + ContextCompat.registerReceiver(context, bluetoothStateReceiver, btAdapterFilter, RECEIVER_EXPORTED) val btDevice = btAdapter.getRemoteDevice(address) if (btGattIsConnecting) { Log.d(logTag, "Device is currently connecting.") @@ -77,20 +84,25 @@ class ConnectionManager(context: Context) { listeners.forEach { it.get()?.onDisconnect?.invoke() } } - btGattIsConnecting = false - btGatt?.close() - btGatt = null + disconnect() connect(btAddress, callerContext) } + @SuppressLint("MissingPermission") + private fun disconnect() { + btGattIsConnecting = false + btGatt?.disconnect() + btGatt = null + } + @SuppressLint("MissingPermission") fun writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - payload: ByteArray - ) { + characteristicId: UUID, + payload: ByteArray) + { if (isConnected) { - btGatt?.findCharacteristic(characteristic.uuid)?.let { characteristic -> + btGatt?.findCharacteristic(characteristicId)?.let { characteristic -> characteristic.value = payload val initialSuccess = btGatt?.writeCharacteristic(characteristic) if(initialSuccess!!) { @@ -117,6 +129,21 @@ class ConnectionManager(context: Context) { return null } + private val bluetoothStateReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) { + val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) + if(state == BluetoothAdapter.STATE_OFF) { + disconnect() + listeners.forEach { it.get()?.onBluetoothStatusChange?.invoke(false) } + } else if (state == BluetoothAdapter.STATE_ON) { + reconnect() + listeners.forEach { it.get()?.onBluetoothStatusChange?.invoke(true) } + } + } + } + } + private val callback = object : BluetoothGattCallback() { @SuppressLint("MissingPermission") override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { @@ -142,6 +169,7 @@ class ConnectionManager(context: Context) { Log.d(logTag,"onConnectionStateChange: status $status encountered for $deviceAddress!") reconnect() } + } @SuppressLint("MissingPermission") diff --git a/app/src/main/java/com.alphasync/cameralink/MyCameraLinkService.kt b/app/src/main/java/com.alphasync/cameralink/MyCameraLinkService.kt index 4a14bbf..ae987a8 100644 --- a/app/src/main/java/com.alphasync/cameralink/MyCameraLinkService.kt +++ b/app/src/main/java/com.alphasync/cameralink/MyCameraLinkService.kt @@ -31,7 +31,7 @@ class MyCameraLinkService: Service() { private var cameraName: String = "" private lateinit var notificationManager: NotificationManagerCompat private val notificationChannel: String = "MyCameraLinkNotificationChannel" - private val notificationConnectDisconnectId: Int = 1 + private val notificationServiceStatusId: Int = 1 private val notificationGpsLostFoundId: Int = 2 fun isPairedToCamera(): Boolean { @@ -92,7 +92,7 @@ class MyCameraLinkService: Service() { .setContentText("Sending GPS to $cameraName") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() - notificationManager.notify(notificationConnectDisconnectId, notificationBuilder) + notificationManager.notify(notificationServiceStatusId, notificationBuilder) } onGpsSignalLost = { notificationManager.cancel(notificationGpsLostFoundId) @@ -102,12 +102,12 @@ class MyCameraLinkService: Service() { .setContentText("Sending GPS to $cameraName paused") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() - notificationManager.notify(notificationConnectDisconnectId, notificationBuilder) + notificationManager.notify(notificationServiceStatusId, notificationBuilder) } onLocationReady = { location -> val characteristic = connectionManager.characteristics.find { it.uuid.toString().contains("0000dd11")} Log.d(logTag, "Writing to ${characteristic!!.uuid}: ${location.toHexString()}") - connectionManager.writeCharacteristic(characteristic, location) + connectionManager.writeCharacteristic(characteristic.uuid, location) } } } @@ -115,38 +115,36 @@ class MyCameraLinkService: Service() { private val connectionEventListener by lazy { ConnectionEventListener().apply { onConnectionSetupComplete = { _ -> - notificationManager.cancel(notificationConnectDisconnectId) + notificationManager.cancel(notificationServiceStatusId) val notificationBuilder = NotificationCompat.Builder(applicationContext, notificationChannel) .setSmallIcon(R.drawable.ic_stat_name) .setContentTitle("$cameraName connected!") .setContentText("Sending GPS coordinates") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() - notificationManager.notify(notificationConnectDisconnectId, notificationBuilder) + notificationManager.notify(notificationServiceStatusId, notificationBuilder) sendEnableGpsCommands() } - onDisconnect = { sonyCommandGenerator.stopLocationReporting(myCameraLinkEventListener) - notificationManager.cancel(notificationConnectDisconnectId) + notificationManager.cancel(notificationServiceStatusId) val notificationBuilder = NotificationCompat.Builder(applicationContext, notificationChannel) .setSmallIcon(R.drawable.ic_camera_disconnected) .setContentTitle("$cameraName disconnected!") .setContentText("GPS coordinates paused") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() - notificationManager.notify(notificationConnectDisconnectId, notificationBuilder) + notificationManager.notify(notificationServiceStatusId, notificationBuilder) tryReconnect() } - onCharacteristicWrite = { _, sentCharacteristic: BluetoothGattCharacteristic -> if (sentCharacteristic.uuid.toString().contains("0000dd30")) { val characteristic = connectionManager.characteristics.find { it.uuid.toString().contains("0000dd31")} if (characteristic != null) { Log.d(logTag, "GPS Enable command: ${characteristic.uuid}") - connectionManager.writeCharacteristic(characteristic, "01".hexToBytes()) + connectionManager.writeCharacteristic(characteristic.uuid, "01".hexToBytes()) } else { Log.d(logTag, "GPS Enable command: Cannot find characteristic containing 0000dd31") } @@ -155,6 +153,28 @@ class MyCameraLinkService: Service() { startSendingCoordinatesToDevice() } } + onBluetoothStatusChange = { isEnabled: Boolean -> + if(!isEnabled) { + sonyCommandGenerator.stopLocationReporting(myCameraLinkEventListener) + notificationManager.cancel(notificationServiceStatusId) + val notificationBuilder = NotificationCompat.Builder(applicationContext, notificationChannel) + .setSmallIcon(R.drawable.ic_bluetooth_disabled) + .setContentTitle("Bluetooth disabled") + .setContentText("GPS coordinates paused") + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .build() + notificationManager.notify(notificationServiceStatusId, notificationBuilder) + } else { + notificationManager.cancel(notificationServiceStatusId) + val notificationBuilder = NotificationCompat.Builder(applicationContext, notificationChannel) + .setSmallIcon(R.drawable.ic_camera_disconnected) + .setContentTitle("$cameraName disconnected!") + .setContentText("GPS coordinates paused") + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .build() + notificationManager.notify(notificationServiceStatusId, notificationBuilder) + } + } } } @@ -166,14 +186,13 @@ class MyCameraLinkService: Service() { createNotificationChannel() val notification = NotificationCompat.Builder(this, notificationChannel) - .setContentTitle("Camera GPS link running") - .setContentText("Paired to $cameraName") - .setSmallIcon(R.drawable.ic_camera_back) + .setContentTitle("Connecting to $cameraName") + .setSmallIcon(R.drawable.ic_device_connecting) .build() ServiceCompat.startForeground( this, - 100, + notificationServiceStatusId, notification, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION or ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE @@ -186,7 +205,7 @@ class MyCameraLinkService: Service() { val characteristic = connectionManager.characteristics.find { it.uuid.toString().contains("0000dd30")} if (characteristic != null) { Log.d(logTag, "GPS Enable command: ${characteristic.uuid}") - connectionManager.writeCharacteristic(characteristic, "01".hexToBytes()) + connectionManager.writeCharacteristic(characteristic.uuid, "01".hexToBytes()) } else { Log.d(logTag, "GPS Enable command: Cannot find characteristic containing 0000dd31") } @@ -218,7 +237,7 @@ class MyCameraLinkService: Service() { } override fun onBind(intent: Intent?): IBinder? { - Log.d("MyService","Service being bound") + Log.d(logTag,"Service is being bound") return binder } } \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/ic_bluetooth_disabled.xml b/app/src/main/res/drawable-anydpi/ic_bluetooth_disabled.xml new file mode 100644 index 0000000..a9f04bc --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_bluetooth_disabled.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable-anydpi/ic_device_connecting.xml b/app/src/main/res/drawable-anydpi/ic_device_connecting.xml new file mode 100644 index 0000000..ba8e65b --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_device_connecting.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_bluetooth_disabled.png b/app/src/main/res/drawable-hdpi/ic_bluetooth_disabled.png new file mode 100644 index 0000000..a0999a6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_bluetooth_disabled.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_device_connecting.png b/app/src/main/res/drawable-hdpi/ic_device_connecting.png new file mode 100644 index 0000000..9b54ef1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_device_connecting.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_bluetooth_disabled.png b/app/src/main/res/drawable-mdpi/ic_bluetooth_disabled.png new file mode 100644 index 0000000..c3f5e8c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_bluetooth_disabled.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_device_connecting.png b/app/src/main/res/drawable-mdpi/ic_device_connecting.png new file mode 100644 index 0000000..48a33e3 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_device_connecting.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_bluetooth_disabled.png b/app/src/main/res/drawable-xhdpi/ic_bluetooth_disabled.png new file mode 100644 index 0000000..0619455 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_bluetooth_disabled.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_device_connecting.png b/app/src/main/res/drawable-xhdpi/ic_device_connecting.png new file mode 100644 index 0000000..4a9fc74 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_device_connecting.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bluetooth_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_bluetooth_disabled.png new file mode 100644 index 0000000..e4f855f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_bluetooth_disabled.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_device_connecting.png b/app/src/main/res/drawable-xxhdpi/ic_device_connecting.png new file mode 100644 index 0000000..640c320 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_device_connecting.png differ