A fork of Simple Time Tracker with WearOS support. (Now integrated upstream)
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(wear): Respect multi-tasking setting

The WearOS app now follows the phone's multitasking setting.

When multi-tasking is disabled, starting a new activity will stop
all other running activities.

When multi-tasking is enabled, starting/stopping an activity
affects only that activity's state.

closes thehale/SimpleTimeTracker-WearOS#18

Joseph Hale (Feb 26, 2024, 10:27 PM -0700) 83cfd398 f7bc8f07

+62 -51
+36
wear/src/main/java/com/example/util/simpletimetracker/presentation/mediators/CurrentActivitiesMediator.kt
··· 1 + package com.example.util.simpletimetracker.presentation.mediators 2 + 3 + import com.example.util.simpletimetracker.wearrpc.CurrentActivity 4 + import com.example.util.simpletimetracker.wearrpc.Settings 5 + import com.example.util.simpletimetracker.wearrpc.Tag 6 + import com.example.util.simpletimetracker.wearrpc.WearRPCClient 7 + 8 + class CurrentActivitiesMediator( 9 + private val rpc: WearRPCClient, 10 + private val currents: Array<CurrentActivity>, 11 + ) { 12 + suspend fun start(activityId: Long) { 13 + start(activityId, arrayOf()) 14 + } 15 + suspend fun start(activityId: Long, tags: Array<Tag>) { 16 + val newCurrent = CurrentActivity( 17 + id = activityId, 18 + startedAt = System.currentTimeMillis(), 19 + tags = tags 20 + ) 21 + if (settings().multitasking) { 22 + this.rpc.setCurrentActivities(currents.plus(newCurrent)) 23 + } else { 24 + this.rpc.setCurrentActivities(arrayOf(newCurrent)) 25 + } 26 + } 27 + 28 + suspend fun stop(currentId: Long) { 29 + val remaining = currents.filter { it.id != currentId }.toTypedArray() 30 + this.rpc.setCurrentActivities(remaining) 31 + } 32 + private suspend fun settings(): Settings { 33 + return this.rpc.querySettings() 34 + } 35 + 36 + }
+2 -20
wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/currentActivities.kt
··· 11 11 import androidx.compose.runtime.mutableIntStateOf 12 12 import androidx.compose.runtime.mutableStateOf 13 13 import androidx.compose.runtime.remember 14 - import androidx.compose.runtime.rememberCoroutineScope 15 14 import androidx.compose.runtime.setValue 16 15 import com.example.util.simpletimetracker.wearrpc.CurrentActivity 17 - import com.example.util.simpletimetracker.wearrpc.WearRPCClient 18 16 import kotlinx.coroutines.Dispatchers 19 17 import kotlinx.coroutines.async 20 - import kotlinx.coroutines.launch 21 18 22 19 /** 23 20 * Handles asynchronous retrieval of the currently running activities in Simple Time Tracker on the ··· 38 35 * from the phone. 39 36 */ 40 37 @Composable 41 - fun rememberCurrentActivities(): Triple< 42 - Array<CurrentActivity>, 43 - (Array<CurrentActivity>) -> Unit, 44 - () -> Unit, 45 - > { 38 + fun rememberCurrentActivities(): Pair<Array<CurrentActivity>, () -> Unit> { 46 39 var rpc = rememberRPCClient() 47 40 var currentActivities: Array<CurrentActivity> by remember { mutableStateOf(arrayOf()) } 48 41 var currentActivitiesQueryCount by remember { mutableIntStateOf(0) } 49 42 val queryCurrentActivities = { currentActivitiesQueryCount++ } 50 - val coroutineScope = rememberCoroutineScope() 51 43 LaunchedEffect(currentActivitiesQueryCount) { 52 44 async(Dispatchers.Default) { 53 45 currentActivities = rpc.queryCurrentActivities() 54 46 } 55 47 } 56 - 57 - return Triple( 58 - currentActivities, 59 - { updatedCurrentActivities -> 60 - coroutineScope.launch(Dispatchers.Default) { 61 - rpc.setCurrentActivities(updatedCurrentActivities) 62 - queryCurrentActivities() 63 - } 64 - }, 65 - { queryCurrentActivities() }, 66 - ) 48 + return Pair(currentActivities, { queryCurrentActivities() }) 67 49 }
+9 -24
wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.kt
··· 8 8 import androidx.compose.runtime.Composable 9 9 import androidx.compose.runtime.rememberCoroutineScope 10 10 import com.example.util.simpletimetracker.presentation.components.ActivitiesList 11 + import com.example.util.simpletimetracker.presentation.mediators.CurrentActivitiesMediator 11 12 import com.example.util.simpletimetracker.presentation.remember.rememberActivities 12 13 import com.example.util.simpletimetracker.presentation.remember.rememberCurrentActivities 13 14 import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient 14 15 import com.example.util.simpletimetracker.wearrpc.Activity 15 - import com.example.util.simpletimetracker.wearrpc.CurrentActivity 16 16 import kotlinx.coroutines.Dispatchers 17 17 import kotlinx.coroutines.launch 18 18 ··· 21 21 val coroutineScope = rememberCoroutineScope() 22 22 val rpc = rememberRPCClient() 23 23 val (activities, refreshActivities) = rememberActivities() 24 - val (currentActivities, setCurrentActivities, refreshCurrentActivities) = rememberCurrentActivities() 24 + val (currentActivities, refreshCurrentActivities) = rememberCurrentActivities() 25 + val currentActivitiesMediator = CurrentActivitiesMediator(rpc, currentActivities) 25 26 val refresh = { 26 27 refreshActivities() 27 28 refreshCurrentActivities() ··· 36 37 if (activityTags.isNotEmpty()) { 37 38 coroutineScope.launch(Dispatchers.Main) { onSelectActivity(it.id) } 38 39 } else { 39 - rpc.setCurrentActivities( 40 - arrayOf( 41 - CurrentActivity( 42 - it.id, 43 - System.currentTimeMillis(), 44 - arrayOf(), 45 - ), 46 - ), 47 - ) 40 + currentActivitiesMediator.start(it.id) 48 41 refresh() 49 42 } 50 43 } 51 44 }, 52 45 onEnableActivity = { 53 46 coroutineScope.launch(Dispatchers.Default) { 54 - rpc.setCurrentActivities( 55 - arrayOf( 56 - CurrentActivity( 57 - it.id, 58 - System.currentTimeMillis(), 59 - arrayOf(), 60 - ), 61 - ), 62 - ) 47 + currentActivitiesMediator.start(it.id) 63 48 refresh() 64 49 } 65 50 }, 66 51 onDisableActivity = { deselectedActivity: Activity -> 67 - val remainingActivities = 68 - currentActivities.filter { it.id != deselectedActivity.id }.toTypedArray() 69 - setCurrentActivities(remainingActivities) 70 - refresh() 52 + coroutineScope.launch(Dispatchers.Default) { 53 + currentActivitiesMediator.stop(deselectedActivity.id) 54 + refresh() 55 + } 71 56 }, 72 57 onRefresh = refresh, 73 58 )
+15 -7
wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/TagsScreen.kt
··· 6 6 package com.example.util.simpletimetracker.presentation.screens 7 7 8 8 import androidx.compose.runtime.Composable 9 + import androidx.compose.runtime.rememberCoroutineScope 9 10 import com.example.util.simpletimetracker.presentation.components.TagList 11 + import com.example.util.simpletimetracker.presentation.mediators.CurrentActivitiesMediator 10 12 import com.example.util.simpletimetracker.presentation.remember.rememberCurrentActivities 13 + import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient 11 14 import com.example.util.simpletimetracker.presentation.remember.rememberTags 12 15 import com.example.util.simpletimetracker.wearrpc.CurrentActivity 16 + import kotlinx.coroutines.Dispatchers 17 + import kotlinx.coroutines.launch 13 18 import java.time.Instant 14 19 15 20 @Composable 16 21 fun TagsScreen(activityId: Long, onSelectTag: () -> Unit) { 22 + val coroutineScope = rememberCoroutineScope() 23 + val rpc = rememberRPCClient() 17 24 val (tags) = rememberTags(activityId) 18 - val (_, setCurrents) = rememberCurrentActivities() 25 + val (currentActivities) = rememberCurrentActivities() 26 + val currentActivitiesMediator = CurrentActivitiesMediator(rpc, currentActivities) 19 27 20 28 TagList( 21 29 tags, 22 30 onSelectTag = { 23 - setCurrents( 24 - arrayOf( 25 - CurrentActivity(activityId, Instant.now().toEpochMilli(), arrayOf(it)), 26 - ), 27 - ) 28 - onSelectTag() 31 + coroutineScope.launch(Dispatchers.Default) { 32 + currentActivitiesMediator.start(activityId, arrayOf(it)) 33 + coroutineScope.launch(Dispatchers.Main) { 34 + onSelectTag() 35 + } 36 + } 29 37 }, 30 38 ) 31 39 }