diff --git a/app/src/main/java/org/hydev/wearsync/MainActivity.kt b/app/src/main/java/org/hydev/wearsync/MainActivity.kt index 7ff218a..e20fc13 100644 --- a/app/src/main/java/org/hydev/wearsync/MainActivity.kt +++ b/app/src/main/java/org/hydev/wearsync/MainActivity.kt @@ -3,21 +3,21 @@ package org.hydev.wearsync import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter import android.content.Intent +import android.graphics.Color import android.os.Bundle -import android.view.Menu -import android.view.MenuItem +import android.view.* +import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.influxdb.client.kotlin.InfluxDBClientKotlin import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow 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.ObservationUnit import org.hydev.wearsync.databinding.ActivityMainBinding -import java.text.DateFormat -import java.text.SimpleDateFormat import java.util.* class MainActivity : AppCompatActivity() @@ -43,21 +43,30 @@ class MainActivity : AppCompatActivity() setContentView(binding.root) setSupportActionBar(binding.toolbar) + binding.content.recycler.let { + it.adapter = RecordAdapter(records) + it.layoutManager = LinearLayoutManager(this) + } + if (!hasPermissions()) permissionCallback.launch(intent()) else afterPermissions() } val permissionCallback = actCallback { afterPermissions() } fun afterPermissions() { + addRecord("Permissions granted") + scope.launch { // Open settings if influx database is inaccessible if (runCatching { prefs.influxPing() }.isFailure) settingsCallback.launch(intent()) - else afterSettings() + else runOnUiThread { afterSettings() } } } val settingsCallback = actCallback { afterSettings() } fun afterSettings() { + addRecord("InfluxDB settings checked") + // Create client influx = prefs.createInflux() @@ -66,9 +75,12 @@ class MainActivity : AppCompatActivity() } val connectCallback = actCallback { + addRecord("Device connected") + // Start collection binding.content.tvDevice.text = "Configured Device: ${prefs.chosenDevice}" binding.content.tvValue.text = "Service started!" + startCollect() } @@ -102,86 +114,55 @@ class MainActivity : AppCompatActivity() } private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - private val dateFormat: DateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.ENGLISH) + private val records = ArrayList() + + class RecordAdapter(val records: List) : RecyclerView.Adapter() { + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val type = itemView.findViewById(R.id.rrType) + val value = itemView.findViewById(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() { - collectHeartRate(ble) - collectPulseOxContinuous(ble) - collectPulseOxSpot(ble) - collectTemperature(ble) - collectWeight(ble) + collect(ble.heartRateChannel) + collect(ble.batteryChannel) } - private fun collectHeartRate(bluetoothHandler: BluetoothHandler) { + private fun collect(channel: Channel) { scope.launch { - bluetoothHandler.heartRateChannel.consumeAsFlow().collect { - withContext(Dispatchers.Main) { - binding.content.tvValue.text = String.format(Locale.ENGLISH, "%d bpm", it.pulse) + channel.consumeAsFlow().collect { + try { + addRecord(it) + influx add it } - influx add it - } - } - } - - 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()) - ) + catch (e: Exception) { + addRecord(e) } } }