kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
builds again
rodzic
8c3b9ce30d
commit
62ee9a43ed
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
# Medium priority
|
||||
|
||||
* remove example code boilerplate from the service
|
||||
* add analytics (make them optional)
|
||||
|
||||
# Low priority
|
||||
|
||||
* also add a receiver that fires after a new update was installed from the play stoe
|
|
@ -1,6 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.geeksville.meshutil">
|
||||
package="com.geeksville.meshutil"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- only useful if this phone can do BTLE -->
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- for job intent service -->
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth_le"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -9,6 +22,14 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<!-- we need bind job service for oreo -->
|
||||
<service
|
||||
android:name=".SoftwareUpdateService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
|
@ -19,6 +40,12 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".BootCompleteReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,14 @@
|
|||
package com.geeksville.meshutil
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
|
||||
class BootCompleteReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(mContext: Context, intent: Intent) {
|
||||
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
|
||||
// FIXME - start listening for bluetooth messages from our device
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,160 @@
|
|||
package com.geeksville.meshutil
|
||||
|
||||
import android.bluetooth.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
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.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
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() = !isEnabled
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
@ -17,7 +162,14 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
fab.setOnClickListener { view ->
|
||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show()
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,3 +189,4 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package com.geeksville.meshutil
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.JobIntentService
|
||||
|
||||
|
||||
/**
|
||||
* Example implementation of a JobIntentService.
|
||||
*/
|
||||
class SimpleJobIntentService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) { // We have received work to do. The system or framework is already
|
||||
// holding a wake lock for us at this point, so we can just go.
|
||||
Log.i("SimpleJobIntentService", "Executing work: $intent")
|
||||
var label = intent.getStringExtra("label")
|
||||
if (label == null) {
|
||||
label = intent.toString()
|
||||
}
|
||||
toast("Executing: $label")
|
||||
for (i in 0..4) {
|
||||
Log.i(
|
||||
"SimpleJobIntentService", "Running service " + (i + 1)
|
||||
+ "/5 @ " + SystemClock.elapsedRealtime()
|
||||
)
|
||||
try {
|
||||
Thread.sleep(1000)
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
Log.i(
|
||||
"SimpleJobIntentService",
|
||||
"Completed service @ " + SystemClock.elapsedRealtime()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
toast("All work complete")
|
||||
}
|
||||
|
||||
val mHandler = Handler()
|
||||
// Helper for showing tests
|
||||
fun toast(text: CharSequence?) {
|
||||
mHandler.post {
|
||||
Toast.makeText(this@SimpleJobIntentService, text, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Unique job ID for this service. Must be the same for all work.
|
||||
*/
|
||||
const val JOB_ID = 1000
|
||||
|
||||
/**
|
||||
* Convenience method for enqueuing work in to this service.
|
||||
*/
|
||||
fun enqueueWork(context: Context, work: Intent) {
|
||||
enqueueWork(
|
||||
context,
|
||||
SimpleJobIntentService::class.java, JOB_ID, work
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue