Skip to content

Commit

Permalink
add wear data cache to avoid unnecessary data loads
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Mar 15, 2024
1 parent 2a18505 commit 811cc8b
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class WearComplicationService : SuspendingComplicationDataSourceService() {
}

private suspend fun buildShortTextData(): ComplicationData {
val activities = wearDataRepo.loadActivities()
val activities = wearDataRepo.loadActivities(forceReload = false)
.getOrNull().orEmpty()
val currentActivities = wearDataRepo.loadCurrentActivities()
val currentActivities = wearDataRepo.loadCurrentActivities(forceReload = false)
.getOrNull().orEmpty()

// Take most current activity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,31 @@
*/
package com.example.util.simpletimetracker.data

import android.content.ComponentName
import android.content.Context
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
import com.example.util.simpletimetracker.complication.WearComplicationService
import com.example.util.simpletimetracker.wear_api.WearActivity
import com.example.util.simpletimetracker.wear_api.WearCurrentActivity
import com.example.util.simpletimetracker.wear_api.WearSettings
import com.example.util.simpletimetracker.wear_api.WearTag
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class WearDataRepo @Inject constructor(
@ApplicationContext private val context: Context,
private val wearRPCClient: WearRPCClient,
) {

Expand All @@ -28,31 +40,66 @@ class WearDataRepo @Inject constructor(
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)

private var activitiesCache: List<WearActivity>? = null
private var currentActivitiesCache: List<WearCurrentActivity>? = null
private val mutex: Mutex = Mutex()

init {
wearRPCClient.addListener { _dataUpdated.emit(Unit) }
wearRPCClient.addListener {
coroutineScope {
val deferred = mutableListOf<Deferred<Any>>()
deferred += async { loadActivities(forceReload = true) }
deferred += async { loadCurrentActivities(forceReload = true) }
deferred.awaitAll()
updateComplications()
_dataUpdated.emit(Unit)
}
}
}

suspend fun loadActivities(): Result<List<WearActivity>> {
return runCatching { wearRPCClient.queryActivities() }
suspend fun loadActivities(
forceReload: Boolean,
): Result<List<WearActivity>> = mutex.withLock {
return runCatching {
activitiesCache.takeUnless { forceReload }
?: wearRPCClient.queryActivities()
.also { activitiesCache = it }
}
}

suspend fun loadCurrentActivities(): Result<List<WearCurrentActivity>> {
return runCatching { wearRPCClient.queryCurrentActivities() }
suspend fun loadCurrentActivities(
forceReload: Boolean,
): Result<List<WearCurrentActivity>> = mutex.withLock {
return runCatching {
currentActivitiesCache.takeUnless { forceReload }
?: wearRPCClient.queryCurrentActivities()
.also { currentActivitiesCache = it }
}
}

suspend fun setCurrentActivities(starting: List<WearCurrentActivity>): Result<Unit> {
suspend fun setCurrentActivities(starting: List<WearCurrentActivity>): Result<Unit> = mutex.withLock {
return runCatching { wearRPCClient.setCurrentActivities(starting) }
}

suspend fun loadTagsForActivity(activityId: Long): Result<List<WearTag>> {
suspend fun loadTagsForActivity(activityId: Long): Result<List<WearTag>> = mutex.withLock {
return runCatching { wearRPCClient.queryTagsForActivity(activityId) }
}

suspend fun loadSettings(): Result<WearSettings> {
suspend fun loadSettings(): Result<WearSettings> = mutex.withLock {
return runCatching { wearRPCClient.querySettings() }
}

suspend fun openAppPhone(): Result<Unit> {
suspend fun openAppPhone(): Result<Unit> = mutex.withLock {
return runCatching { wearRPCClient.openPhoneApp() }
}

fun updateComplications() {
ComplicationDataSourceUpdateRequester.create(
context = context,
complicationDataSourceComponent = ComponentName(
context,
WearComplicationService::class.java,
),
).requestUpdateAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
*/
package com.example.util.simpletimetracker.data

import android.content.ComponentName
import android.content.Context
import android.util.Log
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
import com.example.util.simpletimetracker.complication.WearComplicationService
import com.example.util.simpletimetracker.wear_api.WearRequests
import com.google.android.gms.tasks.Tasks
import com.google.android.gms.wearable.CapabilityClient
Expand Down Expand Up @@ -78,7 +75,6 @@ class WearMessenger @Inject constructor(
when (path) {
WearRequests.DATA_UPDATED -> {
listener?.invoke()
updateComplications()
}
else -> {
Log.d(tag, "$path is an invalid RPC call")
Expand All @@ -88,13 +84,6 @@ class WearMessenger @Inject constructor(
return null
}

private fun updateComplications() {
ComplicationDataSourceUpdateRequester.create(
context = context,
complicationDataSourceComponent = ComponentName(context, WearComplicationService::class.java),
).requestUpdateAll()
}

private fun findNearestNode(capability: String): Node? {
// Find all nodes which support the time tracking message
Log.d(tag, "Searching for nodes with ${context.packageName} installed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class CurrentActivitiesMediator @Inject constructor(
.getOrNull() ?: return Result.failure(WearRPCException)

return if (settings.allowMultitasking) {
val currents = wearDataRepo.loadCurrentActivities()
val currents = wearDataRepo.loadCurrentActivities(forceReload = false)
.getOrNull() ?: return Result.failure(WearRPCException)
wearDataRepo.setCurrentActivities(currents.plus(newCurrent))
} else {
Expand All @@ -37,7 +37,7 @@ class CurrentActivitiesMediator @Inject constructor(
}

suspend fun stop(currentId: Long): Result<Unit> {
val currents = wearDataRepo.loadCurrentActivities()
val currents = wearDataRepo.loadCurrentActivities(forceReload = false)
.getOrNull() ?: return Result.failure(WearRPCException)
val remaining = currents.filter { it.id != currentId }
return wearDataRepo.setCurrentActivities(remaining)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class ActivitiesViewModel @Inject constructor(

fun init() {
if (isInitialized) return
loadData()
subscribeToDataUpdates()
isInitialized = true
}
Expand All @@ -63,17 +62,18 @@ class ActivitiesViewModel @Inject constructor(
if (result.isFailure) showError()
}

fun onRefresh() {
loadData()
fun onRefresh() = viewModelScope.launch {
loadData(forceReload = true)
wearDataRepo.updateComplications()
}

fun onOpenOnPhone() = viewModelScope.launch {
wearDataRepo.openAppPhone()
}

private fun loadData() = viewModelScope.launch {
val activities = wearDataRepo.loadActivities()
val currentActivities = wearDataRepo.loadCurrentActivities()
private suspend fun loadData(forceReload: Boolean) {
val activities = wearDataRepo.loadActivities(forceReload)
val currentActivities = wearDataRepo.loadCurrentActivities(forceReload)

when {
activities.isFailure || currentActivities.isFailure -> {
Expand All @@ -97,7 +97,7 @@ class ActivitiesViewModel @Inject constructor(

private fun subscribeToDataUpdates() {
viewModelScope.launch {
wearDataRepo.dataUpdated.collect { loadData() }
wearDataRepo.dataUpdated.collect { loadData(forceReload = false) }
}
}

Expand Down

0 comments on commit 811cc8b

Please sign in to comment.