[+] Record log
This commit is contained in:
@@ -3,21 +3,21 @@ package org.hydev.wearsync
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.*
|
||||||
import android.view.MenuItem
|
import android.widget.TextView
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.influxdb.client.kotlin.InfluxDBClientKotlin
|
import com.influxdb.client.kotlin.InfluxDBClientKotlin
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
import org.hydev.wearsync.ActivityPermissions.Companion.hasPermissions
|
import org.hydev.wearsync.ActivityPermissions.Companion.hasPermissions
|
||||||
import org.hydev.wearsync.bles.BluetoothHandler
|
|
||||||
import org.hydev.wearsync.bles.BluetoothHandler.Companion.ble
|
import org.hydev.wearsync.bles.BluetoothHandler.Companion.ble
|
||||||
import org.hydev.wearsync.bles.ObservationUnit
|
|
||||||
import org.hydev.wearsync.databinding.ActivityMainBinding
|
import org.hydev.wearsync.databinding.ActivityMainBinding
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity()
|
class MainActivity : AppCompatActivity()
|
||||||
@@ -43,21 +43,30 @@ class MainActivity : AppCompatActivity()
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
|
binding.content.recycler.let {
|
||||||
|
it.adapter = RecordAdapter(records)
|
||||||
|
it.layoutManager = LinearLayoutManager(this)
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPermissions()) permissionCallback.launch(intent<ActivityPermissions>())
|
if (!hasPermissions()) permissionCallback.launch(intent<ActivityPermissions>())
|
||||||
else afterPermissions()
|
else afterPermissions()
|
||||||
}
|
}
|
||||||
|
|
||||||
val permissionCallback = actCallback { afterPermissions() }
|
val permissionCallback = actCallback { afterPermissions() }
|
||||||
fun afterPermissions() {
|
fun afterPermissions() {
|
||||||
|
addRecord("Permissions granted")
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
// Open settings if influx database is inaccessible
|
// Open settings if influx database is inaccessible
|
||||||
if (runCatching { prefs.influxPing() }.isFailure) settingsCallback.launch(intent<ActivitySettings>())
|
if (runCatching { prefs.influxPing() }.isFailure) settingsCallback.launch(intent<ActivitySettings>())
|
||||||
else afterSettings()
|
else runOnUiThread { afterSettings() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val settingsCallback = actCallback { afterSettings() }
|
val settingsCallback = actCallback { afterSettings() }
|
||||||
fun afterSettings() {
|
fun afterSettings() {
|
||||||
|
addRecord("InfluxDB settings checked")
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
influx = prefs.createInflux()
|
influx = prefs.createInflux()
|
||||||
|
|
||||||
@@ -66,9 +75,12 @@ class MainActivity : AppCompatActivity()
|
|||||||
}
|
}
|
||||||
|
|
||||||
val connectCallback = actCallback {
|
val connectCallback = actCallback {
|
||||||
|
addRecord("Device connected")
|
||||||
|
|
||||||
// Start collection
|
// Start collection
|
||||||
binding.content.tvDevice.text = "Configured Device: ${prefs.chosenDevice}"
|
binding.content.tvDevice.text = "Configured Device: ${prefs.chosenDevice}"
|
||||||
binding.content.tvValue.text = "Service started!"
|
binding.content.tvValue.text = "Service started!"
|
||||||
|
|
||||||
startCollect()
|
startCollect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,86 +114,55 @@ class MainActivity : AppCompatActivity()
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
private val dateFormat: DateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.ENGLISH)
|
private val records = ArrayList<Any>()
|
||||||
|
|
||||||
|
class RecordAdapter(val records: List<Any>) : RecyclerView.Adapter<RecordAdapter.ViewHolder>() {
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val type = itemView.findViewById<TextView>(R.id.rrType)
|
||||||
|
val value = itemView.findViewById<TextView>(R.id.rrValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
|
||||||
|
{
|
||||||
|
return ViewHolder(LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.recycler_record, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = records.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int)
|
||||||
|
{
|
||||||
|
val record = records[position]
|
||||||
|
val cls = record.javaClass.name
|
||||||
|
holder.type.text = cls
|
||||||
|
holder.value.text = record.toString().replace(cls, "")
|
||||||
|
|
||||||
|
if (record is Exception) holder.type.setTextColor(Color.parseColor("#FF6C52"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun addRecord(t: Any) {
|
||||||
|
while (records.size > 20) records.remove(0)
|
||||||
|
records.add(t)
|
||||||
|
runOnUiThread { binding.content.recycler.adapter?.notifyDataSetChanged() }
|
||||||
|
}
|
||||||
|
|
||||||
fun startCollect()
|
fun startCollect()
|
||||||
{
|
{
|
||||||
collectHeartRate(ble)
|
collect(ble.heartRateChannel)
|
||||||
collectPulseOxContinuous(ble)
|
collect(ble.batteryChannel)
|
||||||
collectPulseOxSpot(ble)
|
|
||||||
collectTemperature(ble)
|
|
||||||
collectWeight(ble)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectHeartRate(bluetoothHandler: BluetoothHandler) {
|
private fun <T : Any> collect(channel: Channel<T>) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
bluetoothHandler.heartRateChannel.consumeAsFlow().collect {
|
channel.consumeAsFlow().collect {
|
||||||
withContext(Dispatchers.Main) {
|
try {
|
||||||
binding.content.tvValue.text = String.format(Locale.ENGLISH, "%d bpm", it.pulse)
|
addRecord(it)
|
||||||
|
influx add it
|
||||||
}
|
}
|
||||||
influx add it
|
catch (e: Exception) {
|
||||||
}
|
addRecord(e)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectPulseOxContinuous(bluetoothHandler: BluetoothHandler) {
|
|
||||||
scope.launch {
|
|
||||||
bluetoothHandler.pulseOxContinuousChannel.consumeAsFlow().collect {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.content.tvValue.text = String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"SpO2 %d%%, Pulse %d bpm\n%s\n\nfrom %s",
|
|
||||||
it.spO2,
|
|
||||||
it.pulseRate,
|
|
||||||
dateFormat.format(Calendar.getInstance())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectPulseOxSpot(bluetoothHandler: BluetoothHandler) {
|
|
||||||
scope.launch {
|
|
||||||
bluetoothHandler.pulseOxSpotChannel.consumeAsFlow().collect {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.content.tvValue.text = String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"SpO2 %d%%, Pulse %d bpm\n",
|
|
||||||
it.spO2,
|
|
||||||
it.pulseRate
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectTemperature(bluetoothHandler: BluetoothHandler) {
|
|
||||||
scope.launch {
|
|
||||||
bluetoothHandler.temperatureChannel.consumeAsFlow().collect {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.content.tvValue.text = String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"%.1f %s (%s)\n%s\n",
|
|
||||||
it.temperatureValue,
|
|
||||||
if (it.unit == ObservationUnit.Celsius) "celsius" else "fahrenheit",
|
|
||||||
it.type,
|
|
||||||
dateFormat.format(it.timestamp ?: Calendar.getInstance())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectWeight(bluetoothHandler: BluetoothHandler) {
|
|
||||||
scope.launch {
|
|
||||||
bluetoothHandler.weightChannel.consumeAsFlow().collect {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.content.tvValue.text = String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"%.1f %s\n%s\n",
|
|
||||||
it.weight, it.unit.toString(),
|
|
||||||
dateFormat.format(it.timestamp ?: Calendar.getInstance())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user