Skip to content

Commit

Permalink
VIT-6016: Improve how Auto Sync interacts with signed-out state and s…
Browse files Browse the repository at this point in the history
…ignOut() (#99)
  • Loading branch information
andersio authored Mar 18, 2024
1 parent 9274649 commit 26f7e5c
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 24 deletions.
5 changes: 5 additions & 0 deletions VitalClient/src/main/java/io/tryvital/client/VitalClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
Expand Down Expand Up @@ -96,6 +97,9 @@ class VitalClient internal constructor(context: Context) {
/** Moments which can materially change VitalClient.Companion.status */
private val statusChanged = MutableSharedFlow<Unit>(extraBufferCapacity = Int.MAX_VALUE)

val childSDKShouldReset: SharedFlow<Unit> get() = _childSDKShouldReset
private val _childSDKShouldReset = MutableSharedFlow<Unit>(extraBufferCapacity = Int.MAX_VALUE)


@Deprecated("Renamed to `signOut()` and is now a suspend function.", ReplaceWith("signOut()"))
fun cleanUp() {
Expand All @@ -110,6 +114,7 @@ class VitalClient internal constructor(context: Context) {
encryptedSharedPreferences.edit().clear().apply()
jwtAuth.signOut()
statusChanged.emit(Unit)
_childSDKShouldReset.emit(Unit)
}

@Deprecated(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.tryvital.vitalhealthconnect

import android.annotation.SuppressLint
import android.app.AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_BOOT_COMPLETED
import android.os.Looper
import io.tryvital.client.utils.VitalLogger
import io.tryvital.vitalhealthconnect.model.HealthConnectAvailability

const val ACTION_SYNC_DATA = "io.tryvital.vitalhealthconnect.action.SYNC_DATA"
private val ACTION_QUICKBOOT_POWERON = "android.intent.action.QUICKBOOT_POWERON"
Expand All @@ -29,25 +31,32 @@ class SyncBroadcastReceiver: BroadcastReceiver() {
VitalLogger.getOrCreate().info { "BgSync: exactAlarmPermissionStateChanged because ${intent.action}" }

val manager = VitalHealthConnectManager.getOrCreate(context)
if (manager.isBackgroundSyncEnabled) {
manager.scheduleNextExactAlarm(force = true)

// Just in case we are on an earlier Android OS version, in which one can uninstall
// Health Connect.
if (VitalHealthConnectManager.isAvailable(manager.context) != HealthConnectAvailability.Installed) {
return VitalLogger.getOrCreate().info { "BgSync: HealthConnect gone" }
}

manager.scheduleNextExactAlarm(force = true)
}

private fun exactAlarmFired(context: Context, intent: Intent) {
VitalLogger.getOrCreate().info { "BgSync: exactAlarmFired" }

val manager = VitalHealthConnectManager.getOrCreate(context)
manager.scheduleNextExactAlarm(force = true)

if (!context.isConnectedToInternet) {
return VitalLogger.getOrCreate().info { "BgSync: skipped launch - no internet" }
// Just in case we are on an earlier Android OS version, in which one can uninstall
// Health Connect.
if (VitalHealthConnectManager.isAvailable(manager.context) != HealthConnectAvailability.Installed) {
return VitalLogger.getOrCreate().info { "BgSync: HealthConnect gone" }
}

if (manager.pauseSynchronization) {
return VitalLogger.getOrCreate().info { "BgSync: skipped launch - sync paused" }
if (!manager.isBackgroundSyncEnabled) {
return VitalLogger.getOrCreate().info { "BgSync: skipped launch - backgroundSync is disabled" }
}

manager.scheduleNextExactAlarm(force = true)
manager.launchAutoSyncWorker {
context.startForegroundService(intent)
VitalLogger.getOrCreate().info { "BgSync: triggered by exact alarm" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class VitalHealthConnectManager private constructor(
}

private fun resetAutoSync() {
disableBackgroundSync()
taskScope.cancel()
taskScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
}
Expand Down Expand Up @@ -311,12 +312,18 @@ class VitalHealthConnectManager private constructor(
VitalLogger.getOrCreate().info { "BgSync: skipped by pause" }
return
}

if (shouldSkipAutoSync) {
VitalLogger.getOrCreate().info {
"BgSync: skipped by throttle; last synced at ${Instant.ofEpochMilli(lastAutoSyncedAt)}"
}
return
}

if (!context.isConnectedToInternet) {
return VitalLogger.getOrCreate().info { "BgSync: skipped; no internet" }
}

if (!vitalClient.hasUserConnectedTo(ProviderSlug.HealthConnect)) {
VitalLogger.getOrCreate().info { "BgSync: skipped; no CS" }
return
Expand Down Expand Up @@ -524,14 +531,9 @@ class VitalHealthConnectManager private constructor(
*/
@OptIn(DelicateCoroutinesApi::class)
private fun bind(client: VitalHealthConnectManager, coreClient: VitalClient, context: Context) {
VitalClient.Companion.statuses(context)
.onEach { statuses ->
if (VitalClient.Status.SignedIn !in statuses) {
client.resetAutoSync()
}
}
.launchIn(GlobalScope)
coreClient.childSDKShouldReset
.onEach { client.resetAutoSync() }
.launchIn(GlobalScope + Dispatchers.Main)
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import android.os.Looper
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.app.AlarmManagerCompat
import io.tryvital.client.VitalClient
import io.tryvital.client.utils.VitalLogger
import java.time.Instant
import kotlin.time.Duration.Companion.hours
Expand Down Expand Up @@ -155,6 +156,11 @@ internal fun VitalHealthConnectManager.scheduleNextExactAlarm(force: Boolean): B
return false
}

if (!isBackgroundSyncEnabled) {
VitalLogger.getOrCreate().info { "BgSync: scheduling skipped; backgroundSync is disabled" }
return false
}

val alarmManager = context.getSystemService(AlarmManager::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) {
VitalLogger.getOrCreate().info { "BgSync: scheduling aborted; no permission to use exact alarm" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ internal fun processLifecycleObserver(
if (event != Lifecycle.Event.ON_START) {
return
}
if (VitalClient.Status.SignedIn !in VitalClient.status) {
return
}
if (VitalHealthConnectManager.isAvailable(manager.context) != HealthConnectAvailability.Installed) {
return
}

manager.scheduleNextExactAlarm(force = false)

source.lifecycleScope.launch(start = CoroutineStart.UNDISPATCHED) {
manager.checkAndUpdatePermissions()

if (
VitalClient.Status.SignedIn in VitalClient.status
&& VitalHealthConnectManager.isAvailable(manager.context) == HealthConnectAvailability.Installed
) {
manager.launchAutoSyncWorker {
VitalLogger.getOrCreate().info { "BgSync: triggered by process ON_START" }
}
manager.launchAutoSyncWorker {
VitalLogger.getOrCreate().info { "BgSync: triggered by process ON_START" }
}
}
}
}
}

0 comments on commit 26f7e5c

Please sign in to comment.