Commit b0a6ec2b authored by Anon's avatar Anon

WIP

parent a68b812c
......@@ -18,6 +18,7 @@
</activity>
<activity android:name=".ScanActivity" />
<activity android:name=".SendActivity" />
</application>
<uses-permission android:name="android.permission.BLUETOOTH"/>
......
......@@ -2,15 +2,22 @@ package com.github.antweb.donkey
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.os.Environment
import android.util.Log
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.util.*
import java.util.zip.CRC32
private const val TAG = "FileTransfer"
class FileTransfer(
private val gatt: BluetoothGatt,
private val dataCharacteristic: BluetoothGattCharacteristic,
private val tx: BluetoothGattCharacteristic,
private val rx: BluetoothGattCharacteristic,
private val mtu: Int
) {
private val filePath = "/Download/SEND.txt"
......@@ -18,6 +25,9 @@ class FileTransfer(
private val iterator = openFile().iterator()
private var offset: Int = 0
private var lastCrc: Long = 0
private fun openFile(): ByteArray {
// TODO: Don't listen to Android Docs, use stuff that is not deprecated
val file = File(
......@@ -40,16 +50,15 @@ class FileTransfer(
}
fun sendNext() {
Log.d(TAG, "Sending next chunk: $offset")
if (!iterator.hasNext()) {
return
}
gatt?.beginReliableWrite()
val chunk = mutableListOf<Byte>()
for (i in 0 until (mtu - 5)) {
// for (i in 0 until (mtu - 5)) {
for (i in 0 until (10)) {
chunk.add(iterator.next())
}
......@@ -61,12 +70,18 @@ class FileTransfer(
val sendBuffer = ByteBuffer.allocate(chunk.size + 4)
sendBuffer.putInt(header)
sendBuffer.put(chunk.toByteArray())
val bytes = sendBuffer.array()
val crc = CRC32()
crc.update(bytes)
lastCrc = crc.value
dataCharacteristic?.value = sendBuffer.array()
gatt?.writeCharacteristic(dataCharacteristic)
// gatt?.beginReliableWrite()
tx?.value = bytes
val status = gatt?.writeCharacteristic(tx)
// This will trigger the onCharacteristicWrite callback once the other side ACKs
// In there, we can call the actual gatt?.executeReliableWrite()
if (!status) {
Log.d(TAG, "Write status: $status")
}
}
}
\ No newline at end of file
......@@ -15,22 +15,6 @@ private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
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
......@@ -55,102 +39,4 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
}
}
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)
}
}
......@@ -14,6 +14,11 @@ private const val TAG = "ScanActivity"
class ScanActivity : AppCompatActivity() {
// HACK: Figure out how to transfer this later
companion object {
var selectedDevice: BluetoothDevice? = null
}
private lateinit var listView: ListView
private lateinit var listAdapter: DeviceListAdapter
......@@ -32,6 +37,16 @@ class ScanActivity : AppCompatActivity() {
listAdapter = DeviceListAdapter(applicationContext)
listView.adapter = listAdapter
listView.setOnItemClickListener { adapterView, view, i, l ->
val item = adapterView.adapter.getItem(i) as? BluetoothDevice
if (item != null) {
selectedDevice = item
val intent = Intent(this, SendActivity::class.java)
startActivity(intent)
}
}
checkPermissions()
scan()
}
......@@ -47,13 +62,6 @@ class ScanActivity : AppCompatActivity() {
val foundDevices = mutableSetOf<BluetoothDevice>()
val leScanCallback = BluetoothAdapter.LeScanCallback { device, _, _ ->
// if (device.name == targetDeviceName) {
// if (!connected) {
// connected = true
// bluetoothGatt = device.connectGatt(this, true, gattCallback, BluetoothDevice.TRANSPORT_LE)
// }
// }
if (!foundDevices.contains(device)) {
foundDevices.add(device)
listAdapter.add(device)
......
package com.github.antweb.donkey
import android.bluetooth.*
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import java.lang.NullPointerException
import java.util.*
private const val TAG = "SendActivity"
class SendActivity : AppCompatActivity() {
private val mtu = 128
private val serviceUuid = "00422342-2342-2342-2342-234223422342"
private val centralTxCharacteristicUuid = UUID.fromString("01422342-2342-2342-2342-234223422342")
// private var centralTxCharacteristic: BluetoothGattCharacteristic? = null
private val centralRxCharacteristicUuid = UUID.fromString("02422342-2342-2342-2342-234223422342")
// private var centralRxCharacteristic: BluetoothGattCharacteristic? = null
private val clientConfigUuid = "00002902-0000-1000-8000-00805f9b34fb"
private var bluetoothGatt: BluetoothGatt? = null
private var fileService: BluetoothGattService? = null
private var mGatt: BluetoothGatt? = null
private var fileTransferService: FileTransfer? = null
private lateinit var tvConnection: TextView
private lateinit var tvValue: TextView
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_send)
tvValue = findViewById(R.id.text_value)
tvConnection = findViewById(R.id.text_connection_status)
tvConnection.text = "STATE_DISCONNECTED"
val device = ScanActivity.selectedDevice
if (device != null) {
connect(device)
} else {
Log.e(TAG, "Device is NULL!")
}
}
fun connect(device: BluetoothDevice) {
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() == centralTxCharacteristicUuid) {
// centralTxCharacteristic = characteristic
// } else if (characteristic.uuid.toString() == centralRxCharacteristicUuid) {
// centralRxCharacteristic = characteristic
// }
// }
}
if (fileService == null) {
// if (fileService == null || centralTxCharacteristic == 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 onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
super.onCharacteristicChanged(gatt, characteristic)
}
override fun onCharacteristicRead(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
super.onCharacteristicRead(gatt, characteristic, status)
}
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
Log.d(TAG, "MTU changed to: $mtu")
runOnUiThread {
tvValue.text = "MTU: $mtu"
}
val tx = fileService?.getCharacteristic(centralTxCharacteristicUuid)
val rx = fileService?.getCharacteristic(centralRxCharacteristicUuid)
if (gatt != null && tx != null && rx != null) {
val descriptor = rx.getDescriptor(UUID.fromString(clientConfigUuid))
if (descriptor != null) {
gatt.setCharacteristicNotification(rx, true)
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
val success = gatt.writeDescriptor(descriptor)
if (!success) {
Log.e(TAG, "Descriptor write failed")
}
} else {
Log.e(TAG, "Failed to write descriptor")
}
fileTransferService = FileTransfer(gatt, tx, rx, 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()
Thread.sleep(3000)
fileTransferService?.sendNext()
return
}
}
bluetoothGatt = device.connectGatt(this, true, gattCallback, BluetoothDevice.TRANSPORT_LE)
}
}
\ No newline at end of file
......@@ -4,4 +4,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_connection_status"
/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_value"
/>
</LinearLayout>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment