commit
						81fa345453
					
				
					 23 changed files with 232 additions and 604 deletions
				
			
		|  | @ -2,18 +2,8 @@ package be.ugent.sel.studeez | ||||||
| 
 | 
 | ||||||
| import android.content.res.Resources | import android.content.res.Resources | ||||||
| import androidx.compose.foundation.layout.padding | import androidx.compose.foundation.layout.padding | ||||||
| import androidx.compose.material.MaterialTheme | import androidx.compose.material.* | ||||||
| import androidx.compose.material.Scaffold | import androidx.compose.runtime.* | ||||||
| import androidx.compose.material.ScaffoldState |  | ||||||
| import androidx.compose.material.Snackbar |  | ||||||
| import androidx.compose.material.SnackbarHost |  | ||||||
| import androidx.compose.material.Surface |  | ||||||
| import androidx.compose.material.rememberScaffoldState |  | ||||||
| import androidx.compose.runtime.Composable |  | ||||||
| import androidx.compose.runtime.ReadOnlyComposable |  | ||||||
| import androidx.compose.runtime.getValue |  | ||||||
| import androidx.compose.runtime.remember |  | ||||||
| import androidx.compose.runtime.rememberCoroutineScope |  | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.platform.LocalConfiguration | import androidx.compose.ui.platform.LocalConfiguration | ||||||
| import androidx.compose.ui.platform.LocalContext | import androidx.compose.ui.platform.LocalContext | ||||||
|  | @ -48,9 +38,10 @@ import be.ugent.sel.studeez.screens.tasks.forms.SubjectAddRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.SubjectEditRoute | import be.ugent.sel.studeez.screens.tasks.forms.SubjectEditRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.TaskAddRoute | import be.ugent.sel.studeez.screens.tasks.forms.TaskAddRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.TaskEditRoute | import be.ugent.sel.studeez.screens.tasks.forms.TaskEditRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.TimerEditRoute | import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute | import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_overview.add_timer.AddTimerRoute |  | ||||||
| import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionRoute | import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionRoute | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
| import kotlinx.coroutines.CoroutineScope | import kotlinx.coroutines.CoroutineScope | ||||||
|  | @ -245,6 +236,13 @@ fun StudeezNavGraph( | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         composable(StudeezDestinations.TIMER_TYPE_CHOOSING_SCREEN) { | ||||||
|  |             TimerTypeSelectScreen( | ||||||
|  |                 open = open, | ||||||
|  |                 popUp = goBack | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         composable(StudeezDestinations.SESSION_SCREEN) { |         composable(StudeezDestinations.SESSION_SCREEN) { | ||||||
|             SessionRoute( |             SessionRoute( | ||||||
|                 open, |                 open, | ||||||
|  | @ -261,16 +259,14 @@ fun StudeezNavGraph( | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         composable(StudeezDestinations.ADD_TIMER_SCREEN) { |         composable(StudeezDestinations.ADD_TIMER_SCREEN) { | ||||||
|             AddTimerRoute( |             TimerAddRoute( | ||||||
|                 open = open, |                 popUp = goBack, | ||||||
|                 goBack = goBack, |  | ||||||
|                 viewModel = hiltViewModel() |                 viewModel = hiltViewModel() | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         composable(StudeezDestinations.TIMER_EDIT_SCREEN) { |         composable(StudeezDestinations.TIMER_EDIT_SCREEN) { | ||||||
|             TimerEditRoute( |             TimerEditRoute( | ||||||
|                 open = open, |  | ||||||
|                 popUp = goBack, |                 popUp = goBack, | ||||||
|                 viewModel = hiltViewModel() |                 viewModel = hiltViewModel() | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| package be.ugent.sel.studeez.common.composable | package be.ugent.sel.studeez.common.composable | ||||||
| 
 | 
 | ||||||
| import androidx.annotation.StringRes | import androidx.annotation.StringRes | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.text.KeyboardActions | ||||||
| import androidx.compose.foundation.text.KeyboardOptions | import androidx.compose.foundation.text.KeyboardOptions | ||||||
| import androidx.compose.material.Icon | import androidx.compose.material.* | ||||||
| import androidx.compose.material.IconButton |  | ||||||
| import androidx.compose.material.OutlinedTextField |  | ||||||
| import androidx.compose.material.Text |  | ||||||
| import androidx.compose.material.icons.Icons | import androidx.compose.material.icons.Icons | ||||||
| import androidx.compose.material.icons.filled.Email | import androidx.compose.material.icons.filled.Email | ||||||
| import androidx.compose.material.icons.filled.Lock | import androidx.compose.material.icons.filled.Lock | ||||||
|  | @ -14,10 +14,15 @@ import androidx.compose.runtime.* | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.res.painterResource | import androidx.compose.ui.res.painterResource | ||||||
| import androidx.compose.ui.res.stringResource | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.text.input.ImeAction | ||||||
| import androidx.compose.ui.text.input.KeyboardType | import androidx.compose.ui.text.input.KeyboardType | ||||||
| import androidx.compose.ui.text.input.PasswordVisualTransformation | import androidx.compose.ui.text.input.PasswordVisualTransformation | ||||||
| import androidx.compose.ui.text.input.VisualTransformation | import androidx.compose.ui.text.input.VisualTransformation | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
| import be.ugent.sel.studeez.common.ext.fieldModifier | import be.ugent.sel.studeez.common.ext.fieldModifier | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import kotlin.math.sin | ||||||
| import be.ugent.sel.studeez.R.drawable as AppIcon | import be.ugent.sel.studeez.R.drawable as AppIcon | ||||||
| import be.ugent.sel.studeez.R.string as AppText | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
|  | @ -85,6 +90,86 @@ fun EmailField( | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @Composable | ||||||
|  | fun LabeledNumberInputField( | ||||||
|  |     value: Int, | ||||||
|  |     onNewValue: (Int) -> Unit, | ||||||
|  |     @StringRes label: Int, | ||||||
|  |     singleLine: Boolean = false | ||||||
|  | ) { | ||||||
|  |     var number by remember { mutableStateOf(value) } | ||||||
|  |     OutlinedTextField( | ||||||
|  |         value = number.toString(), | ||||||
|  |         singleLine = singleLine, | ||||||
|  |         label = { Text(resources().getString(label)) }, | ||||||
|  |         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), | ||||||
|  |         onValueChange = {typedInt -> | ||||||
|  |             val isNumber = typedInt.matches(Regex("[1-9]+\\d*]")) | ||||||
|  |             if (isNumber) { | ||||||
|  |                 number = typedInt.toInt() | ||||||
|  |                 onNewValue(typedInt.toInt()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun LabeledErrorTextField( | ||||||
|  |     modifier: Modifier = Modifier, | ||||||
|  |     initialValue: String, | ||||||
|  |     @StringRes label: Int, | ||||||
|  |     singleLine: Boolean = false, | ||||||
|  |     errorText: Int, | ||||||
|  |     keyboardType: KeyboardType, | ||||||
|  |     predicate: (String) -> Boolean, | ||||||
|  |     onNewCorrectValue: (String) -> Unit | ||||||
|  | ) { | ||||||
|  |     var value by remember { | ||||||
|  |         mutableStateOf(initialValue) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var isValid by remember { | ||||||
|  |         mutableStateOf(predicate(value)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Column { | ||||||
|  |         OutlinedTextField( | ||||||
|  |             modifier = modifier.fieldModifier(), | ||||||
|  |             value = value, | ||||||
|  |             onValueChange = { newText -> | ||||||
|  |                 value = newText | ||||||
|  |                 isValid = predicate(value) | ||||||
|  |                 if (isValid) { | ||||||
|  |                     onNewCorrectValue(newText) | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             singleLine = singleLine, | ||||||
|  |             label = { Text(text = stringResource(id = label)) }, | ||||||
|  |             isError = !isValid, | ||||||
|  |             keyboardOptions = KeyboardOptions( | ||||||
|  |                 keyboardType = keyboardType, | ||||||
|  |                 imeAction = ImeAction.Done | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         if (!isValid) { | ||||||
|  |             Text( | ||||||
|  |                 modifier = Modifier.padding(start = 16.dp), | ||||||
|  |                 text = stringResource(id = errorText), | ||||||
|  |                 color = MaterialTheme.colors.error | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |  @Preview(showBackground = true) | ||||||
|  |  @Composable | ||||||
|  |  fun IntInputPreview() { | ||||||
|  |      LabeledNumberInputField(value = 1, onNewValue = {}, label = AppText.email) | ||||||
|  |  } | ||||||
|  | 
 | ||||||
| @Composable | @Composable | ||||||
| fun PasswordField( | fun PasswordField( | ||||||
|     value: String, |     value: String, | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ class PomodoroTimerInfo( | ||||||
|     description: String, |     description: String, | ||||||
|     var studyTime: Int, |     var studyTime: Int, | ||||||
|     var breakTime: Int, |     var breakTime: Int, | ||||||
|     val repeats: Int, |     var repeats: Int, | ||||||
|     id: String = "" |     id: String = "" | ||||||
| ):  TimerInfo(id, name, description) { | ):  TimerInfo(id, name, description) { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ object StudeezDestinations { | ||||||
|     // Studying flow |     // Studying flow | ||||||
|     const val TIMER_SELECTION_SCREEN = "timer_selection" |     const val TIMER_SELECTION_SCREEN = "timer_selection" | ||||||
|     const val TIMER_EDIT_SCREEN = "timer_edit" |     const val TIMER_EDIT_SCREEN = "timer_edit" | ||||||
|  |     const val TIMER_TYPE_CHOOSING_SCREEN = "timer_type_choosing_screen" | ||||||
|     const val SESSION_SCREEN = "session" |     const val SESSION_SCREEN = "session" | ||||||
|     const val SESSION_RECAP = "session_recap" |     const val SESSION_RECAP = "session_recap" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,44 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_add |  | ||||||
| 
 |  | ||||||
| import androidx.compose.runtime.Composable |  | ||||||
| import androidx.compose.runtime.getValue |  | ||||||
| import androidx.compose.ui.tooling.preview.Preview |  | ||||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.GetTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.TimerEditViewModel |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.AbstractTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme |  | ||||||
| 
 |  | ||||||
| data class TimerEditActions( |  | ||||||
|     val getTimerInfo: () -> TimerInfo, |  | ||||||
|     val saveTimer: (TimerInfo, () -> Unit) -> Unit |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| fun getTimerEditActions( |  | ||||||
|     viewModel: TimerEditViewModel, |  | ||||||
|     open: (String) -> Unit |  | ||||||
| ): TimerEditActions { |  | ||||||
|     return TimerEditActions( |  | ||||||
|         getTimerInfo = viewModel::getTimerInfo, |  | ||||||
|         saveTimer = viewModel::saveTimer |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun TimerEditRoute( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     popUp: () -> Unit, |  | ||||||
|     viewModel: TimerEditViewModel, |  | ||||||
| ) { |  | ||||||
| 
 |  | ||||||
|     val timerEditActions = getTimerEditActions(viewModel, open) |  | ||||||
| 
 |  | ||||||
|     SecondaryScreenTemplate(title = "Edit Timer", popUp = popUp) { |  | ||||||
| 
 |  | ||||||
|         val timerEditScreen = timerEditActions.getTimerInfo().accept(GetTimerEditScreen()) |  | ||||||
|         timerEditScreen { timerInfo -> |  | ||||||
|             timerEditActions.saveTimer(timerInfo, popUp) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_add |  | ||||||
| 
 |  | ||||||
| import be.ugent.sel.studeez.data.EditTimerState |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo |  | ||||||
| import be.ugent.sel.studeez.domain.LogService |  | ||||||
| import be.ugent.sel.studeez.domain.TimerDAO |  | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel |  | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel |  | ||||||
| import javax.inject.Inject |  | ||||||
| 
 |  | ||||||
| @HiltViewModel |  | ||||||
| class TimerAddViewModel @Inject constructor( |  | ||||||
|     private val editTimerState: EditTimerState, |  | ||||||
|     private val timerDAO: TimerDAO, |  | ||||||
|     logService: LogService |  | ||||||
| ) : StudeezViewModel(logService) { |  | ||||||
| 
 |  | ||||||
|     private val timerInfo: TimerInfo = editTimerState.timerInfo |  | ||||||
| 
 |  | ||||||
|     fun getTimerInfo(): TimerInfo { |  | ||||||
|         return timerInfo |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) { |  | ||||||
|         timerDAO.updateTimer(timerInfo) |  | ||||||
|         goBack() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit |  | ||||||
| 
 |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfoVisitor |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.AbstractTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.BreakTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.CustomTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.EndlessTimerEditScreen |  | ||||||
| 
 |  | ||||||
| class GetTimerEditScreen: TimerInfoVisitor<AbstractTimerEditScreen> { |  | ||||||
| 
 |  | ||||||
|     override fun visitCustomTimerInfo(customTimerInfo: CustomTimerInfo): AbstractTimerEditScreen { |  | ||||||
|         return CustomTimerEditScreen(customTimerInfo) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun visitEndlessTimerInfo(endlessTimerInfo: EndlessTimerInfo): AbstractTimerEditScreen { |  | ||||||
|         return EndlessTimerEditScreen(endlessTimerInfo) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun visitBreakTimerInfo(pomodoroTimerInfo: PomodoroTimerInfo): AbstractTimerEditScreen { |  | ||||||
|         return BreakTimerEditScreen(pomodoroTimerInfo) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,65 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit |  | ||||||
| 
 |  | ||||||
| import androidx.compose.runtime.Composable |  | ||||||
| import androidx.compose.runtime.getValue |  | ||||||
| import androidx.compose.ui.tooling.preview.Preview |  | ||||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo |  | ||||||
| import be.ugent.sel.studeez.screens.timer_edit.editScreens.AbstractTimerEditScreen |  | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme |  | ||||||
| 
 |  | ||||||
| data class TimerEditActions( |  | ||||||
|     val getTimerInfo: () -> TimerInfo, |  | ||||||
|     val saveTimer: (TimerInfo, () -> Unit) -> Unit |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| fun getTimerEditActions( |  | ||||||
|     viewModel: TimerEditViewModel, |  | ||||||
|     open: (String) -> Unit |  | ||||||
| ): TimerEditActions { |  | ||||||
|     return TimerEditActions( |  | ||||||
|         getTimerInfo = viewModel::getTimerInfo, |  | ||||||
|         saveTimer = viewModel::saveTimer |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun TimerEditRoute( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     popUp: () -> Unit, |  | ||||||
|     viewModel: TimerEditViewModel, |  | ||||||
| ) { |  | ||||||
| 
 |  | ||||||
|     val timerEditActions = getTimerEditActions(viewModel, open) |  | ||||||
| 
 |  | ||||||
|     SecondaryScreenTemplate(title = "Edit Timer", popUp = popUp) { |  | ||||||
| 
 |  | ||||||
|         val timerEditScreen = timerEditActions.getTimerInfo().accept(GetTimerEditScreen()) |  | ||||||
|         timerEditScreen { timerInfo -> |  | ||||||
|             timerEditActions.saveTimer(timerInfo, popUp) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.timer_form | ||||||
|  | 
 | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfoVisitor | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.form_screens.AbstractTimerFormScreen | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.form_screens.BreakTimerFormScreen | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.form_screens.CustomTimerFormScreen | ||||||
|  | import be.ugent.sel.studeez.screens.timer_form.form_screens.EndlessTimerFormScreen | ||||||
|  | 
 | ||||||
|  | class GetTimerFormScreen: TimerInfoVisitor<AbstractTimerFormScreen> { | ||||||
|  | 
 | ||||||
|  |     override fun visitCustomTimerInfo(customTimerInfo: CustomTimerInfo): AbstractTimerFormScreen { | ||||||
|  |         return CustomTimerFormScreen(customTimerInfo) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun visitEndlessTimerInfo(endlessTimerInfo: EndlessTimerInfo): AbstractTimerFormScreen { | ||||||
|  |         return EndlessTimerFormScreen(endlessTimerInfo) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun visitBreakTimerInfo(pomodoroTimerInfo: PomodoroTimerInfo): AbstractTimerFormScreen { | ||||||
|  |         return BreakTimerFormScreen(pomodoroTimerInfo) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.timer_form | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.StringRes | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun TimerAddRoute( | ||||||
|  |     popUp: () -> Unit, | ||||||
|  |     viewModel: TimerFormViewModel | ||||||
|  | ) { | ||||||
|  |     TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) { | ||||||
|  |         viewModel.saveTimer(it, goBack = popUp) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun TimerEditRoute( | ||||||
|  |     popUp: () -> Unit, | ||||||
|  |     viewModel: TimerFormViewModel | ||||||
|  | ) { | ||||||
|  |     TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) { | ||||||
|  |         viewModel.editTimer(it, goBack = popUp) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun TimerFormScreen( | ||||||
|  |     popUp: () -> Unit, | ||||||
|  |     getTimerInfo: () -> TimerInfo, | ||||||
|  |     @StringRes label: Int, | ||||||
|  |     onConfirmClick: (TimerInfo) -> Unit | ||||||
|  | ) { | ||||||
|  |     val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen()) | ||||||
|  | 
 | ||||||
|  |     SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) { | ||||||
|  |         timerFormScreen(onConfirmClick) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit | package be.ugent.sel.studeez.screens.timer_form | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.EditTimerState | import be.ugent.sel.studeez.data.EditTimerState | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
|  | @ -9,7 +9,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | @HiltViewModel | ||||||
| class TimerEditViewModel @Inject constructor( | class TimerFormViewModel @Inject constructor( | ||||||
|     private val editTimerState: EditTimerState, |     private val editTimerState: EditTimerState, | ||||||
|     private val timerDAO: TimerDAO, |     private val timerDAO: TimerDAO, | ||||||
|     logService: LogService |     logService: LogService | ||||||
|  | @ -21,9 +21,13 @@ class TimerEditViewModel @Inject constructor( | ||||||
|         return timerInfo |         return timerInfo | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) { |     fun editTimer(timerInfo: TimerInfo, goBack: () -> Unit) { | ||||||
|         timerDAO.updateTimer(timerInfo) |         timerDAO.updateTimer(timerInfo) | ||||||
|         goBack() |         goBack() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) { | ||||||
|  |         timerDAO.saveTimer(timerInfo) | ||||||
|  |         goBack() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | package be.ugent.sel.studeez.screens.timer_form.form_screens | ||||||
| 
 | 
 | ||||||
| import androidx.compose.foundation.layout.Arrangement | import androidx.compose.foundation.layout.Arrangement | ||||||
| import androidx.compose.foundation.layout.Column | import androidx.compose.foundation.layout.Column | ||||||
|  | @ -18,8 +18,9 @@ import be.ugent.sel.studeez.common.composable.BasicButton | ||||||
| import be.ugent.sel.studeez.common.composable.LabelledInputField | import be.ugent.sel.studeez.common.composable.LabelledInputField | ||||||
| import be.ugent.sel.studeez.common.ext.basicButton | import be.ugent.sel.studeez.common.ext.basicButton | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| abstract class AbstractTimerEditScreen(private val timerInfo: TimerInfo) { | abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { | ||||||
| 
 | 
 | ||||||
|     @Composable |     @Composable | ||||||
|     operator fun invoke(onSaveClick: (TimerInfo) -> Unit) { |     operator fun invoke(onSaveClick: (TimerInfo) -> Unit) { | ||||||
|  | @ -50,7 +51,7 @@ abstract class AbstractTimerEditScreen(private val timerInfo: TimerInfo) { | ||||||
|                 LabelledInputField( |                 LabelledInputField( | ||||||
|                     value = description, |                     value = description, | ||||||
|                     onNewValue = { description = it }, |                     onNewValue = { description = it }, | ||||||
|                     label = R.string.description, |                     label = AppText.description, | ||||||
|                     singleLine = false |                     singleLine = false | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
|  | @ -1,20 +1,19 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | package be.ugent.sel.studeez.screens.timer_form.form_screens | ||||||
| 
 | 
 | ||||||
| import androidx.compose.runtime.* | import androidx.compose.runtime.* | ||||||
|  | import androidx.compose.ui.text.input.KeyboardType | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import be.ugent.sel.studeez.R | import be.ugent.sel.studeez.R | ||||||
| import be.ugent.sel.studeez.common.composable.TimePickerButton | import be.ugent.sel.studeez.common.composable.LabeledErrorTextField | ||||||
| import be.ugent.sel.studeez.common.composable.TimePickerCard | import be.ugent.sel.studeez.common.composable.TimePickerCard | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.Time |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| class BreakTimerEditScreen( | 
 | ||||||
|  | class BreakTimerFormScreen( | ||||||
|     private val breakTimerInfo: PomodoroTimerInfo |     private val breakTimerInfo: PomodoroTimerInfo | ||||||
| ): AbstractTimerEditScreen(breakTimerInfo) { | ): AbstractTimerFormScreen(breakTimerInfo) { | ||||||
| 
 | 
 | ||||||
|     @Composable |     @Composable | ||||||
|     override fun ExtraFields() { |     override fun ExtraFields() { | ||||||
|  | @ -26,9 +25,19 @@ class BreakTimerEditScreen( | ||||||
|         TimePickerCard(R.string.breakTime, breakTimerInfo.breakTime) { newTime -> |         TimePickerCard(R.string.breakTime, breakTimerInfo.breakTime) { newTime -> | ||||||
|             breakTimerInfo.breakTime = newTime |             breakTimerInfo.breakTime = newTime | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         LabeledErrorTextField( | ||||||
|  |             initialValue = breakTimerInfo.repeats.toString(), | ||||||
|  |             label = R.string.repeats, | ||||||
|  |             errorText = AppText.repeats_error, | ||||||
|  |             keyboardType = KeyboardType.Decimal, | ||||||
|  |             predicate = { it.matches(Regex("[1-9]+\\d*")) } | ||||||
|  |         ) { correctlyTypedInt -> | ||||||
|  |             breakTimerInfo.repeats = correctlyTypedInt.toInt() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| @Preview | @Preview | ||||||
| @Composable | @Composable | ||||||
|  | @ -41,6 +50,6 @@ fun BreakEditScreenPreview() { | ||||||
|         5 |         5 | ||||||
|     ) |     ) | ||||||
|     StudeezTheme { |     StudeezTheme { | ||||||
|         BreakTimerEditScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) |         BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | package be.ugent.sel.studeez.screens.timer_form.form_screens | ||||||
| 
 | 
 | ||||||
| import androidx.compose.runtime.* | import androidx.compose.runtime.* | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | @ -7,9 +7,9 @@ import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
| import be.ugent.sel.studeez.R.string as AppText | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| class CustomTimerEditScreen( | class CustomTimerFormScreen( | ||||||
|     private val customTimerInfo: CustomTimerInfo |     private val customTimerInfo: CustomTimerInfo | ||||||
|     ): AbstractTimerEditScreen(customTimerInfo) { |     ): AbstractTimerFormScreen(customTimerInfo) { | ||||||
| 
 | 
 | ||||||
|     @Composable |     @Composable | ||||||
|     override fun ExtraFields() { |     override fun ExtraFields() { | ||||||
|  | @ -29,6 +29,6 @@ class CustomTimerEditScreen( | ||||||
| fun CustomEditScreenPreview() { | fun CustomEditScreenPreview() { | ||||||
|     val customTimerInfo = CustomTimerInfo("custom", "my description", 25) |     val customTimerInfo = CustomTimerInfo("custom", "my description", 25) | ||||||
|     StudeezTheme { |     StudeezTheme { | ||||||
|         CustomTimerEditScreen(customTimerInfo).invoke(onSaveClick = {}) |         CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | package be.ugent.sel.studeez.screens.timer_form.form_screens | ||||||
| 
 | 
 | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
| 
 | 
 | ||||||
| class EndlessTimerEditScreen( | class EndlessTimerFormScreen( | ||||||
|     endlessTimerInfo: EndlessTimerInfo |     endlessTimerInfo: EndlessTimerInfo | ||||||
| ): AbstractTimerEditScreen(endlessTimerInfo) { | ): AbstractTimerFormScreen(endlessTimerInfo) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Preview | @Preview | ||||||
|  | @ -18,6 +18,6 @@ fun EndlessEditScreenPreview() { | ||||||
|         "My endless timer description", |         "My endless timer description", | ||||||
|     ) |     ) | ||||||
|     StudeezTheme { |     StudeezTheme { | ||||||
|         EndlessTimerEditScreen(endlessTimerInfo).invoke(onSaveClick = {}) |         EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_add | package be.ugent.sel.studeez.screens.timer_form.timer_type_select | ||||||
| 
 | 
 | ||||||
| import androidx.compose.foundation.layout.Column | import androidx.compose.foundation.layout.Column | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | @ -7,16 +7,18 @@ import androidx.compose.material.Text | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
| import androidx.hilt.navigation.compose.hiltViewModel | import androidx.hilt.navigation.compose.hiltViewModel | ||||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.* | import be.ugent.sel.studeez.data.local.models.timer_info.* | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.CUSTOM | import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.CUSTOM | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.BREAK | import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.BREAK | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.ENDLESS | import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.ENDLESS | ||||||
| 
 | 
 | ||||||
| val defaultTimerInfo: Map<TimerType, TimerInfo> = mapOf( | val defaultTimerInfo: Map<TimerType, TimerInfo> = mapOf( | ||||||
|     CUSTOM to CustomTimerInfo("", "", 0), |     CUSTOM to CustomTimerInfo("", "", 0), | ||||||
|     BREAK to PomodoroTimerInfo("", "", 0, 0, 0), |     BREAK to PomodoroTimerInfo("", "", 0, 0, 1), | ||||||
|     ENDLESS to EndlessTimerInfo("", ""), |     ENDLESS to EndlessTimerInfo("", ""), | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -28,13 +30,14 @@ fun TimerTypeSelectScreen( | ||||||
|     viewModel: TimerTypeSelectViewModel = hiltViewModel() |     viewModel: TimerTypeSelectViewModel = hiltViewModel() | ||||||
| ) { | ) { | ||||||
| 
 | 
 | ||||||
|     SecondaryScreenTemplate(title = "Edit Timer", popUp = popUp) { |     SecondaryScreenTemplate(title = stringResource(id = AppText.timer_type_select), popUp = popUp) { | ||||||
|         Column( |         Column( | ||||||
|             horizontalAlignment = Alignment.CenterHorizontally, |             horizontalAlignment = Alignment.CenterHorizontally, | ||||||
|             modifier = Modifier.fillMaxWidth() |             modifier = Modifier.fillMaxWidth() | ||||||
|         ) { |         ) { | ||||||
|             TimerType.values().forEach { timerType -> |             TimerType.values().forEach { timerType -> | ||||||
|                 Button(onClick = { viewModel.onTimerTypeChosen(defaultTimerInfo[timerType]!!, open) }) { |                 val default: TimerInfo = defaultTimerInfo.getValue(timerType) | ||||||
|  |                 Button(onClick = { viewModel.onTimerTypeChosen(default, open) }) { | ||||||
|                     Text(text = timerType.name) |                     Text(text = timerType.name) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -1,25 +1,22 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_add | package be.ugent.sel.studeez.screens.timer_form.timer_type_select | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.EditTimerState | import be.ugent.sel.studeez.data.EditTimerState | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.domain.TimerDAO |  | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import kotlinx.coroutines.flow.Flow |  | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | @HiltViewModel | ||||||
| class TimerTypeSelectViewModel @Inject constructor( | class TimerTypeSelectViewModel @Inject constructor( | ||||||
|     private val editTimerState: EditTimerState, |     private val editTimerState: EditTimerState, | ||||||
|     private val timerDAO: TimerDAO, |  | ||||||
|     logService: LogService |     logService: LogService | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     fun onTimerTypeChosen(timerInfo: TimerInfo, open: (String) -> Unit) { |     fun onTimerTypeChosen(timerInfo: TimerInfo, open: (String) -> Unit) { | ||||||
|         editTimerState.timerInfo = timerInfo |         editTimerState.timerInfo = timerInfo | ||||||
|         open(StudeezDestinations.TIMER_EDIT_SCREEN) |         open(StudeezDestinations.ADD_TIMER_SCREEN) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -25,7 +25,7 @@ data class TimerOverviewActions( | ||||||
|     val getUserTimers: () -> Flow<List<TimerInfo>>, |     val getUserTimers: () -> Flow<List<TimerInfo>>, | ||||||
|     val getDefaultTimers: () -> List<TimerInfo>, |     val getDefaultTimers: () -> List<TimerInfo>, | ||||||
|     val onEditClick: (TimerInfo) -> Unit, |     val onEditClick: (TimerInfo) -> Unit, | ||||||
|     val onAddClick: () -> Unit, |     val onAddClick: () -> Unit | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| fun getTimerOverviewActions( | fun getTimerOverviewActions( | ||||||
|  | @ -36,7 +36,7 @@ fun getTimerOverviewActions( | ||||||
|         getUserTimers = viewModel::getUserTimers, |         getUserTimers = viewModel::getUserTimers, | ||||||
|         getDefaultTimers = viewModel::getDefaultTimers, |         getDefaultTimers = viewModel::getDefaultTimers, | ||||||
|         onEditClick = { viewModel.update(it, open) }, |         onEditClick = { viewModel.update(it, open) }, | ||||||
|         onAddClick = { viewModel.create(open) } |         onAddClick = { viewModel.onAddClick(open) } | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -48,14 +48,14 @@ fun TimerOverviewRoute( | ||||||
| ) { | ) { | ||||||
|     TimerOverviewScreen( |     TimerOverviewScreen( | ||||||
|         timerOverviewActions = getTimerOverviewActions(viewModel, open), |         timerOverviewActions = getTimerOverviewActions(viewModel, open), | ||||||
|         drawerActions = drawerActions |         drawerActions = drawerActions, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun TimerOverviewScreen( | fun TimerOverviewScreen( | ||||||
|     timerOverviewActions: TimerOverviewActions, |     timerOverviewActions: TimerOverviewActions, | ||||||
|     drawerActions: DrawerActions |     drawerActions: DrawerActions, | ||||||
| ) { | ) { | ||||||
| 
 | 
 | ||||||
|     val timers = timerOverviewActions.getUserTimers().collectAsState(initial = emptyList()) |     val timers = timerOverviewActions.getUserTimers().collectAsState(initial = emptyList()) | ||||||
|  |  | ||||||
|  | @ -32,8 +32,8 @@ class TimerOverviewViewModel @Inject constructor( | ||||||
|         open(StudeezDestinations.TIMER_EDIT_SCREEN) |         open(StudeezDestinations.TIMER_EDIT_SCREEN) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun create(open: (String) -> Unit) { |     fun onAddClick(open: (String) -> Unit) { | ||||||
|         open(StudeezDestinations.ADD_TIMER_SCREEN) |         open(StudeezDestinations.TIMER_TYPE_CHOOSING_SCREEN) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) |     fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) | ||||||
|  |  | ||||||
|  | @ -1,12 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_overview.add_timer |  | ||||||
| 
 |  | ||||||
| data class AddTimerUiState( |  | ||||||
|     val studyTimeHours: Int = 1, |  | ||||||
|     val studyTimeMinutes: Int = 0, |  | ||||||
|     val withBreaks: Boolean = false, |  | ||||||
|     val breakTimeMinutes: Int = 5, |  | ||||||
|     val breakTimeHours: Int = 0, |  | ||||||
|     val repeats: Int = 1, |  | ||||||
|     val name: String = "Timer", |  | ||||||
|     val description: String = "Long study session", |  | ||||||
| ) |  | ||||||
|  | @ -1,91 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_overview.add_timer |  | ||||||
| 
 |  | ||||||
| import androidx.compose.runtime.mutableStateOf |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo |  | ||||||
| import be.ugent.sel.studeez.domain.LogService |  | ||||||
| import be.ugent.sel.studeez.domain.TimerDAO |  | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel |  | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel |  | ||||||
| import javax.inject.Inject |  | ||||||
| 
 |  | ||||||
| @HiltViewModel |  | ||||||
| class AddTimerViewModel @Inject constructor( |  | ||||||
|     logService: LogService, |  | ||||||
|     private val timerDAO: TimerDAO, |  | ||||||
| ): StudeezViewModel(logService) { |  | ||||||
|     var uiState = mutableStateOf(AddTimerUiState()) |  | ||||||
|         private set |  | ||||||
| 
 |  | ||||||
|     private val studyTimeHours |  | ||||||
|         get() = uiState.value.studyTimeHours |  | ||||||
| 
 |  | ||||||
|     private val studyTimeMinutes |  | ||||||
|         get() = uiState.value.studyTimeMinutes |  | ||||||
| 
 |  | ||||||
|     private val breakTimeHours |  | ||||||
|         get() = uiState.value.breakTimeHours |  | ||||||
| 
 |  | ||||||
|     private val breakTimeMinutes |  | ||||||
|         get() = uiState.value.breakTimeMinutes |  | ||||||
| 
 |  | ||||||
|     private val repeats |  | ||||||
|         get() = uiState.value.repeats |  | ||||||
| 
 |  | ||||||
|     private val name |  | ||||||
|         get() = uiState.value.name |  | ||||||
| 
 |  | ||||||
|     private val description |  | ||||||
|         get() = uiState.value.description |  | ||||||
| 
 |  | ||||||
|     fun onStudyTimeHoursChange(newValue: Int) { |  | ||||||
|         uiState.value = uiState.value.copy(studyTimeHours = newValue) |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onStudyTimeMinutesChange(newValue: Int) { |  | ||||||
|         uiState.value = uiState.value.copy(studyTimeMinutes = newValue) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onWithBreaksChange() { |  | ||||||
|         uiState.value = uiState.value.copy(withBreaks = !uiState.value.withBreaks) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onBreakTimeHourChange(newValue: Int) { |  | ||||||
|         uiState.value = uiState.value.copy(breakTimeHours = newValue) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onBreakTimeMinutesChange(newValue: Int) { |  | ||||||
|         uiState.value = uiState.value.copy(breakTimeMinutes = newValue) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onRepeatsChange(newValue: Int) { |  | ||||||
|         uiState.value = uiState.value.copy(repeats = newValue) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun addTimer() { |  | ||||||
|         if (uiState.value.withBreaks) { |  | ||||||
|             timerDAO.saveTimer(PomodoroTimerInfo( |  | ||||||
|                 name = uiState.value.name, |  | ||||||
|                 description = uiState.value.description, |  | ||||||
|                 studyTime = studyTimeHours * 60 * 60 + studyTimeMinutes * 60, |  | ||||||
|                 breakTime = breakTimeHours * 60 * 60 + breakTimeMinutes * 60, |  | ||||||
|                 repeats = repeats |  | ||||||
|             )) |  | ||||||
|         } else { |  | ||||||
|             timerDAO.saveTimer(CustomTimerInfo( |  | ||||||
|                 name = uiState.value.name, |  | ||||||
|                 description = uiState.value.description, |  | ||||||
|                 studyTime = studyTimeHours * 60 * 60 + studyTimeMinutes * 60 |  | ||||||
|             )) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onNameChange(newValue: String) { |  | ||||||
|         uiState.value = uiState.value.copy(name = newValue) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun onDescriptionChange(newValue: String) { |  | ||||||
|         uiState.value = uiState.value.copy(description = newValue) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,274 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_overview.add_timer |  | ||||||
| 
 |  | ||||||
| import androidx.compose.foundation.layout.Arrangement |  | ||||||
| import androidx.compose.foundation.layout.Row |  | ||||||
| import androidx.compose.foundation.layout.fillMaxHeight |  | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth |  | ||||||
| import androidx.compose.foundation.layout.padding |  | ||||||
| import androidx.compose.foundation.lazy.LazyColumn |  | ||||||
| import androidx.compose.material.Button |  | ||||||
| import androidx.compose.material.Checkbox |  | ||||||
| import androidx.compose.material.Text |  | ||||||
| import androidx.compose.material.TextField |  | ||||||
| import androidx.compose.runtime.Composable |  | ||||||
| import androidx.compose.runtime.getValue |  | ||||||
| import androidx.compose.ui.Alignment |  | ||||||
| import androidx.compose.ui.Modifier |  | ||||||
| import androidx.compose.ui.graphics.Color |  | ||||||
| import androidx.compose.ui.res.stringResource |  | ||||||
| import androidx.compose.ui.text.style.TextAlign |  | ||||||
| import androidx.compose.ui.tooling.preview.Preview |  | ||||||
| import androidx.compose.ui.unit.dp |  | ||||||
| import be.ugent.sel.studeez.R |  | ||||||
| import be.ugent.sel.studeez.common.composable.BasicButton |  | ||||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate |  | ||||||
| import be.ugent.sel.studeez.common.composable.navbar.BasicTimePicker |  | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations |  | ||||||
| import be.ugent.sel.studeez.resources |  | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme |  | ||||||
| 
 |  | ||||||
| data class AddTimerActions( |  | ||||||
|     val open: (String) -> Unit, |  | ||||||
|     val goBack: () -> Unit, |  | ||||||
|     val onStudyTimeHoursChange: (Int) -> Unit, |  | ||||||
|     val onStudyTimeMinutesChange: (Int) -> Unit, |  | ||||||
|     val onBreakTimeHourChange: (Int) -> Unit, |  | ||||||
|     val onBreakTimeMinutesChange: (Int) -> Unit, |  | ||||||
|     val onRepeatsChange: (Int) -> Unit, |  | ||||||
|     val onWithBreaksChange: () -> Unit, |  | ||||||
|     val addTimer: () -> Unit, |  | ||||||
|     val onNameChange: (String) -> Unit, |  | ||||||
|     val onDescriptionChange: (String) -> Unit, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| fun getAddTimerActions( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     goBack: () -> Unit, |  | ||||||
|     viewModel: AddTimerViewModel, |  | ||||||
| ): AddTimerActions { |  | ||||||
|     return AddTimerActions( |  | ||||||
|         open = open, |  | ||||||
|         goBack = goBack, |  | ||||||
|         onWithBreaksChange = viewModel::onWithBreaksChange, |  | ||||||
|         onStudyTimeHoursChange = viewModel::onStudyTimeHoursChange, |  | ||||||
|         onStudyTimeMinutesChange = viewModel::onStudyTimeMinutesChange, |  | ||||||
|         onBreakTimeHourChange = viewModel::onBreakTimeHourChange, |  | ||||||
|         onBreakTimeMinutesChange = viewModel::onBreakTimeMinutesChange, |  | ||||||
|         onRepeatsChange = viewModel::onRepeatsChange, |  | ||||||
|         addTimer = viewModel::addTimer, |  | ||||||
|         onNameChange = viewModel::onNameChange, |  | ||||||
|         onDescriptionChange = viewModel::onDescriptionChange |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun AddTimerRoute( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     goBack: () -> Unit, |  | ||||||
|     viewModel: AddTimerViewModel, |  | ||||||
| ) { |  | ||||||
|     val uiState by viewModel.uiState |  | ||||||
| 
 |  | ||||||
|     AddTimerScreen( |  | ||||||
|         addTimerActions = getAddTimerActions( |  | ||||||
|             open = open, |  | ||||||
|             goBack = goBack, |  | ||||||
|             viewModel = viewModel, |  | ||||||
|         ), |  | ||||||
|         uiState = uiState |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun AddTimerScreen( |  | ||||||
|     addTimerActions: AddTimerActions, |  | ||||||
|     uiState: AddTimerUiState, |  | ||||||
| ) { |  | ||||||
|     val mStudyTimePicker = BasicTimePicker( |  | ||||||
|         onHoursChange = addTimerActions.onStudyTimeHoursChange, |  | ||||||
|         onMinutesChange = addTimerActions.onStudyTimeMinutesChange, |  | ||||||
|         Hours = uiState.studyTimeHours, |  | ||||||
|         Minutes = uiState.studyTimeMinutes |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     val mBreakTimePicker = BasicTimePicker( |  | ||||||
|         onHoursChange = addTimerActions.onBreakTimeHourChange, |  | ||||||
|         onMinutesChange = addTimerActions.onBreakTimeMinutesChange, |  | ||||||
|         Hours = uiState.breakTimeHours, |  | ||||||
|         Minutes = uiState.breakTimeMinutes |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     SecondaryScreenTemplate( |  | ||||||
|         title = resources().getString(R.string.add_timer), |  | ||||||
|         popUp = addTimerActions.goBack |  | ||||||
|     ) { |  | ||||||
|         LazyColumn( |  | ||||||
|             modifier = Modifier |  | ||||||
|                 .fillMaxWidth() |  | ||||||
|                 .padding(16.dp), |  | ||||||
|             horizontalAlignment = Alignment.CenterHorizontally |  | ||||||
|         ) { |  | ||||||
|             item { |  | ||||||
|                 Row( |  | ||||||
|                     modifier = Modifier |  | ||||||
|                         .padding(16.dp) |  | ||||||
|                 ) { |  | ||||||
|                     Text( |  | ||||||
|                         text = stringResource(R.string.addTimer_question), |  | ||||||
|                         textAlign = TextAlign.Center |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 Text( |  | ||||||
|                     text = uiState.studyTimeHours.toString() + stringResource(R.string.addTimer_studytime_1) + uiState.studyTimeMinutes + stringResource( |  | ||||||
|                                             R.string.addTimer_studytime_2) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 Button( |  | ||||||
|                     onClick = { |  | ||||||
|                         mStudyTimePicker.show() |  | ||||||
|                     }, |  | ||||||
|                 ) { |  | ||||||
|                     Text( |  | ||||||
|                         text = stringResource(R.string.addTimer_timepicker), |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 Row( |  | ||||||
|                     modifier = Modifier.fillMaxWidth(), |  | ||||||
|                     verticalAlignment = Alignment.CenterVertically, |  | ||||||
|                     horizontalArrangement = Arrangement.Center |  | ||||||
|                 ) { |  | ||||||
|                     Text( |  | ||||||
|                         text = stringResource(R.string.addTimer_break_question), |  | ||||||
|                     ) |  | ||||||
|                     Checkbox( |  | ||||||
|                         checked = uiState.withBreaks, |  | ||||||
|                         onCheckedChange = { addTimerActions.onWithBreaksChange() } |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (uiState.withBreaks) { |  | ||||||
|                 item { |  | ||||||
|                     Text( |  | ||||||
|                         text = if (uiState.repeats == 1) uiState.repeats.toString() + stringResource( |  | ||||||
|                                                     R.string.addTimer_break_1) |  | ||||||
|                                else uiState.repeats.toString() + stringResource( |  | ||||||
|                                                     R.string.addTimer_break_s) |  | ||||||
|                     ) |  | ||||||
|                     TextField( |  | ||||||
|                         value = uiState.repeats.toString(), |  | ||||||
|                         onValueChange = { it: String -> |  | ||||||
|                             it.toIntOrNull()?.let { it1 -> |  | ||||||
|                                 addTimerActions.onRepeatsChange( |  | ||||||
|                                     kotlin.math.abs(it1) |  | ||||||
|                                 ) |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 item { |  | ||||||
|                     Text( |  | ||||||
|                         text = uiState.breakTimeHours.toString() + stringResource(R.string.breakTime_1) + uiState.breakTimeMinutes + stringResource( |  | ||||||
|                                                     R.string.breakTime_2) |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 item { |  | ||||||
|                     Button( |  | ||||||
|                         onClick = { |  | ||||||
|                             mBreakTimePicker.show() |  | ||||||
|                         }, |  | ||||||
|                     ) { |  | ||||||
|                         Text( |  | ||||||
|                             text = stringResource(R.string.addTimer_timepicker), |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 Text( |  | ||||||
|                     text = stringResource(R.string.addTimer_name) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 TextField( |  | ||||||
|                     value = uiState.name, |  | ||||||
|                     onValueChange = { addTimerActions.onNameChange(it) } |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 if (uiState.name == "") { |  | ||||||
|                     Text( |  | ||||||
|                         text = stringResource(R.string.addTimer_name_error), |  | ||||||
|                         color = Color.Red |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 Text( |  | ||||||
|                     text = stringResource(R.string.addTimer_description) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 TextField( |  | ||||||
|                     value = uiState.description, |  | ||||||
|                     onValueChange = { addTimerActions.onDescriptionChange(it) } |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             item { |  | ||||||
|                 if (uiState.description == "") { |  | ||||||
|                     Text( |  | ||||||
|                         text = stringResource(R.string.addTimer_description_error), |  | ||||||
|                         color = Color.Red |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             item { |  | ||||||
|                 Row( |  | ||||||
|                     modifier = Modifier |  | ||||||
|                         .fillMaxWidth() |  | ||||||
|                         .fillMaxHeight(), |  | ||||||
|                     verticalAlignment = Alignment.Bottom, |  | ||||||
|                     horizontalArrangement = Arrangement.Center |  | ||||||
|                 ) { |  | ||||||
|                     BasicButton( |  | ||||||
|                         text = R.string.add_timer, |  | ||||||
|                         modifier = Modifier, |  | ||||||
|                         onClick = { |  | ||||||
|                             if (uiState.description != "" && uiState.name != "") { |  | ||||||
|                                 addTimerActions.addTimer() |  | ||||||
|                                 addTimerActions.open(StudeezDestinations.TIMER_SCREEN) |  | ||||||
|                             } |  | ||||||
|                          } |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun AddTimerScreenPreview() { StudeezTheme { |  | ||||||
|     AddTimerScreen( |  | ||||||
|         addTimerActions = AddTimerActions({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}), |  | ||||||
|         uiState = AddTimerUiState() |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -131,8 +131,13 @@ | ||||||
|     <string name="addTimer_studytime_2">" minutes of studytime"</string> |     <string name="addTimer_studytime_2">" minutes of studytime"</string> | ||||||
|     <string name="addTimer_question">How long do you want to study?</string> |     <string name="addTimer_question">How long do you want to study?</string> | ||||||
| 
 | 
 | ||||||
|  |     <!-- Timer Type Select --> | ||||||
|  |     <string name="timer_type_select">Select Timer Type</string> | ||||||
|  | 
 | ||||||
|     <!-- Edit Timer--> |     <!-- Edit Timer--> | ||||||
|     <string name="name">Name</string> |     <string name="name">Name</string> | ||||||
|  |     <string name="edit_timer">Edit Timer</string> | ||||||
|  |     <string name="repeats_error">Repeats must be a positive non-zero number</string> | ||||||
|     <string name="description">Description</string> |     <string name="description">Description</string> | ||||||
|     <string name="studyTime">Study Time</string> |     <string name="studyTime">Study Time</string> | ||||||
|     <string name="breakTime">Break Time</string> |     <string name="breakTime">Break Time</string> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 lbarraga
						lbarraga