Commit b0a6ec2b authored by Anon's avatar Anon
Browse files

WIP

parent a68b812c
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
</activity> </activity>
<activity android:name=".ScanActivity" /> <activity android:name=".ScanActivity" />
<activity android:name=".SendActivity" />
</application> </application>
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH"/>
......
...@@ -2,15 +2,22 @@ package com.github.antweb.donkey ...@@ -2,15 +2,22 @@ package com.github.antweb.donkey
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.os.Environment import android.os.Environment
import android.util.Log
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.*
import java.util.zip.CRC32
private const val TAG = "FileTransfer"
class FileTransfer( class FileTransfer(
private val gatt: BluetoothGatt, private val gatt: BluetoothGatt,
private val dataCharacteristic: BluetoothGattCharacteristic, private val tx: BluetoothGattCharacteristic,
private val rx: BluetoothGattCharacteristic,
private val mtu: Int private val mtu: Int
) { ) {
private val filePath = "/Download/SEND.txt" private val filePath = "/Download/SEND.txt"
...@@ -18,6 +25,9 @@ class FileTransfer( ...@@ -18,6 +25,9 @@ class FileTransfer(
private val iterator = openFile().iterator() private val iterator = openFile().iterator()
private var offset: Int = 0 private var offset: Int = 0
private var lastCrc: Long = 0
private fun openFile(): ByteArray { private fun openFile(): ByteArray {
// TODO: Don't listen to Android Docs, use stuff that is not deprecated // TODO: Don't listen to Android Docs, use stuff that is not deprecated
val file = File( val file = File(
...@@ -40,16 +50,15 @@ class FileTransfer( ...@@ -40,16 +50,15 @@ class FileTransfer(
} }
fun sendNext() { fun sendNext() {
Log.d(TAG, "Sending next chunk: $offset")
if (!iterator.hasNext()) { if (!iterator.hasNext()) {
return return
} }
gatt?.beginReliableWrite()
val chunk = mutableListOf<Byte>() 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()) chunk.add(iterator.next())
} }
...@@ -61,12 +70,18 @@ class FileTransfer( ...@@ -61,12 +70,18 @@ class FileTransfer(
val sendBuffer = ByteBuffer.allocate(chunk.size + 4) val sendBuffer = ByteBuffer.allocate(chunk.size + 4)
sendBuffer.putInt(header) sendBuffer.putInt(header)
sendBuffer.put(chunk.toByteArray()) sendBuffer.put(chunk.toByteArray())
val bytes = sendBuffer.array()
val crc = CRC32()
crc.update(bytes)
lastCrc = crc.value
dataCharacteristic?.value = sendBuffer.array() // gatt?.beginReliableWrite()
gatt?.writeCharacteristic(dataCharacteristic) tx?.value = bytes
val status = gatt?.writeCharacteristic(tx)
// This will trigger the onCharacteristicWrite callback once the other side ACKs if (!status) {
// In there, we can call the actual gatt?.executeReliableWrite() Log.d(TAG, "Write status: $status")
}
} }
} }
\ No newline at end of file
...@@ -15,22 +15,6 @@ private const val TAG = "MainActivity" ...@@ -15,22 +15,6 @@ private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() { 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 mScanning: Boolean = false
private var connected = false private var connected = false
...@@ -55,102 +39,4 @@ class MainActivity : AppCompatActivity() { ...@@ -55,102 +39,4 @@ class MainActivity : AppCompatActivity() {
startActivity(intent) 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" ...@@ -14,6 +14,11 @@ private const val TAG = "ScanActivity"
class ScanActivity : AppCompatActivity() { 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 listView: ListView
private lateinit var listAdapter: DeviceListAdapter private lateinit var listAdapter: DeviceListAdapter
...@@ -32,6 +37,16 @@ class ScanActivity : AppCompatActivity() { ...@@ -32,6 +37,16 @@ class ScanActivity : AppCompatActivity() {
listAdapter = DeviceListAdapter(applicationContext) listAdapter = DeviceListAdapter(applicationContext)
listView.adapter = listAdapter 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() checkPermissions()
scan() scan()
} }
...@@ -47,13 +62,6 @@ class ScanActivity : AppCompatActivity() { ...@@ -47,13 +62,6 @@ class ScanActivity : AppCompatActivity() {
val foundDevices = mutableSetOf<BluetoothDevice>() val foundDevices = mutableSetOf<BluetoothDevice>()
val leScanCallback = BluetoothAdapter.LeScanCallback { device, _, _ -> 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)) { if (!foundDevices.contains(device)) {
foundDevices.add(device) foundDevices.add(device)
listAdapter.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 @@ ...@@ -4,4 +4,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="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> </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