diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/MainNavGraph.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/MainNavGraph.kt index 2029082..db9d8af 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/MainNavGraph.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/MainNavGraph.kt @@ -22,18 +22,23 @@ import androidx.wear.compose.foundation.lazy.ScalingLazyListState import androidx.wear.compose.navigation.composable import com.rayliu.gymnote.wearos.addrecord.AddRecordScreen import com.rayliu.gymnote.wearos.addrecord.AddRecordViewModel +import com.rayliu.gymnote.wearos.addrecord.recordinput.DEFAULT_TIME_RECORD import com.rayliu.gymnote.wearos.categorylist.CategoryListScreen import com.rayliu.gymnote.wearos.categorylist.CategoryListViewModel import com.rayliu.gymnote.wearos.navigation.CATEGORY_ID_NAV_ARGUMENT import com.rayliu.gymnote.wearos.navigation.DestinationScrollType import com.rayliu.gymnote.wearos.navigation.SCROLL_TYPE_NAV_ARGUMENT import com.rayliu.gymnote.wearos.navigation.Screen +import com.rayliu.gymnote.wearos.navigation.TIME_PICKER_NAV_ARGUMENT +import com.rayliu.gymnote.wearos.navigation.TIME_PICKER_RESULT_NAV_ARGUMENT import com.rayliu.gymnote.wearos.navigation.WORKOUT_ID_NAV_ARGUMENT +import com.rayliu.gymnote.wearos.timepicker.TimePickerScreen import com.rayliu.gymnote.wearos.workout.WorkoutScreen import com.rayliu.gymnote.wearos.workout.WorkoutViewModel import com.rayliu.gymnote.wearos.workoutlist.WorkoutListScreen import com.rayliu.gymnote.wearos.workoutlist.WorkoutListViewModel import kotlinx.collections.immutable.persistentListOf +import kotlinx.datetime.toLocalTime import org.koin.androidx.compose.navigation.koinNavViewModel fun NavGraphBuilder.mainNavGraph( @@ -153,18 +158,62 @@ fun NavGraphBuilder.mainNavGraph( nullable = false } ) - ) { + ) { entry -> val focusRequester = remember { FocusRequester() } val viewModel: AddRecordViewModel = koinNavViewModel() viewModel.performPreScreenTasks() + val recordTypes = viewModel.recordInputTypes.value + val userInputTimeRecord = + entry.savedStateHandle.get(TIME_PICKER_RESULT_NAV_ARGUMENT) + viewModel.setTimeRecord(userInputTimeRecord) + val timeRecord = viewModel.timeRecord.value ?: DEFAULT_TIME_RECORD + AddRecordScreen( recordTypes = recordTypes, focusRequester = focusRequester, + onRequestFocus = { + RequestFocusOnResume(it) + }, onCancelButtonClicked = { navController.popBackStack() + }, + onTimeAdjustButtonClicked = { + navController.navigate( + Screen.TimePickerScreen.route + "?$TIME_PICKER_NAV_ARGUMENT=$timeRecord" + ) + }, + userInputTimeRecord = timeRecord + ) + } + composable( + route = Screen.TimePickerScreen.route + + "?$TIME_PICKER_NAV_ARGUMENT={$TIME_PICKER_NAV_ARGUMENT}", + arguments = listOf( + navArgument(SCROLL_TYPE_NAV_ARGUMENT) { + type = NavType.EnumType(DestinationScrollType::class.java) + defaultValue = DestinationScrollType.NONE + }, + navArgument(TIME_PICKER_NAV_ARGUMENT) { + type = NavType.StringType + defaultValue = DEFAULT_TIME_RECORD + nullable = true } ) + ) { entry -> + val focusRequester = remember { FocusRequester() } + val defaultTime = entry.arguments?.getString(TIME_PICKER_NAV_ARGUMENT) + TimePickerScreen( + focusRequester = focusRequester, + onTimeConfirm = { resultTime -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.set(TIME_PICKER_RESULT_NAV_ARGUMENT, resultTime) + + navController.popBackStack() + }, + defaultTime = defaultTime?.toLocalTime() + ) RequestFocusOnResume(focusRequester) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/WearApp.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/WearApp.kt index 9307b5b..169df37 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/WearApp.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/WearApp.kt @@ -53,7 +53,9 @@ fun WearApp( } key(backStackEntry.destination.route) { - TimeText(modifier = timeTextModifier ?: Modifier) + TimeText( + modifier = timeTextModifier ?: Modifier + ) } } }, diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordScreen.kt index ffba951..d401667 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordScreen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordScreen.kt @@ -8,7 +8,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -33,6 +36,9 @@ fun AddRecordScreen( recordTypes: ImmutableSet, focusRequester: FocusRequester, onCancelButtonClicked: () -> Unit, + onTimeAdjustButtonClicked: () -> Unit, + onRequestFocus: @Composable (FocusRequester) -> Unit, + userInputTimeRecord: String?, modifier: Modifier = Modifier ) { Box( @@ -52,7 +58,13 @@ fun AddRecordScreen( ) { page -> for (recordType in recordTypes) { if (page == recordTypes.indexOf(recordType)) { - ShowScreenByRecordType(focusRequester, recordType) + ShowScreenByRecordType( + focusRequester, + recordType, + onTimeAdjustButtonClicked, + userInputTimeRecord + ) + onRequestFocus(focusRequester) } } @@ -85,29 +97,54 @@ fun AddRecordScreen( private fun ShowScreenByRecordType( focusRequester: FocusRequester, recordType: RecordType, + onTimeAdjustButtonClicked: () -> Unit, + userInputTimeRecord: String? ) { when (recordType) { RecordType.WEIGHT -> { + val defaultText = "0.0" + var userInput by remember { mutableStateOf(defaultText) } AddWeightRecordScreen( + focusRequester = focusRequester, + valueUnit = "kg", + defaultText = defaultText, + userInput = userInput, + onInputChanged = { userInput = it }, modifier = Modifier ) } RecordType.REPS -> { + val defaultText = "0" + var userInput by remember { mutableStateOf(defaultText) } AddRepsRecordScreen( + focusRequester = focusRequester, + valueUnit = "reps", + defaultText = defaultText, + userInput = userInput, + onInputChanged = { userInput = it }, modifier = Modifier ) } RecordType.TIME -> { AddTimeRecordScreen( - modifier = Modifier + onAdjustButtonClicked = onTimeAdjustButtonClicked, + focusRequester = focusRequester, + modifier = Modifier, + defaultText = userInputTimeRecord ) } RecordType.DISTANCE -> { + val defaultText = "0.0" + var userInput by remember { mutableStateOf(defaultText) } AddDistanceRecordScreen( focusRequester = focusRequester, + valueUnit = "km", + defaultText = defaultText, + userInput = userInput, + onInputChanged = { userInput = it }, modifier = Modifier ) } @@ -138,7 +175,10 @@ private fun AddRecordScreenPreview() { AddRecordScreen( persistentSetOf(), focusRequester = FocusRequester(), - onCancelButtonClicked = {} + onRequestFocus = {}, + onCancelButtonClicked = {}, + onTimeAdjustButtonClicked = {}, + userInputTimeRecord = null ) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordViewModel.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordViewModel.kt index 1346b78..33249c7 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordViewModel.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/AddRecordViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.rayliu.commonmain.domain.model.SportRecordType -import com.rayliu.commonmain.domain.model.WorkoutInfo import com.rayliu.commonmain.domain.usecase.GetWorkoutInfo import com.rayliu.gymnote.wearos.navigation.WORKOUT_ID_NAV_ARGUMENT import kotlinx.collections.immutable.ImmutableSet @@ -21,15 +20,12 @@ class AddRecordViewModel( private val workoutId: Int = savedStateHandle[WORKOUT_ID_NAV_ARGUMENT] ?: -1 - val workoutInfo = mutableStateOf(null) val recordInputTypes = mutableStateOf>(persistentSetOf()) + val timeRecord = mutableStateOf(null) fun performPreScreenTasks() { viewModelScope.launch { - val info = getWorkoutInfo(workoutId).also { - workoutInfo.value = it - } ?: return@launch - + val info = getWorkoutInfo(workoutId) ?: return@launch when (info.sportRecordType) { SportRecordType.WEIGHT_REPS -> { recordInputTypes.value = persistentSetOf(RecordType.WEIGHT, RecordType.REPS) @@ -56,4 +52,8 @@ class AddRecordViewModel( } } } + + fun setTimeRecord(userInputTimeRecord: String?) { + timeRecord.value = userInputTimeRecord + } } \ No newline at end of file diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddDistanceRecordScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddDistanceRecordScreen.kt index fc84d98..5e31c1d 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddDistanceRecordScreen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddDistanceRecordScreen.kt @@ -2,19 +2,24 @@ package com.rayliu.gymnote.wearos.addrecord.recordinput import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Undo import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.wear.compose.material.CompactButton +import androidx.wear.compose.material.Icon +import androidx.wear.compose.material.Text import com.rayliu.gymnote.R import com.rayliu.gymnote.wearos.theme.GymNoteTheme import com.rayliu.gymnote.wearos.theme.PreviewConstants @@ -24,6 +29,10 @@ import com.rayliu.gymnote.wearos.utils.InputUtils @Composable fun AddDistanceRecordScreen( focusRequester: FocusRequester, + valueUnit: String, + defaultText: String, + userInput: String, + onInputChanged: (String) -> Unit, modifier: Modifier = Modifier ) { Row( @@ -31,31 +40,75 @@ fun AddDistanceRecordScreen( horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxSize() ) { - val step = 1 - val defaultText = "0" - var userInput by remember { mutableStateOf(defaultText) } + val step = 0.5f + val minimumValue = 0.0f val context = LocalContext.current val waringText = stringResource(id = R.string.warning_input_type_is_not_number) + val undoButtonVisibility = if (userInput != defaultText) { + 1f + } else { + 0f + } + + Column( + verticalArrangement = Arrangement.Center + ) { + CompactButton( + onClick = { onInputChanged(defaultText) }, + modifier = Modifier.alpha(undoButtonVisibility) + ) { + Icon( + imageVector = Icons.Rounded.Undo, + contentDescription = "undo" + ) + } + } + ValueInput( currentInput = userInput, focusRequester = focusRequester, - onUserInputText = { result, minimumValue -> + isMinusValueEnabled = userInput.toFloat() > minimumValue, + onUserInputText = { result -> val newInput = result?.toString()?.trim() ?: defaultText - if (InputUtils.isNumeric(newInput) && newInput.toInt() >= minimumValue) { - userInput = newInput + if (InputUtils.isNumeric(newInput) && newInput.toFloat() >= minimumValue) { + onInputChanged(newInput) } else { Toast.makeText(context, waringText, Toast.LENGTH_SHORT).show() } }, onUserClickPlusButton = { - userInput = (it.toInt() + step).toString() + val newInput = (it.toFloat() + step).toString() + onInputChanged(newInput) }, onUserClickMinorButton = { - userInput = (it.toInt() - step).toString() + val value = it.toFloat() + if (value > minimumValue) { + val newInput = (value - step).toString() + onInputChanged(newInput) + } } ) + + Column( + verticalArrangement = Arrangement.Center + ) { + Box { + // For layout only + CompactButton( + modifier = Modifier.alpha(0f), + enabled = false, + onClick = {} + ) {} + + Text( + valueUnit, + textAlign = TextAlign.Center, + modifier = Modifier.align(Alignment.Center) + ) + } + } } } @@ -69,7 +122,11 @@ fun AddDistanceRecordScreen( private fun AddDistanceRecordPreview() { GymNoteTheme { AddDistanceRecordScreen( - FocusRequester() + FocusRequester(), + valueUnit = "km", + defaultText = "0", + userInput = "0", + onInputChanged = {} ) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddRepsRecordScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddRepsRecordScreen.kt index e0989fe..0e99dcb 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddRepsRecordScreen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddRepsRecordScreen.kt @@ -1,18 +1,38 @@ package com.rayliu.gymnote.wearos.addrecord.recordinput +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Undo import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.wear.compose.material.CompactButton +import androidx.wear.compose.material.Icon import androidx.wear.compose.material.Text +import com.rayliu.gymnote.R import com.rayliu.gymnote.wearos.theme.GymNoteTheme import com.rayliu.gymnote.wearos.theme.PreviewConstants +import com.rayliu.gymnote.wearos.ui.ValueInput +import com.rayliu.gymnote.wearos.utils.InputUtils @Composable fun AddRepsRecordScreen( + focusRequester: FocusRequester, + valueUnit: String, + defaultText: String, + userInput: String, + onInputChanged: (String) -> Unit, modifier: Modifier = Modifier ) { Row( @@ -20,7 +40,75 @@ fun AddRepsRecordScreen( horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxSize() ) { - Text("Add Reps Records") + val step = 1 + val minimumValue = 0 + + val context = LocalContext.current + val waringText = stringResource(id = R.string.warning_input_type_is_not_number) + + val undoButtonVisibility = if (userInput != defaultText) { + 1f + } else { + 0f + } + + Column( + verticalArrangement = Arrangement.Center + ) { + CompactButton( + onClick = { onInputChanged(defaultText) }, + modifier = Modifier.alpha(undoButtonVisibility) + ) { + Icon( + imageVector = Icons.Rounded.Undo, + contentDescription = "undo" + ) + } + } + + ValueInput( + currentInput = userInput, + focusRequester = focusRequester, + isMinusValueEnabled = userInput.toInt() > minimumValue, + onUserInputText = { result -> + val newInput = result?.toString()?.trim() ?: defaultText + if (InputUtils.isNumeric(newInput) && newInput.toInt() >= minimumValue) { + onInputChanged(newInput) + } else { + Toast.makeText(context, waringText, Toast.LENGTH_SHORT).show() + } + }, + onUserClickPlusButton = { + val newInput = (it.toInt() + step).toString() + onInputChanged(newInput) + }, + onUserClickMinorButton = { + val value = it.toInt() + if (value > minimumValue) { + val newInput = (value - step).toString() + onInputChanged(newInput) + } + } + ) + + Column( + verticalArrangement = Arrangement.Center + ) { + Box { + // For layout only + CompactButton( + modifier = Modifier.alpha(0f), + enabled = false, + onClick = {} + ) {} + + Text( + valueUnit, + textAlign = TextAlign.Center, + modifier = Modifier.align(Alignment.Center) + ) + } + } } } @@ -33,6 +121,12 @@ fun AddRepsRecordScreen( @Composable private fun AddRepsRecordPreview() { GymNoteTheme { - AddRepsRecordScreen() + AddRepsRecordScreen( + focusRequester = FocusRequester(), + valueUnit = "reps", + defaultText = "0", + userInput = "0", + onInputChanged = {} + ) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddTimeRecordScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddTimeRecordScreen.kt index 5934e4a..ed0cab2 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddTimeRecordScreen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddTimeRecordScreen.kt @@ -1,26 +1,56 @@ package com.rayliu.gymnote.wearos.addrecord.recordinput import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material.CompactChip import androidx.wear.compose.material.Text +import com.rayliu.gymnote.R import com.rayliu.gymnote.wearos.theme.GymNoteTheme import com.rayliu.gymnote.wearos.theme.PreviewConstants +const val DEFAULT_TIME_RECORD = "00:00:00" + @Composable fun AddTimeRecordScreen( - modifier: Modifier = Modifier + focusRequester: FocusRequester, + onAdjustButtonClicked: () -> Unit, + modifier: Modifier = Modifier, + defaultText: String? = DEFAULT_TIME_RECORD ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, - modifier = modifier.fillMaxSize() + modifier = modifier + .fillMaxSize() + .focusRequester(focusRequester) ) { - Text("Add Time Records") + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + defaultText ?: DEFAULT_TIME_RECORD, + fontSize = 22.sp + ) + CompactChip( + onClick = onAdjustButtonClicked, + label = { + Text(stringResource(id = R.string.button_action_adjust)) + }, + modifier = Modifier.padding(top = 8.dp) + ) + } } } @@ -33,6 +63,9 @@ fun AddTimeRecordScreen( @Composable private fun AddTimeRecordPreview() { GymNoteTheme { - AddTimeRecordScreen() + AddTimeRecordScreen( + onAdjustButtonClicked = {}, + focusRequester = FocusRequester() + ) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddWeightRecordScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddWeightRecordScreen.kt index 3b7ae66..b514bb0 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddWeightRecordScreen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/addrecord/recordinput/AddWeightRecordScreen.kt @@ -1,18 +1,38 @@ package com.rayliu.gymnote.wearos.addrecord.recordinput +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Undo import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.wear.compose.material.CompactButton +import androidx.wear.compose.material.Icon import androidx.wear.compose.material.Text +import com.rayliu.gymnote.R import com.rayliu.gymnote.wearos.theme.GymNoteTheme import com.rayliu.gymnote.wearos.theme.PreviewConstants +import com.rayliu.gymnote.wearos.ui.ValueInput +import com.rayliu.gymnote.wearos.utils.InputUtils @Composable fun AddWeightRecordScreen( + focusRequester: FocusRequester, + valueUnit: String, + defaultText: String, + userInput: String, + onInputChanged: (String) -> Unit, modifier: Modifier = Modifier ) { Row( @@ -20,7 +40,75 @@ fun AddWeightRecordScreen( horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxSize() ) { - Text("Add Weight Records") + val step = 0.5f + val minimumValue = 0.0f + + val context = LocalContext.current + val waringText = stringResource(id = R.string.warning_input_type_is_not_number) + + val undoButtonVisibility = if (userInput != defaultText) { + 1f + } else { + 0f + } + + Column( + verticalArrangement = Arrangement.Center + ) { + CompactButton( + onClick = { onInputChanged(defaultText) }, + modifier = Modifier.alpha(undoButtonVisibility) + ) { + Icon( + imageVector = Icons.Rounded.Undo, + contentDescription = "undo" + ) + } + } + + ValueInput( + currentInput = userInput, + focusRequester = focusRequester, + isMinusValueEnabled = userInput.toFloat() > minimumValue, + onUserInputText = { result -> + val newInput = result?.toString()?.trim() ?: defaultText + if (InputUtils.isNumeric(newInput) && newInput.toFloat() >= minimumValue) { + onInputChanged(newInput) + } else { + Toast.makeText(context, waringText, Toast.LENGTH_SHORT).show() + } + }, + onUserClickPlusButton = { + val newInput = (it.toFloat() + step).toString() + onInputChanged(newInput) + }, + onUserClickMinorButton = { + val value = it.toFloat() + if (value > minimumValue) { + val newInput = (value - step).toString() + onInputChanged(newInput) + } + } + ) + + Column( + verticalArrangement = Arrangement.Center + ) { + Box { + // For layout only + CompactButton( + modifier = Modifier.alpha(0f), + enabled = false, + onClick = {} + ) {} + + Text( + valueUnit, + textAlign = TextAlign.Center, + modifier = Modifier.align(Alignment.Center) + ) + } + } } } @@ -33,6 +121,12 @@ fun AddWeightRecordScreen( @Composable private fun AddWeightRecordPreview() { GymNoteTheme { - AddWeightRecordScreen() + AddWeightRecordScreen( + focusRequester = FocusRequester(), + valueUnit = "kg", + defaultText = "0.0", + userInput = "0.0", + onInputChanged = {} + ) } } diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/navigation/Screen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/navigation/Screen.kt index d80dd90..fae2d05 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/navigation/Screen.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/navigation/Screen.kt @@ -3,12 +3,15 @@ package com.rayliu.gymnote.wearos.navigation const val SCROLL_TYPE_NAV_ARGUMENT = "scrollType" const val CATEGORY_ID_NAV_ARGUMENT = "categoryId" const val WORKOUT_ID_NAV_ARGUMENT = "workoutId" +const val TIME_PICKER_RESULT_NAV_ARGUMENT = "timePickerResult" +const val TIME_PICKER_NAV_ARGUMENT = "timePickerTime" sealed class Screen(val route: String) { object SportsCategory : Screen("sports_category") object WorkoutList : Screen("workout_list") object Workout : Screen("workout") object AddWorkoutRecord : Screen("add_workout_record") + object TimePickerScreen : Screen("time_picker_screen") fun withArguments(vararg arguments: String): String { return buildString { diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/timepicker/TimePickerScreen.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/timepicker/TimePickerScreen.kt new file mode 100644 index 0000000..f0ee374 --- /dev/null +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/timepicker/TimePickerScreen.kt @@ -0,0 +1,65 @@ +package com.rayliu.gymnote.wearos.timepicker + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.google.android.horologist.composables.TimePicker +import com.rayliu.gymnote.wearos.addrecord.recordinput.DEFAULT_TIME_RECORD +import com.rayliu.gymnote.wearos.theme.GymNoteTheme +import com.rayliu.gymnote.wearos.theme.PreviewConstants +import kotlinx.datetime.LocalTime +import kotlinx.datetime.toJavaLocalTime +import kotlinx.datetime.toLocalTime + +@Composable +fun TimePickerScreen( + focusRequester: FocusRequester, + onTimeConfirm: (String) -> Unit, + modifier: Modifier = Modifier, + defaultTime: LocalTime? = null, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = modifier.fillMaxSize() + ) { + val localTime = + defaultTime?.toJavaLocalTime() ?: DEFAULT_TIME_RECORD.toLocalTime().toJavaLocalTime() + TimePicker( + time = localTime, + onTimeConfirm = { + val hourText = "%02d".format(it.hour) + val minuteText = "%02d".format(it.minute) + val secondText = "%02d".format(it.second) + onTimeConfirm("$hourText:$minuteText:$secondText") + }, + modifier = Modifier + .padding(6.dp) + .focusRequester(focusRequester) + ) + } +} + +@Preview( + group = "Screen Preview", + widthDp = PreviewConstants.WEAR_PREVIEW_DEVICE_WIDTH_DP, + heightDp = PreviewConstants.WEAR_PREVIEW_DEVICE_HEIGHT_DP, + apiLevel = PreviewConstants.WEAR_PREVIEW_API_LEVEL +) +@Composable +fun TimePickerScreenPreview() { + GymNoteTheme { + TimePickerScreen( + focusRequester = FocusRequester(), + onTimeConfirm = {} + ) + } +} diff --git a/wear/src/main/java/com/rayliu/gymnote/wearos/ui/WearInputUI.kt b/wear/src/main/java/com/rayliu/gymnote/wearos/ui/WearInputUI.kt index a7deca1..0e64eea 100644 --- a/wear/src/main/java/com/rayliu/gymnote/wearos/ui/WearInputUI.kt +++ b/wear/src/main/java/com/rayliu/gymnote/wearos/ui/WearInputUI.kt @@ -25,12 +25,12 @@ import kotlin.math.roundToInt @Composable fun ValueInput( focusRequester: FocusRequester, - onUserInputText: (CharSequence?, minimumValue: Int) -> Unit, + currentInput: String, + isMinusValueEnabled: Boolean, + onUserInputText: (CharSequence?) -> Unit, onUserClickPlusButton: (String) -> Unit, onUserClickMinorButton: (String) -> Unit, - modifier: Modifier = Modifier, - currentInput: String = "0", - minimumValue: Int = 0 + modifier: Modifier = Modifier ) { Column( verticalArrangement = Arrangement.Center, @@ -43,7 +43,7 @@ fun ValueInput( val accumulatedValue = it.roundToInt() if (accumulatedValue > 0) { onUserClickPlusButton(currentInput) - } else if (accumulatedValue < 0 && currentInput.toInt() > minimumValue) { + } else if (accumulatedValue < 0) { onUserClickMinorButton(currentInput) } } @@ -60,15 +60,13 @@ fun ValueInput( WearEditText( text = currentInput, - onUserInputText = { - onUserInputText(it, minimumValue) - }, + onUserInputText = onUserInputText, fontSize = 20.sp ) CompactButton( onClick = { onUserClickMinorButton(currentInput) }, - enabled = currentInput.toInt() > minimumValue, + enabled = isMinusValueEnabled, modifier = Modifier.padding(top = 6.dp) ) { Image( @@ -90,9 +88,11 @@ fun ValueInputPreview() { GymNoteTheme { ValueInput( focusRequester = FocusRequester(), - onUserInputText = { _, _ -> }, + currentInput = "0", + isMinusValueEnabled = true, + onUserInputText = {}, onUserClickPlusButton = {}, - onUserClickMinorButton = {}, + onUserClickMinorButton = {} ) } } \ No newline at end of file diff --git a/wear/src/main/res/values-zh-rTW/strings.xml b/wear/src/main/res/values-zh-rTW/strings.xml index d9b9d03..d619f5a 100644 --- a/wear/src/main/res/values-zh-rTW/strings.xml +++ b/wear/src/main/res/values-zh-rTW/strings.xml @@ -6,4 +6,5 @@ %1$s 下 %1$s 公里 請輸入數字 + 調整 \ No newline at end of file diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml index 92483a9..061e278 100644 --- a/wear/src/main/res/values/strings.xml +++ b/wear/src/main/res/values/strings.xml @@ -6,4 +6,5 @@ %1$s Reps %1$s Km Please enter numbers only + Changes \ No newline at end of file