From 194a4f21e5adac7f2ea8c26f3b5bcb6585b27927 Mon Sep 17 00:00:00 2001 From: razeeman Date: Sun, 13 Oct 2024 09:55:52 +0300 Subject: [PATCH] add repeat button to wear --- .../core/interactor/RecordRepeatInteractor.kt | 20 ++++-- .../SettingsDisplayViewModelDelegate.kt | 3 + .../feature_wear/WearDataLocalMapper.kt | 19 +++++ .../feature_wear/WearDataRepo.kt | 9 +++ .../feature_wear/WearRPCServer.kt | 5 ++ .../feature_wear/src/main/res/values/wear.xml | 1 + .../viewModel/WidgetUniversalViewModel.kt | 3 +- .../data/WearDataLocalMapper.kt | 17 +++++ .../simpletimetracker/data/WearDataRepo.kt | 21 +++++- .../simpletimetracker/data/WearRPCClient.kt | 9 +++ .../domain/mediator/StartActivityMediator.kt | 5 ++ .../domain/model/WearRecordRepeatResult.kt | 17 +++++ .../domain/model/WearSettings.kt | 1 + .../navigation/WearNavigator.kt | 20 ++++++ .../screens/activities/ActivitiesScreen.kt | 7 +- .../activities/ActivitiesViewDataMapper.kt | 26 ++++++- .../screens/activities/ActivitiesViewModel.kt | 52 +++++++++++++- .../screens/dialog/MessageDialog.kt | 71 +++++++++++++++++++ .../screens/settings/SettingsViewModel.kt | 12 ++-- .../screens/tagsSelection/TagsViewModel.kt | 3 +- .../ui/components/ActivitiesList.kt | 42 +++-------- .../ui/components/ActivityChip.kt | 1 + .../ui/components/ActivityChipCompact.kt | 1 + .../ui/components/ActivityChipType.kt | 6 ++ .../ui/components/SettingsList.kt | 2 +- .../presentation/ui/components/TagList.kt | 2 +- .../ui/layout/ScaffoldedScrollingColumn.kt | 4 +- .../presentation/ui/layout/Scaffolding.kt | 4 +- .../presentation/ui/layout/ScrollingColumn.kt | 10 +-- wear/src/main/res/drawable/wear_error.xml | 9 +++ wear/src/main/res/drawable/wear_repeat.xml | 9 +++ wear/src/main/res/drawable/wear_settings.xml | 2 +- .../wear/StartActivityMediatorTest.kt | 11 --- .../wear_api/WearCommunicationAPI.kt | 7 ++ .../simpletimetracker/wear_api/WearDTO.kt | 15 ++++ .../wear_api/WearRequests.kt | 1 + 36 files changed, 370 insertions(+), 77 deletions(-) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearRecordRepeatResult.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/dialog/MessageDialog.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipType.kt create mode 100644 wear/src/main/res/drawable/wear_error.xml create mode 100644 wear/src/main/res/drawable/wear_repeat.xml diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordRepeatInteractor.kt b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordRepeatInteractor.kt index dbc68a420..6fa7148f2 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordRepeatInteractor.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordRepeatInteractor.kt @@ -21,7 +21,7 @@ class RecordRepeatInteractor @Inject constructor( private val resourceRepo: ResourceRepo, ) { - suspend fun repeat(): Boolean { + suspend fun repeat(): ActionResult { return execute { messageResId -> SnackBarParams( message = resourceRepo.getString(messageResId), @@ -39,9 +39,13 @@ class RecordRepeatInteractor @Inject constructor( } } + suspend fun repeatWithoutMessage(): ActionResult { + return execute(messageShower = {}) + } + private suspend fun execute( messageShower: (messageResId: Int) -> Unit, - ): Boolean { + ): ActionResult { val type = prefsInteractor.getRepeatButtonType() val prevRecord = recordInteractor.getPrev( @@ -54,11 +58,11 @@ class RecordRepeatInteractor @Inject constructor( } } ?: run { messageShower(R.string.running_records_repeat_no_prev_record) - return false + return ActionResult.NoPreviousFound } if (runningRecordInteractor.get(prevRecord.typeId) != null) { messageShower(R.string.running_records_repeat_already_tracking) - return false + return ActionResult.AlreadyTracking } addRunningRecordMediator.startTimer( @@ -66,6 +70,12 @@ class RecordRepeatInteractor @Inject constructor( tagIds = prevRecord.tagIds, comment = prevRecord.comment, ) - return true + return ActionResult.Started + } + + sealed interface ActionResult { + object Started : ActionResult + object NoPreviousFound : ActionResult + object AlreadyTracking : ActionResult } } \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt index 74f4c4c56..e09b4ec69 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt @@ -9,6 +9,7 @@ import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInte import com.example.util.simpletimetracker.domain.interactor.PomodoroStopInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordsContainerUpdateInteractor +import com.example.util.simpletimetracker.domain.interactor.WearInteractor import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor import com.example.util.simpletimetracker.domain.model.CardOrder import com.example.util.simpletimetracker.domain.model.CardTagOrder @@ -34,6 +35,7 @@ class SettingsDisplayViewModelDelegate @Inject constructor( private val settingsMapper: SettingsMapper, private val notificationTypeInteractor: NotificationTypeInteractor, private val notificationActivitySwitchInteractor: NotificationActivitySwitchInteractor, + private val wearInteractor: WearInteractor, private val widgetInteractor: WidgetInteractor, private val settingsDisplayViewDataInteractor: SettingsDisplayViewDataInteractor, private val pomodoroStopInteractor: PomodoroStopInteractor, @@ -295,6 +297,7 @@ class SettingsDisplayViewModelDelegate @Inject constructor( prefsInteractor.setEnableRepeatButton(newValue) notificationTypeInteractor.updateNotifications() notificationActivitySwitchInteractor.updateNotification() + wearInteractor.update() parent?.updateContent() } } diff --git a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataLocalMapper.kt b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataLocalMapper.kt index de40128e3..6b6276239 100644 --- a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataLocalMapper.kt +++ b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataLocalMapper.kt @@ -5,6 +5,7 @@ */ package com.example.util.simpletimetracker.feature_wear +import com.example.util.simpletimetracker.core.interactor.RecordRepeatInteractor import com.example.util.simpletimetracker.core.mapper.RecordTagViewDataMapper import com.example.util.simpletimetracker.domain.mapper.AppColorMapper import com.example.util.simpletimetracker.domain.model.AppColor @@ -13,6 +14,7 @@ import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.wear_api.WearActivityDTO import com.example.util.simpletimetracker.wear_api.WearCurrentActivityDTO +import com.example.util.simpletimetracker.wear_api.WearRecordRepeatResponse import com.example.util.simpletimetracker.wear_api.WearSettingsDTO import com.example.util.simpletimetracker.wear_api.WearTagDTO import javax.inject.Inject @@ -61,10 +63,27 @@ class WearDataLocalMapper @Inject constructor( fun map( allowMultitasking: Boolean, recordTagSelectionCloseAfterOne: Boolean, + enableRepeatButton: Boolean, ): WearSettingsDTO { return WearSettingsDTO( allowMultitasking = allowMultitasking, recordTagSelectionCloseAfterOne = recordTagSelectionCloseAfterOne, + enableRepeatButton = enableRepeatButton, + ) + } + + fun map( + result: RecordRepeatInteractor.ActionResult, + ): WearRecordRepeatResponse { + return WearRecordRepeatResponse( + result = when (result) { + is RecordRepeatInteractor.ActionResult.Started -> + WearRecordRepeatResponse.ActionResult.STARTED + is RecordRepeatInteractor.ActionResult.NoPreviousFound -> + WearRecordRepeatResponse.ActionResult.NO_PREVIOUS_FOUND + is RecordRepeatInteractor.ActionResult.AlreadyTracking -> + WearRecordRepeatResponse.ActionResult.ALREADY_TRACKING + }, ) } diff --git a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataRepo.kt b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataRepo.kt index ca88269b4..b135d17eb 100644 --- a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataRepo.kt +++ b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearDataRepo.kt @@ -5,6 +5,7 @@ */ package com.example.util.simpletimetracker.feature_wear +import com.example.util.simpletimetracker.core.interactor.RecordRepeatInteractor import com.example.util.simpletimetracker.domain.interactor.AddRunningRecordMediator import com.example.util.simpletimetracker.domain.interactor.GetSelectableTagsInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor @@ -20,6 +21,7 @@ import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.wear_api.WearActivityDTO import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI import com.example.util.simpletimetracker.wear_api.WearCurrentActivityDTO +import com.example.util.simpletimetracker.wear_api.WearRecordRepeatResponse import com.example.util.simpletimetracker.wear_api.WearSettingsDTO import com.example.util.simpletimetracker.wear_api.WearShouldShowTagSelectionRequest import com.example.util.simpletimetracker.wear_api.WearShouldShowTagSelectionResponse @@ -38,6 +40,7 @@ class WearDataRepo @Inject constructor( private val shouldShowTagSelectionInteractor: ShouldShowTagSelectionInteractor, private val removeRunningRecordMediator: Lazy, private val addRunningRecordMediator: Lazy, + private val recordRepeatInteractor: Lazy, private val router: Router, private val widgetInteractor: WidgetInteractor, private val settingsDataUpdateInteractor: SettingsDataUpdateInteractor, @@ -77,6 +80,11 @@ class WearDataRepo @Inject constructor( removeRunningRecordMediator.get().removeWithRecordAdd(current) } + override suspend fun repeatActivity(): WearRecordRepeatResponse { + return recordRepeatInteractor.get().repeatWithoutMessage() + .let(wearDataLocalMapper::map) + } + override suspend fun queryTagsForActivity(activityId: Long): List { val types = recordTypeInteractor.getAll().associateBy { it.id } return getSelectableTagsInteractor.execute(activityId) @@ -101,6 +109,7 @@ class WearDataRepo @Inject constructor( return wearDataLocalMapper.map( allowMultitasking = prefsInteractor.getAllowMultitasking(), recordTagSelectionCloseAfterOne = prefsInteractor.getRecordTagSelectionCloseAfterOne(), + enableRepeatButton = prefsInteractor.getEnableRepeatButton(), ) } diff --git a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt index a2c60be2f..25053449c 100644 --- a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt +++ b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt @@ -37,6 +37,7 @@ class WearRPCServer @Inject constructor( WearRequests.QUERY_CURRENT_ACTIVITIES -> onQueryCurrentActivities() WearRequests.START_ACTIVITY -> onStartActivity(request) WearRequests.STOP_ACTIVITY -> onStopActivity(request) + WearRequests.REPEAT_ACTIVITY -> onRepeatActivity() WearRequests.QUERY_TAGS_FOR_ACTIVITY -> onQueryTagsForActivity(request) WearRequests.QUERY_SHOULD_SHOW_TAG_SELECTION -> onQueryShouldShowTagSelection(request) WearRequests.QUERY_SETTINGS -> onQuerySettings() @@ -96,6 +97,10 @@ class WearRPCServer @Inject constructor( return ByteArray(0) } + private suspend fun onRepeatActivity(): ByteArray { + return mapToBytes(repo.repeatActivity()) + } + private suspend fun onQueryActivities(): ByteArray { return mapToBytes(repo.queryActivities()) } diff --git a/features/feature_wear/src/main/res/values/wear.xml b/features/feature_wear/src/main/res/values/wear.xml index 21b36e90a..763bde46d 100644 --- a/features/feature_wear/src/main/res/values/wear.xml +++ b/features/feature_wear/src/main/res/values/wear.xml @@ -9,6 +9,7 @@ /stt/QUERY_CURRENT_ACTIVITIES /stt/START_ACTIVITY /stt/STOP_ACTIVITY + /stt/REPEAT_ACTIVITY /stt/QUERY_TAGS_FOR_ACTIVITY /stt/QUERY_SHOULD_SHOW_TAG_SELECTION /stt/QUERY_SETTINGS diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt index cbbfa1307..3d2b52704 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt @@ -98,7 +98,8 @@ class WidgetUniversalViewModel @Inject constructor( when (item.type) { is RunningRecordTypeSpecialViewData.Type.Repeat -> { - started = recordRepeatInteractor.repeat() + val result = recordRepeatInteractor.repeat() + started = result is RecordRepeatInteractor.ActionResult.Started } else -> return@launch } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataLocalMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataLocalMapper.kt index 6a641c6dd..fdda28375 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataLocalMapper.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataLocalMapper.kt @@ -7,10 +7,12 @@ package com.example.util.simpletimetracker.data import com.example.util.simpletimetracker.domain.model.WearActivity import com.example.util.simpletimetracker.domain.model.WearCurrentActivity +import com.example.util.simpletimetracker.domain.model.WearRecordRepeatResult import com.example.util.simpletimetracker.domain.model.WearSettings import com.example.util.simpletimetracker.domain.model.WearTag import com.example.util.simpletimetracker.wear_api.WearActivityDTO import com.example.util.simpletimetracker.wear_api.WearCurrentActivityDTO +import com.example.util.simpletimetracker.wear_api.WearRecordRepeatResponse import com.example.util.simpletimetracker.wear_api.WearSettingsDTO import com.example.util.simpletimetracker.wear_api.WearTagDTO import javax.inject.Inject @@ -46,6 +48,7 @@ class WearDataLocalMapper @Inject constructor() { return WearSettings( allowMultitasking = dto.allowMultitasking, recordTagSelectionCloseAfterOne = dto.recordTagSelectionCloseAfterOne, + enableRepeatButton = dto.enableRepeatButton, ) } @@ -53,6 +56,20 @@ class WearDataLocalMapper @Inject constructor() { return WearSettingsDTO( allowMultitasking = domain.allowMultitasking, recordTagSelectionCloseAfterOne = domain.recordTagSelectionCloseAfterOne, + enableRepeatButton = domain.enableRepeatButton, + ) + } + + fun map(dto: WearRecordRepeatResponse): WearRecordRepeatResult { + return WearRecordRepeatResult( + result = when (dto.result) { + WearRecordRepeatResponse.ActionResult.STARTED -> + WearRecordRepeatResult.ActionResult.Started + WearRecordRepeatResponse.ActionResult.NO_PREVIOUS_FOUND -> + WearRecordRepeatResult.ActionResult.NoPreviousFound + WearRecordRepeatResponse.ActionResult.ALREADY_TRACKING -> + WearRecordRepeatResult.ActionResult.AlreadyTracking + }, ) } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt index 23757ad4b..1b5d128a6 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt @@ -8,11 +8,13 @@ package com.example.util.simpletimetracker.data import com.example.util.simpletimetracker.complication.WearComplicationManager import com.example.util.simpletimetracker.domain.model.WearActivity import com.example.util.simpletimetracker.domain.model.WearCurrentActivity +import com.example.util.simpletimetracker.domain.model.WearRecordRepeatResult import com.example.util.simpletimetracker.domain.model.WearSettings import com.example.util.simpletimetracker.domain.model.WearTag import com.example.util.simpletimetracker.notification.WearNotificationManager import com.example.util.simpletimetracker.wear_api.WearActivityDTO import com.example.util.simpletimetracker.wear_api.WearCurrentActivityDTO +import com.example.util.simpletimetracker.wear_api.WearSettingsDTO import com.example.util.simpletimetracker.wear_api.WearShouldShowTagSelectionRequest import com.example.util.simpletimetracker.wear_api.WearStartActivityRequest import com.example.util.simpletimetracker.wear_api.WearStopActivityRequest @@ -47,6 +49,7 @@ class WearDataRepo @Inject constructor( private var activitiesCache: List? = null private var currentActivitiesCache: List? = null + private var settingsCache: WearSettingsDTO? = null private val mutex: Mutex = Mutex() init { @@ -55,6 +58,7 @@ class WearDataRepo @Inject constructor( val deferred = mutableListOf>() deferred += async { loadActivities(forceReload = true) } deferred += async { loadCurrentActivities(forceReload = true) } + deferred += async { loadSettings(forceReload = true) } deferred.awaitAll() wearComplicationManager.updateComplications() wearNotificationManager.get().updateNotifications() @@ -99,6 +103,13 @@ class WearDataRepo @Inject constructor( } } + suspend fun repeatActivity(): Result = mutex.withLock { + return runCatching { + val response = wearRPCClient.repeatActivity() + wearDataLocalMapper.map(response) + } + } + suspend fun loadTagsForActivity(activityId: Long): Result> = mutex.withLock { return runCatching { val data = wearRPCClient.queryTagsForActivity(activityId) @@ -113,10 +124,14 @@ class WearDataRepo @Inject constructor( } } - suspend fun loadSettings(): Result = mutex.withLock { + suspend fun loadSettings( + forceReload: Boolean, + ): Result = mutex.withLock { return runCatching { - val data = wearRPCClient.querySettings() - wearDataLocalMapper.map(data) + val data = settingsCache.takeUnless { forceReload } + ?: wearRPCClient.querySettings() + .also { settingsCache = it } + data.let(wearDataLocalMapper::map) } } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt index 13b15e347..0acb78414 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt @@ -8,6 +8,7 @@ package com.example.util.simpletimetracker.data import com.example.util.simpletimetracker.wear_api.WearActivityDTO import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI import com.example.util.simpletimetracker.wear_api.WearCurrentActivityDTO +import com.example.util.simpletimetracker.wear_api.WearRecordRepeatResponse import com.example.util.simpletimetracker.wear_api.WearRequests import com.example.util.simpletimetracker.wear_api.WearSettingsDTO import com.example.util.simpletimetracker.wear_api.WearShouldShowTagSelectionRequest @@ -52,6 +53,14 @@ class WearRPCClient @Inject constructor( messenger.send(WearRequests.STOP_ACTIVITY, mapToBytes(request)) } + override suspend fun repeatActivity(): WearRecordRepeatResponse { + val response: WearRecordRepeatResponse? = messenger + .send(WearRequests.REPEAT_ACTIVITY) + ?.let(::mapFromBytes) + + return response ?: throw WearRPCException + } + override suspend fun queryTagsForActivity(activityId: Long): List { val response: List? = messenger .send(WearRequests.QUERY_TAGS_FOR_ACTIVITY, mapToBytes(activityId)) diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt index 967ebcbf6..6ee552eb6 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt @@ -7,6 +7,7 @@ package com.example.util.simpletimetracker.domain.mediator import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.data.WearRPCException +import com.example.util.simpletimetracker.domain.model.WearRecordRepeatResult import javax.inject.Inject class StartActivityMediator @Inject constructor( @@ -42,4 +43,8 @@ class StartActivityMediator @Inject constructor( suspend fun stop(currentId: Long): Result { return wearDataRepo.stopActivity(currentId) } + + suspend fun repeat(): Result { + return wearDataRepo.repeatActivity() + } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearRecordRepeatResult.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearRecordRepeatResult.kt new file mode 100644 index 000000000..200e30a32 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearRecordRepeatResult.kt @@ -0,0 +1,17 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.domain.model + +data class WearRecordRepeatResult( + val result: ActionResult, +) { + + sealed interface ActionResult { + object Started : ActionResult + object NoPreviousFound : ActionResult + object AlreadyTracking : ActionResult + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearSettings.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearSettings.kt index 22c245698..3633aa4a5 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearSettings.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearSettings.kt @@ -8,4 +8,5 @@ package com.example.util.simpletimetracker.domain.model data class WearSettings( val allowMultitasking: Boolean, val recordTagSelectionCloseAfterOne: Boolean, + val enableRepeatButton: Boolean, ) \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt b/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt index f6b126232..6a045d776 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt @@ -10,13 +10,16 @@ import androidx.wear.compose.navigation.SwipeDismissableNavHost import androidx.wear.compose.navigation.composable import androidx.wear.compose.navigation.rememberSwipeDismissableNavController import com.example.util.simpletimetracker.presentation.screens.activities.ActivitiesScreen +import com.example.util.simpletimetracker.presentation.screens.dialog.MessageDialog import com.example.util.simpletimetracker.presentation.screens.settings.SettingsScreen import com.example.util.simpletimetracker.presentation.screens.tagsSelection.TagsScreen +import com.example.util.simpletimetracker.utils.getString object Route { const val ACTIVITIES = "activities" const val TAGS = "activities/{id}/tags" const val SETTINGS = "settings" + const val ALERT = "alert/{textResId}" } @Composable @@ -35,6 +38,10 @@ fun WearNavigator() { onSettingsClick = { navigation.navigate(Route.SETTINGS) }, + onShowMessage = { + val route = Route.ALERT.replace("{textResId}", it.toString()) + navigation.navigate(route) + }, ) } composable(Route.TAGS) { @@ -53,5 +60,18 @@ fun WearNavigator() { composable(Route.SETTINGS) { SettingsScreen() } + composable(Route.ALERT) { + val textResId = it.arguments + ?.getString("textResId") + ?.toIntOrNull() + ?: return@composable + + MessageDialog( + message = getString(textResId), + onDismiss = { + navigation.popBackStack() + }, + ) + } } } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt index f5672fed7..8233d64d0 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt @@ -9,8 +9,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesList import com.example.util.simpletimetracker.presentation.screens.activities.ActivitiesViewModel.Effect +import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesList import com.example.util.simpletimetracker.utils.OnLifecycle import com.example.util.simpletimetracker.utils.collectEffects @@ -18,6 +18,7 @@ import com.example.util.simpletimetracker.utils.collectEffects fun ActivitiesScreen( onRequestTagSelection: (activityId: Long) -> Unit, onSettingsClick: () -> Unit, + onShowMessage: (Int) -> Unit, ) { val viewModel = hiltViewModel() viewModel.init() @@ -26,6 +27,7 @@ fun ActivitiesScreen( viewModel.effects.collectEffects(key = viewModel) { when (it) { is Effect.OnRequestTagSelection -> onRequestTagSelection(it.activityId) + is Effect.ShowMessage -> onShowMessage(it.textResId) } } @@ -33,8 +35,7 @@ fun ActivitiesScreen( ActivitiesList( state = state, - onStart = viewModel::tryStartActivity, - onStop = viewModel::stopActivity, + onItemClick = viewModel::onItemClick, onRefresh = viewModel::onRefresh, onOpenOnPhone = viewModel::onOpenOnPhone, onSettingsClick = onSettingsClick, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt index 3cf683702..ecd183cbf 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt @@ -5,16 +5,23 @@ */ package com.example.util.simpletimetracker.presentation.screens.activities +import androidx.compose.ui.graphics.toArgb import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.data.WearIconMapper +import com.example.util.simpletimetracker.data.WearResourceRepo import com.example.util.simpletimetracker.domain.model.WearActivity +import com.example.util.simpletimetracker.domain.model.WearActivityIcon import com.example.util.simpletimetracker.domain.model.WearCurrentActivity +import com.example.util.simpletimetracker.domain.model.WearSettings +import com.example.util.simpletimetracker.presentation.theme.ColorInactive import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesListState import com.example.util.simpletimetracker.presentation.ui.components.ActivityChipState +import com.example.util.simpletimetracker.presentation.ui.components.ActivityChipType import javax.inject.Inject class ActivitiesViewDataMapper @Inject constructor( private val wearIconMapper: WearIconMapper, + private val resourceRepo: WearResourceRepo, ) { fun mapErrorState(): ActivitiesListState.Error { @@ -28,10 +35,16 @@ class ActivitiesViewDataMapper @Inject constructor( fun mapContentState( activities: List, currentActivities: List, + settings: WearSettings?, showCompactList: Boolean, ): ActivitiesListState.Content { val currentActivitiesMap = currentActivities.associateBy { it.id } - val items = activities.map { activity -> + val items = mutableListOf() + + if (settings?.enableRepeatButton == true) { + items += mapRepeatItem() + } + items += activities.map { activity -> mapItem( activity = activity, currentActivity = currentActivitiesMap[activity.id], @@ -61,8 +74,19 @@ class ActivitiesViewDataMapper @Inject constructor( name = activity.name, icon = icon, color = activity.color, + type = ActivityChipType.Base, startedAt = currentActivity?.startedAt, tagString = tagString, ) } + + private fun mapRepeatItem(): ActivityChipState { + return ActivityChipState( + id = 0L, + name = resourceRepo.getString(R.string.running_records_repeat), + icon = WearActivityIcon.Image(R.drawable.wear_repeat), + color = ColorInactive.toArgb().toLong(), + type = ActivityChipType.Repeat, + ) + } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt index 840470dbb..4c5ba83d7 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt @@ -5,15 +5,20 @@ */ package com.example.util.simpletimetracker.presentation.screens.activities +import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.complication.WearComplicationManager import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.domain.interactor.WearCheckNotificationsPermissionInteractor import com.example.util.simpletimetracker.domain.interactor.WearPrefsInteractor import com.example.util.simpletimetracker.domain.mediator.StartActivityMediator +import com.example.util.simpletimetracker.domain.model.WearRecordRepeatResult import com.example.util.simpletimetracker.notification.WearNotificationManager import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesListState +import com.example.util.simpletimetracker.presentation.ui.components.ActivityChipState +import com.example.util.simpletimetracker.presentation.ui.components.ActivityChipType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -53,19 +58,49 @@ class ActivitiesViewModel @Inject constructor( isInitialized = true } - fun stopActivity(activityId: Long) = viewModelScope.launch { + fun onItemClick(item: ActivityChipState) { + when (item.type) { + is ActivityChipType.Base -> { + val isRunning = item.startedAt != null + if (isRunning) { + stopActivity(item.id) + } else { + tryStartActivity(item.id) + } + } + is ActivityChipType.Repeat -> { + repeatActivity() + } + } + } + + private fun stopActivity(activityId: Long) = viewModelScope.launch { setLoading(activityId, isLoading = true) val result = startActivitiesMediator.stop(activityId) if (result.isFailure) showError() } - fun tryStartActivity(activityId: Long) { + private fun tryStartActivity(activityId: Long) { wearCheckNotificationsPermissionInteractor.execute( onEnabled = { startActivity(activityId) }, onDisabled = { startActivity(activityId) }, ) } + private fun repeatActivity() = viewModelScope.launch { + val result = startActivitiesMediator.repeat() + when (result.getOrNull()?.result) { + is WearRecordRepeatResult.ActionResult.Started -> { + // Do nothing + } + is WearRecordRepeatResult.ActionResult.NoPreviousFound -> + showMessage(R.string.running_records_repeat_no_prev_record) + is WearRecordRepeatResult.ActionResult.AlreadyTracking -> + showMessage(R.string.running_records_repeat_already_tracking) + null -> showError() + } + } + fun onRefresh() = viewModelScope.launch { loadData(forceReload = true) wearComplicationManager.updateComplications() @@ -105,9 +140,14 @@ class ActivitiesViewModel @Inject constructor( private suspend fun loadData(forceReload: Boolean) { val activities = wearDataRepo.loadActivities(forceReload) val currentActivities = wearDataRepo.loadCurrentActivities(forceReload) + val settings = wearDataRepo.loadSettings(forceReload) + + val loadError = activities.isFailure || + currentActivities.isFailure || + settings.isFailure when { - activities.isFailure || currentActivities.isFailure -> { + loadError -> { showError() } activities.getOrNull().isNullOrEmpty() -> { @@ -117,6 +157,7 @@ class ActivitiesViewModel @Inject constructor( _state.value = activitiesViewDataMapper.mapContentState( activities = activities.getOrNull().orEmpty(), currentActivities = currentActivities.getOrNull().orEmpty(), + settings = settings.getOrNull(), showCompactList = wearPrefsInteractor.getWearShowCompactList(), ) } @@ -127,6 +168,10 @@ class ActivitiesViewModel @Inject constructor( _state.value = activitiesViewDataMapper.mapErrorState() } + private suspend fun showMessage(@StringRes textResId: Int) { + _effects.emit(Effect.ShowMessage(textResId)) + } + private fun subscribeToDataUpdates() { viewModelScope.launch { wearDataRepo.dataUpdated.collect { loadData(forceReload = false) } @@ -135,5 +180,6 @@ class ActivitiesViewModel @Inject constructor( sealed interface Effect { data class OnRequestTagSelection(val activityId: Long) : Effect + data class ShowMessage(val textResId: Int) : Effect } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/dialog/MessageDialog.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/dialog/MessageDialog.kt new file mode 100644 index 000000000..98cc7f49f --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/dialog/MessageDialog.kt @@ -0,0 +1,71 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.screens.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import androidx.wear.compose.material.Icon +import androidx.wear.compose.material.Text +import androidx.wear.compose.material.dialog.Confirmation +import androidx.wear.compose.material.dialog.Dialog +import androidx.wear.tooling.preview.devices.WearDevices +import com.example.util.simpletimetracker.R + +@Composable +fun MessageDialog( + message: String, + onDismiss: () -> Unit, +) { + Dialog( + showDialog = true, + onDismissRequest = onDismiss, + ) { + Content( + message = message, + onDismiss = onDismiss, + ) + } +} + +@Composable +private fun Content( + message: String, + onDismiss: () -> Unit = {}, +) { + Confirmation( + icon = { + Icon( + painter = painterResource(R.drawable.wear_error), + contentDescription = null, + ) + }, + onTimeout = onDismiss, + ) { + Text( + text = message, + textAlign = TextAlign.Center, + ) + } +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun Preview() { + Content( + message = "Some message", + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewLong() { + Content( + message = LoremIpsum().values.first(), + ) +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt index 4dc0f1fd0..266a9c77c 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt @@ -33,13 +33,13 @@ class SettingsViewModel @Inject constructor( fun init() { if (isInitialized) return - viewModelScope.launch { loadData() } + viewModelScope.launch { loadData(true) } subscribeToDataUpdates() isInitialized = true } fun onRefresh() = viewModelScope.launch { - loadData() + loadData(forceReload = true) } fun onSettingClick(itemType: SettingsItemType) = viewModelScope.launch { @@ -57,12 +57,12 @@ class SettingsViewModel @Inject constructor( wearPrefsInteractor.setWearShowCompactList(!value) } } - loadData() + loadData(forceReload = true) } - private suspend fun loadData() { + private suspend fun loadData(forceReload: Boolean) { val showCompactList = wearPrefsInteractor.getWearShowCompactList() - val settings = wearDataRepo.loadSettings() + val settings = wearDataRepo.loadSettings(forceReload) when { settings.isFailure -> { @@ -84,7 +84,7 @@ class SettingsViewModel @Inject constructor( private fun subscribeToDataUpdates() { viewModelScope.launch { - wearDataRepo.dataUpdated.collect { loadData() } + wearDataRepo.dataUpdated.collect { loadData(forceReload = false) } } } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt index 8fc56326c..6ccd98e1e 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt @@ -46,6 +46,7 @@ class TagsViewModel @Inject constructor( private var settings: WearSettings = WearSettings( allowMultitasking = false, recordTagSelectionCloseAfterOne = false, + enableRepeatButton = false, ) // TODO switch to savedStateHandle @@ -95,7 +96,7 @@ class TagsViewModel @Inject constructor( private fun loadData() = viewModelScope.launch { val activityId = this@TagsViewModel.activityId ?: return@launch - val settingsResult = wearDataRepo.loadSettings().getOrNull() + val settingsResult = wearDataRepo.loadSettings(forceReload = false).getOrNull() val tagsResult = wearDataRepo.loadTagsForActivity(activityId).getOrNull() if (settingsResult != null && tagsResult != null) { diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt index 7713cbbd3..bf554c711 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt @@ -23,9 +23,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope import androidx.wear.compose.material.CircularProgressIndicator import androidx.wear.compose.material.Icon -import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R @@ -56,8 +56,7 @@ sealed interface ActivitiesListState { @Composable fun ActivitiesList( state: ActivitiesListState, - onStart: (activityId: Long) -> Unit = {}, - onStop: (activityId: Long) -> Unit = {}, + onItemClick: (item: ActivityChipState) -> Unit = {}, onRefresh: () -> Unit = {}, onOpenOnPhone: () -> Unit = {}, onSettingsClick: () -> Unit = {}, @@ -78,8 +77,7 @@ fun ActivitiesList( is ActivitiesListState.Content -> { renderContent( state = state, - onStart = onStart, - onStop = onStop, + onItemClick = onItemClick, onSettingsClick = onSettingsClick, ) item { RefreshButton(onRefresh) } @@ -135,8 +133,7 @@ private fun RenderEmpty( private fun ScalingLazyListScope.renderContent( state: ActivitiesListState.Content, - onStart: (activityId: Long) -> Unit, - onStop: (activityId: Long) -> Unit, + onItemClick: (item: ActivityChipState) -> Unit, onSettingsClick: () -> Unit, ) { item { @@ -145,34 +142,24 @@ private fun ScalingLazyListScope.renderContent( if (state.isCompact) { renderContentCompact( state = state, - onStart = onStart, - onStop = onStop, + onItemClick = onItemClick, ) } else { renderContentFull( state = state, - onStart = onStart, - onStop = onStop, + onItemClick = onItemClick, ) } } private fun ScalingLazyListScope.renderContentFull( state: ActivitiesListState.Content, - onStart: (activityId: Long) -> Unit, - onStop: (activityId: Long) -> Unit, + onItemClick: (item: ActivityChipState) -> Unit, ) { for (itemState in state.items) { item(key = itemState.id) { - val isRunning = itemState.startedAt != null val onClick = remember(itemState) { - { - if (isRunning) { - onStop(itemState.id) - } else { - onStart(itemState.id) - } - } + { onItemClick(itemState) } } ActivityChip( state = itemState, @@ -184,8 +171,7 @@ private fun ScalingLazyListScope.renderContentFull( private fun ScalingLazyListScope.renderContentCompact( state: ActivitiesListState.Content, - onStart: (activityId: Long) -> Unit, - onStop: (activityId: Long) -> Unit, + onItemClick: (item: ActivityChipState) -> Unit, ) { state.items .withIndex() @@ -198,15 +184,8 @@ private fun ScalingLazyListScope.renderContentCompact( ) { CompactChipPlaceHolder(part.size) part.forEach { itemState -> - val isRunning = itemState.startedAt != null val onClick = remember(itemState) { - { - if (isRunning) { - onStop(itemState.id) - } else { - onStart(itemState.id) - } - } + { onItemClick(itemState) } } ActivityChipCompact( modifier = Modifier @@ -217,6 +196,7 @@ private fun ScalingLazyListScope.renderContentCompact( id = itemState.id, icon = itemState.icon, color = itemState.color, + type = itemState.type, startedAt = itemState.startedAt, isLoading = itemState.isLoading, ), diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt index 2964f8f5f..1cfc95cb5 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt @@ -37,6 +37,7 @@ data class ActivityChipState( val name: String, val icon: WearActivityIcon, val color: Long, + val type: ActivityChipType = ActivityChipType.Base, val startedAt: Long? = null, val tagString: String = "", val isLoading: Boolean = false, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt index f8dd27399..cb2cca333 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt @@ -38,6 +38,7 @@ data class ActivityChipCompatState( val id: Long, val icon: WearActivityIcon, val color: Long, + val type: ActivityChipType = ActivityChipType.Base, val startedAt: Long? = null, val isLoading: Boolean = false, ) diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipType.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipType.kt new file mode 100644 index 000000000..d2bc2f140 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipType.kt @@ -0,0 +1,6 @@ +package com.example.util.simpletimetracker.presentation.ui.components + +sealed interface ActivityChipType { + object Base : ActivityChipType + object Repeat : ActivityChipType +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt index 5de5f8b6e..3896fd1db 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt @@ -17,9 +17,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope import androidx.wear.compose.material.CircularProgressIndicator import androidx.wear.compose.material.Icon -import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt index de57d8f80..ced107620 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt @@ -16,9 +16,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope import androidx.wear.compose.material.CircularProgressIndicator import androidx.wear.compose.material.Icon -import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt index bce4be3e1..8f09332e4 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt @@ -8,8 +8,8 @@ package com.example.util.simpletimetracker.presentation.ui.layout import androidx.compose.runtime.Composable import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.wear.compose.material.ScalingLazyListScope -import androidx.wear.compose.material.rememberScalingLazyListState +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope +import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState @Composable fun ScaffoldedScrollingColumn( diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt index 19de39638..fd79cb6c6 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt @@ -7,13 +7,13 @@ package com.example.util.simpletimetracker.presentation.ui.layout import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.wear.compose.foundation.lazy.ScalingLazyListState +import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState import androidx.wear.compose.material.PositionIndicator import androidx.wear.compose.material.Scaffold -import androidx.wear.compose.material.ScalingLazyListState import androidx.wear.compose.material.TimeText import androidx.wear.compose.material.Vignette import androidx.wear.compose.material.VignettePosition -import androidx.wear.compose.material.rememberScalingLazyListState import androidx.wear.compose.material.scrollAway @Composable diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt index 0b4f2c62c..3020dc8c4 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt @@ -12,12 +12,12 @@ import androidx.compose.foundation.selection.selectableGroup import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp -import androidx.wear.compose.material.AutoCenteringParams +import androidx.wear.compose.foundation.lazy.AutoCenteringParams +import androidx.wear.compose.foundation.lazy.ScalingLazyColumn +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope +import androidx.wear.compose.foundation.lazy.ScalingLazyListState +import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState import androidx.wear.compose.material.MaterialTheme -import androidx.wear.compose.material.ScalingLazyColumn -import androidx.wear.compose.material.ScalingLazyListScope -import androidx.wear.compose.material.ScalingLazyListState -import androidx.wear.compose.material.rememberScalingLazyListState import com.google.android.horologist.compose.focus.rememberActiveFocusRequester import com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi import com.google.android.horologist.compose.rotaryinput.rememberRotaryHapticFeedback diff --git a/wear/src/main/res/drawable/wear_error.xml b/wear/src/main/res/drawable/wear_error.xml new file mode 100644 index 000000000..4c5029eeb --- /dev/null +++ b/wear/src/main/res/drawable/wear_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/wear/src/main/res/drawable/wear_repeat.xml b/wear/src/main/res/drawable/wear_repeat.xml new file mode 100644 index 000000000..09cd79143 --- /dev/null +++ b/wear/src/main/res/drawable/wear_repeat.xml @@ -0,0 +1,9 @@ + + + diff --git a/wear/src/main/res/drawable/wear_settings.xml b/wear/src/main/res/drawable/wear_settings.xml index a277a99d5..1b2e74bab 100644 --- a/wear/src/main/res/drawable/wear_settings.xml +++ b/wear/src/main/res/drawable/wear_settings.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt b/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt index 6b86df068..9345c6cd5 100644 --- a/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt +++ b/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt @@ -8,8 +8,6 @@ package com.example.util.simpletimetracker.wear import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.domain.mediator.StartActivityMediator import com.example.util.simpletimetracker.domain.model.WearActivity -import com.example.util.simpletimetracker.domain.model.WearSettings -import com.example.util.simpletimetracker.domain.model.WearTag import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Before @@ -28,15 +26,6 @@ class StartActivityMediatorTest { icon = "🛏️", color = 0xFF123456, ) - private val sampleTag = WearTag( - id = 13, - name = "Sleep", - color = 0xFF654321, - ) - private val settings = WearSettings( - allowMultitasking = false, - recordTagSelectionCloseAfterOne = false, - ) @Before fun setup() { diff --git a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt index 74612f478..5a2ca7261 100644 --- a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt +++ b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt @@ -34,6 +34,13 @@ interface WearCommunicationAPI { */ suspend fun stopActivity(request: WearStopActivityRequest) + /** + * [WearRequests.REPEAT_ACTIVITY] + * + * Repeat last timer. + */ + suspend fun repeatActivity(): WearRecordRepeatResponse + /** * [WearRequests.QUERY_TAGS_FOR_ACTIVITY] * diff --git a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearDTO.kt b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearDTO.kt index 3711dbd33..1b7a4bf1d 100644 --- a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearDTO.kt +++ b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearDTO.kt @@ -53,6 +53,8 @@ data class WearSettingsDTO( val allowMultitasking: Boolean, @SerializedName("recordTagSelectionCloseAfterOne") val recordTagSelectionCloseAfterOne: Boolean, + @SerializedName("enableRepeatButton") + val enableRepeatButton: Boolean, ) : Parcelable @Parcelize @@ -80,3 +82,16 @@ data class WearShouldShowTagSelectionResponse( @SerializedName("shouldShow") val shouldShow: Boolean, ) : Parcelable + +@Parcelize +data class WearRecordRepeatResponse( + @SerializedName("result") + val result: ActionResult, +) : Parcelable { + + enum class ActionResult { + STARTED, + NO_PREVIOUS_FOUND, + ALREADY_TRACKING, + } +} diff --git a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt index d9bc9b415..3b9e6d8d0 100644 --- a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt +++ b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt @@ -14,6 +14,7 @@ object WearRequests { const val QUERY_CURRENT_ACTIVITIES = "$PATH/QUERY_CURRENT_ACTIVITIES" const val START_ACTIVITY = "$PATH/START_ACTIVITY" const val STOP_ACTIVITY = "$PATH/STOP_ACTIVITY" + const val REPEAT_ACTIVITY = "$PATH/REPEAT_ACTIVITY" const val QUERY_TAGS_FOR_ACTIVITY = "$PATH/QUERY_TAGS_FOR_ACTIVITY" const val QUERY_SHOULD_SHOW_TAG_SELECTION = "$PATH/QUERY_SHOULD_SHOW_TAG_SELECTION" const val QUERY_SETTINGS = "$PATH/QUERY_SETTINGS"