2020-01-20 15:53:22 -08:00
package com.geeksville.meshutil
2020-01-21 09:37:39 -08:00
import android.bluetooth.*
import android.content.Context
import android.content.Intent
2020-01-20 15:53:22 -08:00
import android.os.Bundle
2020-01-21 09:37:39 -08:00
import android.os.Handler
import android.util.Log
2020-01-20 15:53:22 -08:00
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import kotlinx.android.synthetic.main.activity_main.*
2020-01-21 09:37:39 -08:00
import java.util.*
2020-01-20 15:53:22 -08:00
class MainActivity : AppCompatActivity ( ) {
2020-01-21 09:37:39 -08:00
companion object {
const val REQUEST _ENABLE _BT = 10
private const val SCAN _PERIOD : Long = 10000
const val ACTION _GATT _CONNECTED = " com.example.bluetooth.le.ACTION_GATT_CONNECTED "
const val ACTION _GATT _DISCONNECTED = " com.example.bluetooth.le.ACTION_GATT_DISCONNECTED "
private const val STATE _DISCONNECTED = 0
private const val STATE _CONNECTING = 1
private const val STATE _CONNECTED = 2
private val TAG = MainActivity :: class . java . simpleName // FIXME - use my logging class instead
private val SW _UPDATE _UUID = UUID . fromString ( " cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30 " )
private val SW _UPDATE _TOTALSIZE _CHARACTER = UUID . fromString ( " e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e " ) // write|read total image size, 32 bit, write this first, then read read back to see if it was acceptable (0 mean not accepted)
private val SW _UPDATE _DATA _CHARACTER = UUID . fromString ( " e272ebac-d463-4b98-bc84-5cc1a39ee517 " ) // write data, variable sized, recommended 512 bytes, write one for each block of file
private val SW _UPDATE _CRC32 _CHARACTER = UUID . fromString ( " 4826129c-c22a-43a3-b066-ce8f0d5bacc6 " ) // write crc32, write last - writing this will complete the OTA operation, now you can read result
private val SW _UPDATE _RESULT _CHARACTER = UUID . fromString ( " 5e134862-7411-4424-ac4a-210937432c77 " ) // read|notify result code, readable but will notify when the OTA operation completes
}
private val BluetoothAdapter . isDisabled : Boolean
get ( ) = !is Enabled
private val bluetoothAdapter : BluetoothAdapter by lazy ( LazyThreadSafetyMode . NONE ) {
val bluetoothManager = getSystemService ( Context . BLUETOOTH _SERVICE ) as BluetoothManager
bluetoothManager . adapter !!
}
private var mScanning : Boolean = false
private val handler = Handler ( )
private val leScanCallback = BluetoothAdapter . LeScanCallback { device , _ , _ ->
runOnUiThread {
/ *
leDeviceListAdapter . addDevice ( device )
leDeviceListAdapter . notifyDataSetChanged ( )
* /
lateinit var bluetoothGatt : BluetoothGatt
//var connectionState = STATE_DISCONNECTED
lateinit var totalSizeDesc : BluetoothGattCharacteristic
// Send the next block of our file to the device
fun sendNextBlock ( ) {
}
// Various callback methods defined by the BLE API.
val gattCallback = object : BluetoothGattCallback ( ) {
override fun onConnectionStateChange (
gatt : BluetoothGatt ,
status : Int ,
newState : Int
) {
//val intentAction: String
when ( newState ) {
BluetoothProfile . STATE _CONNECTED -> {
//intentAction = ACTION_GATT_CONNECTED
//connectionState = STATE_CONNECTED
// broadcastUpdate(intentAction)
Log . i ( TAG , " Connected to GATT server. " )
Log . i ( TAG , " Attempting to start service discovery: " +
bluetoothGatt . discoverServices ( ) )
}
BluetoothProfile . STATE _DISCONNECTED -> {
//intentAction = ACTION_GATT_DISCONNECTED
//connectionState = STATE_DISCONNECTED
Log . i ( TAG , " Disconnected from GATT server. " )
// broadcastUpdate(intentAction)
}
}
}
// New services discovered
override fun onServicesDiscovered ( gatt : BluetoothGatt , status : Int ) {
when ( status ) {
BluetoothGatt . GATT _SUCCESS -> {
// broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED)
val updateService = gatt . services . find { it . uuid == SW _UPDATE _UUID }
if ( updateService != null ) {
// Start the update by writing the # of bytes in the image
val numBytes = 45
totalSizeDesc = updateService . getCharacteristic ( SW _UPDATE _TOTALSIZE _CHARACTER ) !!
assert ( totalSizeDesc . setValue ( numBytes , BluetoothGattCharacteristic . FORMAT _UINT32 , 0 ) )
assert ( bluetoothGatt . writeCharacteristic ( totalSizeDesc ) )
assert ( bluetoothGatt . readCharacteristic ( totalSizeDesc ) )
}
}
else -> Log . w ( TAG , " onServicesDiscovered received: $status " )
}
}
// Result of a characteristic read operation
override fun onCharacteristicRead (
gatt : BluetoothGatt ,
characteristic : BluetoothGattCharacteristic ,
status : Int
) {
assert ( status == BluetoothGatt . GATT _SUCCESS )
if ( characteristic == totalSizeDesc ) {
// Our read of this has completed, either fail or continue updating
val readvalue = characteristic . getIntValue ( BluetoothGattCharacteristic . FORMAT _UINT32 , 0 )
assert ( readvalue != 0 ) // FIXME - handle this case
sendNextBlock ( ) // FIXME, call this in a job queue of the service
}
// broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic)
}
}
bluetoothGatt = device . connectGatt ( this , false , gattCallback ) !!
}
}
private fun scanLeDevice ( enable : Boolean ) {
when ( enable ) {
true -> {
// Stops scanning after a pre-defined scan period.
handler . postDelayed ( {
mScanning = false
bluetoothAdapter . stopLeScan ( leScanCallback )
} , SCAN _PERIOD )
mScanning = true
bluetoothAdapter . startLeScan ( leScanCallback )
}
else -> {
mScanning = false
bluetoothAdapter . stopLeScan ( leScanCallback )
}
}
}
2020-01-20 15:53:22 -08:00
override fun onCreate ( savedInstanceState : Bundle ? ) {
super . onCreate ( savedInstanceState )
setContentView ( R . layout . activity _main )
setSupportActionBar ( toolbar )
fab . setOnClickListener { view ->
Snackbar . make ( view , " Replace with your own action " , Snackbar . LENGTH _LONG )
2020-01-21 09:37:39 -08:00
. setAction ( " Action " , null ) . show ( )
}
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
bluetoothAdapter . takeIf { it . isDisabled } ?. apply {
val enableBtIntent = Intent ( BluetoothAdapter . ACTION _REQUEST _ENABLE )
startActivityForResult ( enableBtIntent , REQUEST _ENABLE _BT )
2020-01-20 15:53:22 -08:00
}
}
override fun onCreateOptionsMenu ( menu : Menu ) : Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater . inflate ( R . menu . menu _main , menu )
return true
}
override fun onOptionsItemSelected ( item : MenuItem ) : Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
2020-01-20 16:13:40 -08:00
return when ( item . itemId ) {
2020-01-20 15:53:22 -08:00
R . id . action _settings -> true
else -> super . onOptionsItemSelected ( item )
}
}
}
2020-01-21 09:37:39 -08:00