[O] Split code into service

This commit is contained in:
Azalea Gui
2023-01-23 22:50:31 -05:00
parent 7f1bed7620
commit aa4b6fd7d1
4 changed files with 102 additions and 27 deletions
+2
View File
@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
@@ -39,6 +40,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyService"/>
</application>
</manifest>
@@ -1,6 +1,7 @@
package org.hydev.wearsync
import android.app.Activity
import android.app.Service
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
@@ -17,6 +18,7 @@ import com.influxdb.client.domain.WritePrecision
import com.influxdb.client.kotlin.InfluxDBClientKotlin
import com.influxdb.client.kotlin.InfluxDBClientKotlinFactory
import kotlin.reflect.KProperty
import kotlin.reflect.full.isSubclassOf
fun View.snack(msg: String) = Snackbar.make(this, msg, Snackbar.LENGTH_LONG)
@@ -62,8 +64,15 @@ val Context.prefs get() = object : Prefs {
}
}
inline fun <reified T : Activity> Context.intent() = Intent(this, T::class.java)
inline fun <reified T : Activity> Context.act() = startActivity(intent<T>())
inline fun <reified T> Context.intent() = Intent(this, T::class.java)
inline fun <reified T> Context.act()
{
if (T::class.isSubclassOf(Activity::class))
startActivity(intent<T>())
else if (T::class.isSubclassOf(Service::class))
startService(intent<T>())
else TODO("Unimplemented: ${T::class}")
}
fun ComponentActivity.actCallback(fn: ActivityResultCallback<ActivityResult>) =
registerForActivityResult(ActivityResultContracts.StartActivityForResult(), fn)
@@ -1,6 +1,8 @@
package org.hydev.wearsync
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.bluetooth.BluetoothAdapter
import android.content.Intent
import android.graphics.Color
@@ -13,15 +15,18 @@ 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.Companion.ble
import org.hydev.wearsync.databinding.ActivityMainBinding
import java.util.*
class MainActivity : AppCompatActivity()
{
companion object {
var instance: MainActivity? = null
}
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
lateinit var binding: ActivityMainBinding
lateinit var influx: InfluxDBClientKotlin
@@ -41,15 +46,24 @@ class MainActivity : AppCompatActivity()
{
super.onCreate(savedInstanceState)
// Bind activity
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
// Create recycler
binding.content.recycler.let {
it.adapter = RecordAdapter(records)
it.layoutManager = LinearLayoutManager(this)
}
// Create notification channel
val chan = NotificationChannel(MyService.NOTIF_CHANNEL_ID, "Keep-alive Notification",
NotificationManager.IMPORTANCE_MIN)
getSysServ<NotificationManager>().createNotificationChannel(chan)
instance = this
if (!hasPermissions()) permissionCallback.launch(intent<ActivityPermissions>())
else afterPermissions()
}
@@ -83,7 +97,7 @@ class MainActivity : AppCompatActivity()
binding.content.tvDevice.text = "Configured Device: ${prefs.chosenDevice}"
binding.content.tvValue.text = "Service started!"
startCollect()
act<MyService>()
}
override fun onResume()
@@ -119,7 +133,6 @@ class MainActivity : AppCompatActivity()
}
}
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val records = ArrayList<Any>()
class RecordAdapter(val records: List<Any>) : RecyclerView.Adapter<RecordAdapter.ViewHolder>() {
@@ -154,24 +167,4 @@ class MainActivity : AppCompatActivity()
records.add(t)
runOnUiThread { binding.content.recycler.adapter?.notifyDataSetChanged() }
}
fun startCollect()
{
collect(ble.heartRateChannel)
collect(ble.batteryChannel)
}
private fun <T : Any> collect(channel: Channel<T>) {
scope.launch {
channel.consumeAsFlow().collect {
try {
addRecord(it)
influx add it
}
catch (e: Exception) {
addRecord(e)
}
}
}
}
}
@@ -0,0 +1,71 @@
package org.hydev.wearsync
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import androidx.core.app.NotificationCompat
import com.influxdb.client.kotlin.InfluxDBClientKotlin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
import org.hydev.wearsync.bles.BluetoothHandler.Companion.ble
class MyService : Service()
{
private lateinit var influx: InfluxDBClientKotlin
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override fun onBind(intent: Intent?) = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
{
influx = prefs.createInflux()
startCollect()
startForeground()
return super.onStartCommand(intent, flags, startId)
}
fun startCollect()
{
collect(ble.heartRateChannel)
collect(ble.batteryChannel)
}
private fun <T : Any> collect(channel: Channel<T>) {
scope.launch {
channel.consumeAsFlow().collect {
try {
MainActivity.instance?.addRecord(it)
influx add it
}
catch (e: Exception) {
MainActivity.instance?.addRecord(e)
}
}
}
}
private fun startForeground()
{
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
startForeground(NOTIF_ID, NotificationCompat.Builder(this, NOTIF_CHANNEL_ID)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_watch_24)
.setContentTitle("🐱 Running!")
.setContentIntent(pendingIntent)
.build()
)
}
companion object
{
private const val NOTIF_ID = 1
const val NOTIF_CHANNEL_ID = "Channel_Id"
}
}