From da94f55ab0c95e6af97277d9406b7a116ee2a4e1 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Sun, 13 Oct 2024 10:15:56 -0500 Subject: [PATCH] Refresh watch data on changes --- .../java/org/tasks/wear/WearDataService.kt | 12 +++++++++ .../java/org/tasks/wear/WearService.kt | 27 +++++++++++++++++++ .../org/tasks/wear/LastUpdateSerializer.kt | 24 +++++++++++++++++ wear-datalayer/src/main/proto/grpc.proto | 4 +++ .../presentation/screens/TaskListViewModel.kt | 18 ++++++++++--- 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 wear-datalayer/src/main/java/org/tasks/wear/LastUpdateSerializer.kt diff --git a/app/src/googleplay/java/org/tasks/wear/WearDataService.kt b/app/src/googleplay/java/org/tasks/wear/WearDataService.kt index 16238f9b0..6eec61634 100644 --- a/app/src/googleplay/java/org/tasks/wear/WearDataService.kt +++ b/app/src/googleplay/java/org/tasks/wear/WearDataService.kt @@ -1,12 +1,16 @@ package org.tasks.wear +import androidx.datastore.core.DataStore import androidx.lifecycle.lifecycleScope import com.google.android.horologist.annotations.ExperimentalHorologistApi +import com.google.android.horologist.data.ProtoDataStoreHelper.protoDataStore import com.google.android.horologist.data.WearDataLayerRegistry import com.google.android.horologist.datalayer.grpc.server.BaseGrpcDataService import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.service.TaskCompleter import dagger.hilt.android.AndroidEntryPoint +import org.tasks.GrpcProto.LastUpdate +import org.tasks.LocalBroadcastManager import org.tasks.WearServiceGrpcKt import org.tasks.preferences.Preferences import javax.inject.Inject @@ -18,6 +22,11 @@ class WearDataService : BaseGrpcDataService by lazy { + registry.protoDataStore(lifecycleScope) + } override val registry: WearDataLayerRegistry by lazy { WearDataLayerRegistry.fromContext( @@ -25,6 +34,7 @@ class WearDataService : BaseGrpcDataService, + localBroadcastManager: LocalBroadcastManager, ) : WearServiceGrpcKt.WearServiceCoroutineImplBase() { + private val scope = CoroutineScope(Dispatchers.IO) + + init { + localBroadcastManager.registerRefreshReceiver( + object : android.content.BroadcastReceiver() { + override fun onReceive(context: android.content.Context?, intent: Intent?) { + scope.launch { + lastUpdate.updateData { + it.copy { + now = System.currentTimeMillis() + } + } + } + } + } + ) + } + override suspend fun getTasks(request: GetTasksRequest): Tasks { return Tasks.newBuilder() .addAllTasks(getTasks()) diff --git a/wear-datalayer/src/main/java/org/tasks/wear/LastUpdateSerializer.kt b/wear-datalayer/src/main/java/org/tasks/wear/LastUpdateSerializer.kt new file mode 100644 index 000000000..9b58d594c --- /dev/null +++ b/wear-datalayer/src/main/java/org/tasks/wear/LastUpdateSerializer.kt @@ -0,0 +1,24 @@ +package org.tasks.wear + +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.Serializer +import com.google.protobuf.InvalidProtocolBufferException +import org.tasks.GrpcProto.LastUpdate +import java.io.InputStream +import java.io.OutputStream + +object LastUpdateSerializer : Serializer { + override val defaultValue: LastUpdate + get() = LastUpdate.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): LastUpdate = + try { + LastUpdate.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } + + override suspend fun writeTo(t: LastUpdate, output: OutputStream) { + t.writeTo(output) + } +} diff --git a/wear-datalayer/src/main/proto/grpc.proto b/wear-datalayer/src/main/proto/grpc.proto index 51d072d27..1a4be8ce6 100644 --- a/wear-datalayer/src/main/proto/grpc.proto +++ b/wear-datalayer/src/main/proto/grpc.proto @@ -17,6 +17,10 @@ message Tasks { repeated Task tasks = 1; } +message LastUpdate { + uint64 now = 1; +} + message GetTasksRequest {} message CompleteTaskRequest { uint64 id = 1; diff --git a/wear/src/main/java/org/tasks/presentation/screens/TaskListViewModel.kt b/wear/src/main/java/org/tasks/presentation/screens/TaskListViewModel.kt index d44c138b3..a35e805c7 100644 --- a/wear/src/main/java/org/tasks/presentation/screens/TaskListViewModel.kt +++ b/wear/src/main/java/org/tasks/presentation/screens/TaskListViewModel.kt @@ -4,17 +4,23 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.google.android.horologist.annotations.ExperimentalHorologistApi +import com.google.android.horologist.data.ProtoDataStoreHelper.protoFlow import com.google.android.horologist.data.TargetNodeId import com.google.android.horologist.data.WearDataLayerRegistry import com.google.android.horologist.datalayer.grpc.GrpcExtensions.grpcClient import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.tasks.GrpcProto +import org.tasks.GrpcProto.LastUpdate import org.tasks.GrpcProto.Tasks import org.tasks.WearServiceGrpcKt +import org.tasks.wear.LastUpdateSerializer import org.tasks.wear.TasksSerializer data class TaskListScreenState( @@ -33,6 +39,7 @@ class TaskListViewModel( coroutineScope = scope, ).apply { registerSerializer(TasksSerializer) + registerSerializer(LastUpdateSerializer) } private val wearService : WearServiceGrpcKt.WearServiceCoroutineStub = wearDataLayerRegistry.grpcClient( nodeId = TargetNodeId.PairedPhone, @@ -40,12 +47,15 @@ class TaskListViewModel( ) { WearServiceGrpcKt.WearServiceCoroutineStub(it) } + private val lastUpdate: Flow = wearDataLayerRegistry.protoFlow(TargetNodeId.PairedPhone) init { - viewModelScope.launch { - val tasks = wearService.getTasks(GrpcProto.GetTasksRequest.getDefaultInstance()) - uiState.update { it.copy(tasks = tasks) } - } + lastUpdate + .onEach { + val tasks = wearService.getTasks(GrpcProto.GetTasksRequest.getDefaultInstance()) + uiState.update { it.copy(tasks = tasks) } + } + .launchIn(viewModelScope) } fun completeTask(it: Long) = viewModelScope.launch {