From 375bad46a7cd50bf58dc7386433138e4628fa1a9 Mon Sep 17 00:00:00 2001 From: Avior Date: Tue, 19 Jul 2022 00:29:15 +0200 Subject: [PATCH] fix: Steps Servicevnotvworking as intended --- app/src/main/AndroidManifest.xml | 5 +- .../dzeio/openhealth/adapters/StepsAdapter.kt | 2 +- .../com/dzeio/openhealth/data/step/Step.kt | 30 +++++++-- .../com/dzeio/openhealth/data/step/StepDao.kt | 10 --- .../openhealth/data/step/StepRepository.kt | 23 ++++++- .../openhealth/services/OpenHealthService.kt | 54 ++++++++++----- .../openhealth/ui/browse/BrowseFragment.kt | 40 ++++++++++- .../openhealth/ui/browse/BrowseViewModel.kt | 19 +++++- .../ui/extensions/ExtensionsFragment.kt | 12 +++- .../openhealth/ui/steps/StepsHomeFragment.kt | 6 +- .../com/dzeio/openhealth/utils/GraphUtils.kt | 16 ++++- .../openhealth/workers/StepCountService.kt | 67 ------------------- app/src/main/res/layout/fragment_browse.xml | 1 + 13 files changed, 169 insertions(+), 116 deletions(-) delete mode 100644 app/src/main/java/com/dzeio/openhealth/workers/StepCountService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 233d020..4ec27b2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,8 +46,7 @@ + android:permission="android.permission.ACTIVITY_RECOGNITION" /> - \ No newline at end of file + diff --git a/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt b/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt index c6f7fb6..1560fd3 100644 --- a/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt +++ b/app/src/main/java/com/dzeio/openhealth/adapters/StepsAdapter.kt @@ -19,7 +19,7 @@ class StepsAdapter() : BaseAdapter() { 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) diff --git a/app/src/main/java/com/dzeio/openhealth/data/step/Step.kt b/app/src/main/java/com/dzeio/openhealth/data/step/Step.kt index 6a6b361..1c4f78c 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/step/Step.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/step/Step.kt @@ -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) diff --git a/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt b/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt index 5d97152..ecdab8d 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/step/StepDao.kt @@ -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 { @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? } diff --git a/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt b/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt index ad6774a..aca13bd 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/step/StepRepository.kt @@ -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 } -} \ No newline at end of file + + fun currentStep() = lastStep().filter { + return@filter it != null && it.isCurrent() + } + +} diff --git a/app/src/main/java/com/dzeio/openhealth/services/OpenHealthService.kt b/app/src/main/java/com/dzeio/openhealth/services/OpenHealthService.kt index 8ec23d3..4bc0579 100644 --- a/app/src/main/java/com/dzeio/openhealth/services/OpenHealthService.kt +++ b/app/src/main/java/com/dzeio/openhealth/services/OpenHealthService.kt @@ -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() @@ -129,4 +147,4 @@ class OpenHealthService : Service() { return notification } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt index 932f42b..05d35fd 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseFragment.kt @@ -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") + } + } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt index 6570d91..7fd10d0 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/browse/BrowseViewModel.kt @@ -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 = _steps + + init { + viewModelScope.launch { + _steps.postValue(stepRepository.todaySteps()) + } + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/ui/extensions/ExtensionsFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/extensions/ExtensionsFragment.kt index 72386ca..d2a0eb6 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/extensions/ExtensionsFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/extensions/ExtensionsFragment.kt @@ -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 @@ -109,4 +115,4 @@ class ExtensionsFragment : ExtensionsFragmentDirections.actionNavExtensionsToNavExtension(it.id) ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt index b4a0734..c07468d 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt @@ -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() ) }, "" diff --git a/app/src/main/java/com/dzeio/openhealth/utils/GraphUtils.kt b/app/src/main/java/com/dzeio/openhealth/utils/GraphUtils.kt index d7fd056..9af6325 100644 --- a/app/src/main/java/com/dzeio/openhealth/utils/GraphUtils.kt +++ b/app/src/main/java/com/dzeio/openhealth/utils/GraphUtils.kt @@ -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) + } + } } diff --git a/app/src/main/java/com/dzeio/openhealth/workers/StepCountService.kt b/app/src/main/java/com/dzeio/openhealth/workers/StepCountService.kt deleted file mode 100644 index b529d28..0000000 --- a/app/src/main/java/com/dzeio/openhealth/workers/StepCountService.kt +++ /dev/null @@ -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(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() - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_browse.xml b/app/src/main/res/layout/fragment_browse.xml index 0610bf2..bfec972 100644 --- a/app/src/main/res/layout/fragment_browse.xml +++ b/app/src/main/res/layout/fragment_browse.xml @@ -73,6 +73,7 @@