1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-23 11:22:10 +00:00

fix: Steps Servicevnotvworking as intended

This commit is contained in:
Florian Bouillon 2022-07-19 00:29:15 +02:00
parent c4c6e45687
commit 375bad46a7
Signed by: Florian Bouillon
GPG Key ID: 0A288052C94BD2C8
13 changed files with 169 additions and 116 deletions

View File

@ -46,8 +46,7 @@
<service
android:name=".services.OpenHealthService"
android:permission="android.permission.ACTIVITY_RECOGNITION"
android:exported="false"/>
android:permission="android.permission.ACTIVITY_RECOGNITION" />
</application>
</manifest>

View File

@ -19,7 +19,7 @@ class StepsAdapter() : BaseAdapter<Step, LayoutItemListBinding>() {
item: Step,
position: Int
) {
holder.binding.value.text = "${item.value}ml"
holder.binding.value.text = "${item.value}steps"
holder.binding.datetime.text = item.formatTimestamp()
holder.binding.edit.setOnClickListener {
onItemClick?.invoke(item)

View File

@ -3,21 +3,22 @@ package com.dzeio.openhealth.data.step
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.sql.Date
import java.text.DateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.TimeZone
@Entity()
data class Step(
@PrimaryKey(autoGenerate = true) var id: Long = 0,
var value: Float = 0f,
@ColumnInfo(index = true)
var value: Int = 0,
/**
* Timestamp down to an hour
*
* ex: 2022-09-22 10:00:00
*/
@ColumnInfo(index = true)
var timestamp: Long = 0,
var source: String = "OpenHealth"
) {
@ -33,9 +34,30 @@ data class Step(
}
}
fun formatTimestamp(): String = DateFormat.getDateInstance().format(Date(timestamp))
fun formatTimestamp(): String {
val formatter = DateFormat.getDateTimeInstance(
DateFormat.SHORT,
DateFormat.SHORT,
Locale.getDefault()
)
return formatter.format(Date(this.timestamp))
}
fun isToday(): Boolean {
val it = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
it.timeInMillis = timestamp
it.set(Calendar.HOUR, 0)
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
cal.set(Calendar.HOUR, 0)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
return it.timeInMillis == cal.timeInMillis
}
fun isCurrent(): Boolean {
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)

View File

@ -1,9 +1,7 @@
package com.dzeio.openhealth.data.step
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.dzeio.openhealth.core.BaseDao
import kotlinx.coroutines.flow.Flow
@ -24,12 +22,4 @@ interface StepDao : BaseDao<Step> {
@Query("DELETE FROM Step where source = :source")
suspend fun deleteFromSource(source: String)
@Update
fun updateNF(vararg obj: Step)
@Insert
fun insertNF(vararg obj: Step)
@Query("Select * FROM Step ORDER BY timestamp DESC LIMIT 1")
fun lastNF(): Step?
}

View File

@ -1,6 +1,8 @@
package com.dzeio.openhealth.data.step
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.withTimeoutOrNull
import javax.inject.Inject
import javax.inject.Singleton
@ -20,7 +22,22 @@ class StepRepository @Inject constructor(
suspend fun deleteStep(value: Step) = stepDao.delete(value)
suspend fun deleteFromSource(value: String) = stepDao.deleteFromSource(value)
fun todayStep() = lastStep().filter {
return@filter it != null && it.isToday()
suspend fun todaySteps(): Int {
val steps = getSteps().firstOrNull()
if (steps == null) {
return 0
}
var total = 0
for (item in steps) {
total += item.value
}
return total
}
fun currentStep() = lastStep().filter {
return@filter it != null && it.isCurrent()
}
}

View File

@ -32,18 +32,30 @@ import kotlinx.coroutines.withTimeoutOrNull
class OpenHealthService : Service() {
companion object {
private const val TAG = "${Application.TAG}/OpenHealthService"
private const val TAG = "${Application.TAG}/Service"
}
val stepRepository: StepRepository
get() = StepRepository_Factory.newInstance(AppDatabase.getInstance(applicationContext).stepDao())
private val stepRepository: StepRepository
get() = StepRepository_Factory.newInstance(
AppDatabase.getInstance(applicationContext).stepDao()
)
private var mNM: NotificationManager? = null
private lateinit var notification: Notification
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private val NOTIFICATION: Int = 8942
/**
* Displayed steps in the notification panel
*/
private var stepsTaken = 0u
/**
* Need a buffer because sometime it does not complete the function
*/
private var stepsBuffer = 0
/**
* Coroutine shit
*/
private val job = SupervisorJob()
private val scope = CoroutineScope(Dispatchers.IO + job)
@ -54,8 +66,8 @@ class OpenHealthService : Service() {
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.i("LocalService", "Received start id $startId: $intent")
scope.launch {
val source = StepSource(this@OpenHealthService)
scope.launch {
val source = StepSource(this@OpenHealthService)
source.events.receiveAsFlow().collectLatest {
Log.d(TAG, "Received value: $it")
@ -64,21 +76,24 @@ class OpenHealthService : Service() {
return@collectLatest
}
Log.d(TAG, "New steps registered: $it")
val step = withTimeoutOrNull(100) {
return@withTimeoutOrNull stepRepository.todayStep().firstOrNull()
stepsTaken += it.toUInt()
stepsBuffer += it.toInt()
showNotification()
val step = withTimeoutOrNull(1000) {
return@withTimeoutOrNull stepRepository.currentStep().firstOrNull()
}
Log.d(TAG, "stepRepository: $step")
if (step != null) {
step.value += it
step.value += stepsBuffer
stepRepository.updateStep(step)
} else {
stepRepository.addStep(Step(value = it))
stepRepository.addStep(Step(value = stepsBuffer))
}
stepsBuffer = 0
Log.d(TAG, "Added step!")
}
}
// Display a notification about us starting. We put an icon in the status bar.
startForeground(NotificationIds.Service.ordinal, showNotification())
@ -89,10 +104,10 @@ class OpenHealthService : Service() {
override fun onDestroy() {
stopForeground(true)
// Tell the user we stopped.
Toast.makeText(this, "Service stopped", Toast.LENGTH_SHORT).show()
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? {
@ -115,12 +130,15 @@ class OpenHealthService : Service() {
.getPendingIntent(0, flag)
// Set the info for the views that show in the notification panel.
val notification: Notification = NotificationCompat.Builder(this, NotificationChannels.SERVICE.id)
this.notification = NotificationCompat.Builder(
this,
NotificationChannels.SERVICE.id
)
.setSmallIcon(R.drawable.ic_logo_small)
// .setTicker("Pouet") // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle("Open Health Service") // the label of the entry
.setContentText("Watching for your steps") // the contents of the entry
.setContentText("Watching for your steps ($stepsTaken)") // the contents of the entry
.setContentIntent(intent) // The intent to send when the entry is clicked
.build()

View File

@ -1,14 +1,21 @@
package com.dzeio.openhealth.ui.browse
import android.Manifest
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import com.dzeio.openhealth.R
import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.databinding.FragmentBrowseBinding
import com.dzeio.openhealth.utils.PermissionsManager
import com.google.android.material.card.MaterialCardView
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
@ -22,6 +29,19 @@ class BrowseFragment :
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
private lateinit var button: MaterialCardView
private val activityResult = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (!it) {
// TODO: Show a popup with choice to change it
Toast.makeText(requireContext(), R.string.permission_declined, Toast.LENGTH_LONG).show()
return@registerForActivityResult
}
button.callOnClick()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -34,7 +54,25 @@ class BrowseFragment :
}
binding.steps.setOnClickListener {
findNavController().navigate(BrowseFragmentDirections.actionNavBrowseToStepsHomeFragment())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val hasPermission = PermissionsManager.hasPermission(
requireContext(),
Manifest.permission.ACTIVITY_RECOGNITION
)
if (!hasPermission) {
activityResult.launch(Manifest.permission.ACTIVITY_RECOGNITION)
return@setOnClickListener
}
}
findNavController().navigate(
BrowseFragmentDirections.actionNavBrowseToStepsHomeFragment()
)
}
viewModel.steps.observe(viewLifecycleOwner) {
binding.stepsText.setText("$it of xxx steps")
}
}
}

View File

@ -1,8 +1,25 @@
package com.dzeio.openhealth.ui.browse
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.step.StepRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class BrowseViewModel @Inject internal constructor() : BaseViewModel()
class BrowseViewModel @Inject internal constructor(
stepRepository: StepRepository
) : BaseViewModel() {
private val _steps = MutableLiveData(0)
val steps: LiveData<Int> = _steps
init {
viewModelScope.launch {
_steps.postValue(stepRepository.todaySteps())
}
}
}

View File

@ -32,7 +32,9 @@ class ExtensionsFragment :
private lateinit var activeExtension: Extension
private val activityResult = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { map ->
private val activityResult = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { map ->
if (map.containsValue(false)) {
// TODO: Show a popup with choice to change it
Toast.makeText(requireContext(), R.string.permission_declined, Toast.LENGTH_LONG).show()
@ -77,7 +79,11 @@ class ExtensionsFragment :
private fun extensionPermissionsVerified(it: Extension) {
// Check for the extension permissions
if (it.permissions != null && !PermissionsManager.hasPermission(requireContext(), it.permissions!!)) {
if (it.permissions != null && !PermissionsManager.hasPermission(
requireContext(),
it.permissions!!
)
) {
// TODO: show popup explaining the permissions requested
// show permissions

View File

@ -57,7 +57,7 @@ class StepsHomeFragment :
)
)
chart.xAxis.valueFormatter = GraphUtils.DateValueFormatter(1000 * 60 * 60)
chart.xAxis.valueFormatter = GraphUtils.DateTimeValueFormatter()
viewModel.items.observe(viewLifecycleOwner) { list ->
adapter.set(list)
@ -65,8 +65,8 @@ class StepsHomeFragment :
val dataset = BarDataSet(
list.map {
return@map BarEntry(
(it.timestamp / 60 / 60 / 24).toFloat(),
it.value
(it.timestamp).toFloat(),
it.value.toFloat()
)
},
""

View File

@ -39,9 +39,7 @@ object GraphUtils {
mainColor: Int,
textColor: Int
) {
chart.apply {
// Setup
legend.isEnabled = true
description = Description().apply { isEnabled = false }
@ -60,6 +58,7 @@ object GraphUtils {
}
axisLeft.apply {
axisMinimum = 0f
isEnabled = false
axisLineColor = mainColor
this.textColor = textColor
@ -69,6 +68,7 @@ object GraphUtils {
setDrawBorders(false)
}
axisRight.apply {
axisMinimum = 0f
this.textColor = textColor
setLabelCount(4, true)
}
@ -96,4 +96,16 @@ object GraphUtils {
// return super.getAxisLabel(value, axis)
}
}
class DateTimeValueFormatter(
private val transformer: Int = 1
) : ValueFormatter() {
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
return SimpleDateFormat(
"yyyy-MM-dd hh",
Locale.getDefault()
).format(Date(value.toLong() * transformer))
// return super.getAxisLabel(value, axis)
}
}
}

View File

@ -1,67 +0,0 @@
package com.dzeio.openhealth.workers
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters
import com.dzeio.openhealth.Application
import com.dzeio.openhealth.core.BaseService
import com.dzeio.openhealth.data.AppDatabase
import com.dzeio.openhealth.data.step.Step
import com.dzeio.openhealth.data.step.StepRepository
import com.dzeio.openhealth.data.step.StepSource
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeoutOrNull
import java.util.concurrent.TimeUnit
@SuppressLint("SpecifyJobSchedulerIdRange")
class StepCountService(
private val context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
const val TAG = "${Application.TAG}/StepCountService"
fun setup(context: Context) {
BaseService.schedule(
TAG,
PeriodicWorkRequestBuilder<StepCountService>(PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
.addTag(TAG)
.build(),
context
)
}
}
override suspend fun doWork(): Result {
Log.d(TAG, "Service Started")
val appDatabase = AppDatabase.getInstance(this.context)
val repo = StepRepository(appDatabase.stepDao())
val source = StepSource(this.context)
val value = withTimeoutOrNull(10000) {
source.events.receive()
}
if (value == null || value == 0f) {
Log.d(TAG, "No new steps registered ($value)")
return Result.success()
}
Log.d(TAG, "New steps registered: $value")
coroutineContext
val step = repo.todayStep().first()
if (step != null) {
step.value += value
repo.updateStep(step)
} else {
repo.addStep(Step(value = value))
}
return Result.success()
}
}

View File

@ -73,6 +73,7 @@
<TextView
android:layout_width="match_parent"
android:id="@+id/steps_text"
android:layout_height="wrap_content"
android:text="xxxx of xxxx steps" />