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.

refactor: Hoist all rpc calls to `ActivitiesScreen`

Components are more reusable when they have minimal state
and/or dependency on outside resources.

This commit raises the rpc calls to `ActivitiesScreen` which has
the responsibility for managing the data flow and interactions
between components.

This update also fixes a few bugs:
- Tapping an activity chip will start the activity if it doesn't have
any tags.
- The switches now update automatically after an activity is started or
stopped.

closes thehale/SimpleTimeTracker-WearOS#12
closes thehale/SimpleTimeTracker-WearOS#13

Joseph Hale (Feb 26, 2024, 10:27 PM -0700) 047c536f 5f429f69

+60 -55
+8 -39
wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt
··· 1 - 2 1 /* 3 2 * This Source Code Form is subject to the terms of the Mozilla Public 4 3 * License, v. 2.0. If a copy of the MPL was not distributed with this ··· 6 5 */ 7 6 package com.example.util.simpletimetracker.presentation.components 8 7 9 - import android.widget.Toast 10 8 import androidx.compose.runtime.Composable 11 9 import androidx.compose.runtime.rememberCoroutineScope 12 10 import androidx.compose.ui.platform.LocalContext ··· 16 14 import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient 17 15 import com.example.util.simpletimetracker.wearrpc.Activity 18 16 import com.example.util.simpletimetracker.wearrpc.CurrentActivity 19 - import kotlinx.coroutines.Dispatchers 20 - import kotlinx.coroutines.launch 21 17 22 18 @Composable 23 19 fun ActivitiesList( 24 20 activities: Array<Activity>, 25 21 currentActivities: Array<CurrentActivity>, 26 22 onSelectActivity: (activity: Activity) -> Unit, 27 - onDeselectActivity: (activity: Activity) -> Unit, 23 + onEnableActivity: (activity: Activity) -> Unit, 24 + onDisableActivity: (activity: Activity) -> Unit, 28 25 onRefresh: () -> Unit, 29 26 ) { 30 - val coroutineScope = rememberCoroutineScope() 31 - val rpcClient = rememberRPCClient() 32 - val context = LocalContext.current 33 - 34 27 ScaffoldedScrollingColumn { 35 28 for (activity in activities) { 36 29 val currentActivity = currentActivities.filter { it.id == activity.id }.getOrNull(0) 37 - item { 30 + item(key = activity.id) { 38 31 ActivityChip( 39 32 activity, 40 33 startedAt = currentActivity?.startedAt, 41 34 tags = currentActivity?.tags ?: arrayOf(), 42 - onSelectActivity = { 43 - coroutineScope.launch(Dispatchers.Default) { 44 - val activityTags = rpcClient.queryTagsForActivity(activity.id) 45 - coroutineScope.launch(Dispatchers.Main) { 46 - if (activityTags.isNotEmpty()) { 47 - onSelectActivity(activity) 48 - } else { 49 - Toast.makeText(context, "Activity has no tags", Toast.LENGTH_SHORT).show() 50 - } 51 - } 52 - } 53 - }, 54 - onSelectActivitySkipTagSelection = { 55 - coroutineScope.launch(Dispatchers.Default) { 56 - rpcClient.setCurrentActivities( 57 - currentActivities.plus( 58 - CurrentActivity( 59 - activity.id, 60 - System.currentTimeMillis(), 61 - arrayOf(), 62 - ), 63 - ), 64 - ) 65 - } 66 - }, 67 - onDeselectActivity = { 68 - onDeselectActivity(activity) 69 - }, 35 + onPress = { onSelectActivity(activity) }, 36 + onToggleOn = { onEnableActivity(activity) }, 37 + onToggleOff = { onDisableActivity(activity) }, 70 38 ) 71 39 } 72 40 } ··· 93 61 activities, 94 62 currentActivities = currents, 95 63 onSelectActivity = { /* `it` is the selected activity */ }, 96 - onDeselectActivity = { /* `it` is the deselected activity */ }, 64 + onEnableActivity = { /* `it` is the enabled activity */ }, 65 + onDisableActivity = { /* `it` is the disabled activity */ }, 97 66 onRefresh = { /* What to do when requesting a refresh */ }, 98 67 ) 99 68 }
+7 -10
wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt
··· 37 37 activity: Activity, 38 38 startedAt: Long? = null, 39 39 tags: Array<Tag> = arrayOf(), 40 - onSelectActivity: () -> Unit = {}, 41 - onSelectActivitySkipTagSelection: () -> Unit = {}, 42 - onDeselectActivity: () -> Unit = {}, 40 + onPress: () -> Unit = {}, 41 + onToggleOn: () -> Unit = {}, 42 + onToggleOff: () -> Unit = {}, 43 43 ) { 44 44 val briefIcon = if (activity.icon.startsWith("ic_")) { 45 45 "?" ··· 60 60 var modifier = Modifier 61 61 .fillMaxWidth(0.9f) 62 62 .padding(top = 10.dp) 63 - var switchChecked by remember { mutableStateOf(startedAt != null) } 63 + var switchChecked = startedAt != null 64 64 SplitToggleChip( 65 65 modifier = modifier, 66 66 label = { ··· 87 87 ), 88 88 onCheckedChange = { 89 89 if (it) { 90 - onSelectActivitySkipTagSelection() 90 + onToggleOn() 91 91 } else { 92 - onDeselectActivity() 92 + onToggleOff() 93 93 } 94 - switchChecked = it 95 94 }, 96 95 checked = switchChecked, 97 - onClick = { 98 - onSelectActivity() 99 - }, 96 + onClick = onPress, 100 97 toggleControl = { 101 98 Switch( 102 99 checked = switchChecked,
+45 -6
wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.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.ActivitiesList 10 11 import com.example.util.simpletimetracker.presentation.remember.rememberActivities 11 12 import com.example.util.simpletimetracker.presentation.remember.rememberCurrentActivities 13 + import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient 12 14 import com.example.util.simpletimetracker.wearrpc.Activity 15 + import com.example.util.simpletimetracker.wearrpc.CurrentActivity 16 + import kotlinx.coroutines.Dispatchers 17 + import kotlinx.coroutines.launch 13 18 14 19 @Composable 15 20 fun ActivitiesScreen(onSelectActivity: (activityId: Long) -> Unit) { 21 + val coroutineScope = rememberCoroutineScope() 22 + val rpc = rememberRPCClient() 16 23 val (activities, refreshActivities) = rememberActivities() 17 24 val (currentActivities, setCurrentActivities, refreshCurrentActivities) = rememberCurrentActivities() 25 + val refresh = { 26 + refreshActivities() 27 + refreshCurrentActivities() 28 + } 18 29 19 30 ActivitiesList( 20 31 activities, 21 32 currentActivities, 22 33 onSelectActivity = { 23 - onSelectActivity(it.id) 34 + coroutineScope.launch(Dispatchers.Default) { 35 + val activityTags = rpc.queryTagsForActivity(it.id) 36 + if (activityTags.isNotEmpty()) { 37 + coroutineScope.launch(Dispatchers.Main) { onSelectActivity(it.id) } 38 + } else { 39 + rpc.setCurrentActivities( 40 + arrayOf( 41 + CurrentActivity( 42 + it.id, 43 + System.currentTimeMillis(), 44 + arrayOf(), 45 + ), 46 + ), 47 + ) 48 + refresh() 49 + } 50 + } 24 51 }, 25 - onDeselectActivity = { deselectedActivity: Activity -> 52 + onEnableActivity = { 53 + coroutineScope.launch(Dispatchers.Default) { 54 + rpc.setCurrentActivities( 55 + arrayOf( 56 + CurrentActivity( 57 + it.id, 58 + System.currentTimeMillis(), 59 + arrayOf(), 60 + ), 61 + ), 62 + ) 63 + refresh() 64 + } 65 + }, 66 + onDisableActivity = { deselectedActivity: Activity -> 26 67 val remainingActivities = 27 68 currentActivities.filter { it.id != deselectedActivity.id }.toTypedArray() 28 69 setCurrentActivities(remainingActivities) 70 + refresh() 29 71 }, 30 - onRefresh = { 31 - refreshActivities() 32 - refreshCurrentActivities() 33 - }, 72 + onRefresh = refresh, 34 73 ) 35 74 }