commit
						d3159e0b2a
					
				
					 29 changed files with 583 additions and 110 deletions
				
			
		|  | @ -32,6 +32,7 @@ import be.ugent.sel.studeez.screens.sessions.SessionsRoute | |||
| import be.ugent.sel.studeez.screens.settings.SettingsRoute | ||||
| import be.ugent.sel.studeez.screens.sign_up.SignUpRoute | ||||
| import be.ugent.sel.studeez.screens.splash.SplashRoute | ||||
| import be.ugent.sel.studeez.screens.timer_edit.TimerEditRoute | ||||
| 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 | ||||
|  | @ -205,6 +206,14 @@ fun StudeezNavGraph( | |||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         composable(StudeezDestinations.TIMER_EDIT_SCREEN) { | ||||
|             TimerEditRoute( | ||||
|                 open = open, | ||||
|                 popUp = goBack, | ||||
|                 viewModel = hiltViewModel() | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         // Friends flow | ||||
|         composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { | ||||
|             // TODO | ||||
|  |  | |||
|  | @ -3,46 +3,113 @@ package be.ugent.sel.studeez.common.composable | |||
| import android.app.TimePickerDialog | ||||
| import android.app.TimePickerDialog.OnTimeSetListener | ||||
| import android.content.Context | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.foundation.BorderStroke | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.ButtonColors | ||||
| import androidx.compose.material.ButtonDefaults | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.* | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.MutableState | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| 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.ext.fieldModifier | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | ||||
| import java.util.* | ||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||
| 
 | ||||
| @Composable | ||||
| fun TimePickerCard( | ||||
|     @StringRes text: Int, | ||||
|     initialSeconds: Int, | ||||
|     onTimeChosen: (Int) -> Unit | ||||
| ) { | ||||
|     Card( | ||||
|         modifier = Modifier | ||||
|             .fillMaxWidth() | ||||
|             .fieldModifier(), | ||||
|         elevation = 10.dp | ||||
|     ) { | ||||
|         Row( | ||||
|             modifier = Modifier | ||||
|                 .fillMaxWidth() | ||||
|                 .fieldModifier(), | ||||
|             horizontalArrangement = Arrangement.SpaceBetween, | ||||
|             verticalAlignment = Alignment.CenterVertically | ||||
|         ) { | ||||
|             Text( | ||||
|                 text = stringResource(id = text), | ||||
|                 fontWeight = FontWeight.Medium | ||||
|             ) | ||||
| 
 | ||||
|             TimePickerButton( | ||||
|                 initialSeconds = initialSeconds, | ||||
|                 onTimeChosen = onTimeChosen | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun TimePickerButton( | ||||
|     hoursMinutesSeconds: HoursMinutesSeconds, | ||||
|     initialSeconds: Int, | ||||
|     modifier: Modifier = Modifier, | ||||
|     colors: ButtonColors = ButtonDefaults.buttonColors(), | ||||
|     border: BorderStroke? = null, | ||||
|     onTimeSetListener: OnTimeSetListener | ||||
|     onTimeChosen: (Int) -> Unit | ||||
| ) { | ||||
|     val context = LocalContext.current | ||||
|     val timeState: MutableState<Int> = remember { | ||||
|         mutableStateOf(initialSeconds) | ||||
|     } | ||||
| 
 | ||||
|     Button( | ||||
|         onClick = { pickDuration(context, onTimeSetListener) }, | ||||
|         onClick = { pickDuration(context, onTimeChosen, timeState) }, | ||||
|         modifier = modifier, | ||||
|         shape = RoundedCornerShape(20.dp), | ||||
|         colors = colors, | ||||
|         border = border | ||||
|     ) { | ||||
|         Text(text = hoursMinutesSeconds.toString()) | ||||
|         Text(text = HoursMinutesSeconds(timeState.value).toString()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| private fun pickDuration(context: Context, listener: OnTimeSetListener) { | ||||
|     val timePickerDialog = TimePickerDialog( | ||||
| private fun pickDuration(context: Context, onTimeChosen: (Int) -> Unit, timeState: MutableState<Int>) { | ||||
|     val listener = OnTimeSetListener { _, hour, minute -> | ||||
|         timeState.value = HoursMinutesSeconds(hour, minute, 0).getTotalSeconds() | ||||
|         onTimeChosen(timeState.value) | ||||
|     } | ||||
|     val hms = HoursMinutesSeconds(timeState.value) | ||||
|     val mTimePickerDialog = TimePickerDialog( | ||||
|         context, | ||||
|         listener, | ||||
|         0, | ||||
|         0, | ||||
|         hms.hours, | ||||
|         hms.minutes, | ||||
|         true | ||||
|     ) | ||||
|     timePickerDialog.show() | ||||
|     mTimePickerDialog.show() | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun TimePickerButtonPreview() { | ||||
|     StudeezTheme { | ||||
|         TimePickerButton(initialSeconds = 5 * 60 + 12, onTimeChosen = {}) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun TimePickerCardPreview() { | ||||
|     StudeezTheme { | ||||
|         TimePickerCard(text = R.string.studyTime, initialSeconds = 5 * 60 + 12, onTimeChosen = {}) | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +1,11 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| class EditTimerState { | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| @Singleton | ||||
| class EditTimerState @Inject constructor(){ | ||||
|     lateinit var timerInfo: TimerInfo | ||||
| } | ||||
|  | @ -4,7 +4,7 @@ class FunctionalCustomTimer(studyTime: Int) : FunctionalTimer(studyTime) { | |||
| 
 | ||||
|     override fun tick() { | ||||
|         if (!hasEnded()) { | ||||
|             time.minOne() | ||||
|             time-- | ||||
|             totalStudyTime++ | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ class FunctionalEndlessTimer : FunctionalTimer(0) { | |||
|     } | ||||
| 
 | ||||
|     override fun tick() { | ||||
|         time.plusOne() | ||||
|         time++ | ||||
|         totalStudyTime++ | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ class FunctionalPomodoroTimer( | |||
|             } | ||||
|             isInBreak = !isInBreak | ||||
|         } | ||||
|         time.minOne() | ||||
|         time-- | ||||
| 
 | ||||
|         if (!isInBreak) { | ||||
|             totalStudyTime++ | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import be.ugent.sel.studeez.data.local.models.SessionReport | |||
| import com.google.firebase.Timestamp | ||||
| 
 | ||||
| abstract class FunctionalTimer(initialValue: Int) { | ||||
|     val time: Time = Time(initialValue) | ||||
|     var time: Time = Time(initialValue) | ||||
|     var totalStudyTime: Int = 0 | ||||
| 
 | ||||
|     fun getHoursMinutesSeconds(): HoursMinutesSeconds { | ||||
|  |  | |||
|  | @ -2,6 +2,12 @@ package be.ugent.sel.studeez.data.local.models.timer_functional | |||
| 
 | ||||
| data class HoursMinutesSeconds(val hours: Int, val minutes: Int, val seconds: Int) { | ||||
| 
 | ||||
|     constructor(sec: Int): this( | ||||
|         sec / (60 * 60), | ||||
|         (sec / (60)) % 60, | ||||
|         sec % 60 | ||||
|     ) | ||||
| 
 | ||||
|     fun getTotalSeconds(): Int { | ||||
|         return hours * 60 * 60 + minutes * 60 + seconds | ||||
|     } | ||||
|  |  | |||
|  | @ -1,23 +1,13 @@ | |||
| package be.ugent.sel.studeez.data.local.models.timer_functional | ||||
| 
 | ||||
| class Time(initialTime: Int) { | ||||
| class Time(var time: Int) { | ||||
|     operator fun invoke() = time | ||||
| 
 | ||||
|     var time = initialTime | ||||
|     operator fun inc(): Time = Time(time + 1) | ||||
| 
 | ||||
|     fun minOne() { | ||||
|         time-- | ||||
|     } | ||||
| 
 | ||||
|     fun plusOne() { | ||||
|         time++ | ||||
|     } | ||||
|     operator fun dec(): Time = Time(time - 1) | ||||
| 
 | ||||
|     fun getAsHMS(): HoursMinutesSeconds { | ||||
|         val hours: Int = time / (60 * 60) | ||||
|         val minutes: Int = (time / (60)) % 60 | ||||
|         val seconds: Int = time % 60 | ||||
| 
 | ||||
|         return HoursMinutesSeconds(hours, minutes, seconds) | ||||
|         return HoursMinutesSeconds(time) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -7,8 +7,8 @@ import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVi | |||
| class PomodoroTimerInfo( | ||||
|     name: String, | ||||
|     description: String, | ||||
|     val studyTime: Int, | ||||
|     val breakTime: Int, | ||||
|     var studyTime: Int, | ||||
|     var breakTime: Int, | ||||
|     val repeats: Int, | ||||
|     id: String = "" | ||||
| ):  TimerInfo(id, name, description) { | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ object StudeezDestinations { | |||
| 
 | ||||
|     // Studying flow | ||||
|     const val TIMER_SELECTION_SCREEN = "timer_selection" | ||||
|     const val TIMER_EDIT_SCREEN = "timer_edit" | ||||
|     const val SESSION_SCREEN = "session" | ||||
|     const val SESSION_RECAP = "session_recap" | ||||
| 
 | ||||
|  |  | |||
|  | @ -78,7 +78,7 @@ abstract class AbstractSessionScreen { | |||
|         val hms = sessionActions.getTimer().getHoursMinutesSeconds() | ||||
|         Column { | ||||
|             Text( | ||||
|                 text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}", | ||||
|                 text = hms.toString(), | ||||
|                 modifier = Modifier | ||||
|                     .fillMaxWidth() | ||||
|                     .padding(50.dp), | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi | |||
|     Column( | ||||
|         modifier = modifier | ||||
|     ) { | ||||
|         Text(text = "You studied: ${hms.hours} : ${hms.minutes} : ${hms.seconds}") | ||||
|         Text(text = "You studied: $hms") | ||||
| 
 | ||||
|         BasicButton( | ||||
|             R.string.save, Modifier.basicButton() | ||||
|  |  | |||
|  | @ -0,0 +1,44 @@ | |||
| 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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,29 @@ | |||
| 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() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,43 @@ | |||
| package be.ugent.sel.studeez.screens.timer_add | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.hilt.navigation.compose.hiltViewModel | ||||
| 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.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.ENDLESS | ||||
| 
 | ||||
| val defaultTimerInfo: Map<TimerType, TimerInfo> = mapOf( | ||||
|     CUSTOM to CustomTimerInfo("", "", 0), | ||||
|     BREAK to PomodoroTimerInfo("", "", 0, 0, 0), | ||||
|     ENDLESS to EndlessTimerInfo("", ""), | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| @Composable | ||||
| fun TimerTypeSelectScreen( | ||||
|     open: (String) -> Unit, | ||||
|     popUp: () -> Unit, | ||||
|     viewModel: TimerTypeSelectViewModel = hiltViewModel() | ||||
| ) { | ||||
| 
 | ||||
|     SecondaryScreenTemplate(title = "Edit Timer", popUp = popUp) { | ||||
|         Column( | ||||
|             horizontalAlignment = Alignment.CenterHorizontally, | ||||
|             modifier = Modifier.fillMaxWidth() | ||||
|         ) { | ||||
|             TimerType.values().forEach { timerType -> | ||||
|                 Button(onClick = { viewModel.onTimerTypeChosen(defaultTimerInfo[timerType]!!, open) }) { | ||||
|                     Text(text = timerType.name) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,25 @@ | |||
| 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.navigation.StudeezDestinations | ||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| @HiltViewModel | ||||
| class TimerTypeSelectViewModel @Inject constructor( | ||||
|     private val editTimerState: EditTimerState, | ||||
|     private val timerDAO: TimerDAO, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
| 
 | ||||
|     fun onTimerTypeChosen(timerInfo: TimerInfo, open: (String) -> Unit) { | ||||
|         editTimerState.timerInfo = timerInfo | ||||
|         open(StudeezDestinations.TIMER_EDIT_SCREEN) | ||||
|     } | ||||
| } | ||||
|  | @ -1,34 +1,26 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit | ||||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.BasicButton | ||||
| import be.ugent.sel.studeez.common.ext.basicButton | ||||
| 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 GetTimerEditView: TimerInfoVisitor<Unit> { | ||||
| 
 | ||||
|     @SuppressLint("ComposableNaming") | ||||
|     override fun visitCustomTimerInfo(customTimerInfo: CustomTimerInfo) { | ||||
| class GetTimerEditScreen: TimerInfoVisitor<AbstractTimerEditScreen> { | ||||
| 
 | ||||
|     override fun visitCustomTimerInfo(customTimerInfo: CustomTimerInfo): AbstractTimerEditScreen { | ||||
|         return CustomTimerEditScreen(customTimerInfo) | ||||
|     } | ||||
| 
 | ||||
|     @SuppressLint("ComposableNaming") | ||||
|     override fun visitEndlessTimerInfo(endlessTimerInfo: EndlessTimerInfo) { | ||||
| 
 | ||||
|     override fun visitEndlessTimerInfo(endlessTimerInfo: EndlessTimerInfo): AbstractTimerEditScreen { | ||||
|         return EndlessTimerEditScreen(endlessTimerInfo) | ||||
|     } | ||||
| 
 | ||||
|     @SuppressLint("ComposableNaming") | ||||
|     override fun visitBreakTimerInfo(pomodoroTimerInfo: PomodoroTimerInfo) { | ||||
| 
 | ||||
|     override fun visitBreakTimerInfo(pomodoroTimerInfo: PomodoroTimerInfo): AbstractTimerEditScreen { | ||||
|         return BreakTimerEditScreen(pomodoroTimerInfo) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,2 +1,65 @@ | |||
| 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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,29 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit | ||||
| 
 | ||||
| class TimerEditViewModel { | ||||
| 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 TimerEditViewModel @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,4 +1,70 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit | ||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.fillMaxHeight | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.BasicButton | ||||
| import be.ugent.sel.studeez.common.composable.LabelledInputField | ||||
| import be.ugent.sel.studeez.common.ext.basicButton | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| 
 | ||||
| abstract class AbstractTimerEditScreen(private val timerInfo: TimerInfo) { | ||||
| 
 | ||||
|     @Composable | ||||
|     operator fun invoke(onSaveClick: (TimerInfo) -> Unit) { | ||||
| 
 | ||||
|         var name by remember { mutableStateOf(timerInfo.name) } | ||||
|         var description by remember { mutableStateOf(timerInfo.description) } | ||||
| 
 | ||||
|         // This shall rerun whenever name and description change | ||||
|         timerInfo.name = name | ||||
|         timerInfo.description = description | ||||
| 
 | ||||
|         Column( | ||||
|             verticalArrangement = Arrangement.SpaceBetween, | ||||
|             modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()), | ||||
|         ) { | ||||
|             Column( | ||||
|                 modifier = Modifier.fillMaxWidth(), | ||||
|                 horizontalAlignment = Alignment.CenterHorizontally | ||||
|             ) { | ||||
| 
 | ||||
|                 // Fields that every timer shares (ommited id) | ||||
|                 LabelledInputField( | ||||
|                     value = name, | ||||
|                     onNewValue = { name = it }, | ||||
|                     label = R.string.name | ||||
|                 ) | ||||
| 
 | ||||
|                 repeat(20) { | ||||
|                     LabelledInputField( | ||||
|                         value = description, | ||||
|                         onNewValue = { description = it }, | ||||
|                         label = R.string.description, | ||||
|                         singleLine = false | ||||
|                     ) | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|                 ExtraFields() | ||||
| 
 | ||||
|             } | ||||
|             BasicButton(R.string.save, Modifier.basicButton()) { | ||||
|                 onSaveClick(timerInfo) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Composable | ||||
|     open fun ExtraFields() { | ||||
|         // By default no extra fields, unless overwritten by subclass. | ||||
|     } | ||||
| 
 | ||||
| abstract class AbstractTimerEditScreen { | ||||
| } | ||||
|  | @ -1,4 +1,46 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | ||||
| 
 | ||||
| class BreakTimerEditScreen { | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.TimePickerButton | ||||
| 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.ui.theme.StudeezTheme | ||||
| 
 | ||||
| class BreakTimerEditScreen( | ||||
|     private val breakTimerInfo: PomodoroTimerInfo | ||||
| ): AbstractTimerEditScreen(breakTimerInfo) { | ||||
| 
 | ||||
|     @Composable | ||||
|     override fun ExtraFields() { | ||||
|         // If the user presses the OK button on the timepicker, the time in the button should change | ||||
| 
 | ||||
|         TimePickerCard(R.string.studyTime, breakTimerInfo.studyTime) { newTime -> | ||||
|             breakTimerInfo.studyTime = newTime | ||||
|         } | ||||
|         TimePickerCard(R.string.breakTime, breakTimerInfo.breakTime) { newTime -> | ||||
|             breakTimerInfo.breakTime = newTime | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun BreakEditScreenPreview() { | ||||
|     val pomodoroTimerInfo = PomodoroTimerInfo( | ||||
|         "Breaky the Breaktimer", | ||||
|         "Breaky is a breakdancer", | ||||
|         10 * 60, | ||||
|         60, | ||||
|         5 | ||||
|     ) | ||||
|     StudeezTheme { | ||||
|         BreakTimerEditScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +1,34 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit | ||||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | ||||
| 
 | ||||
| class CustomTimerEditScreen { | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import be.ugent.sel.studeez.common.composable.TimePickerCard | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo | ||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
| class CustomTimerEditScreen( | ||||
|     private val customTimerInfo: CustomTimerInfo | ||||
|     ): AbstractTimerEditScreen(customTimerInfo) { | ||||
| 
 | ||||
|     @Composable | ||||
|     override fun ExtraFields() { | ||||
|         TimePickerCard( | ||||
|             text = AppText.studyTime, | ||||
|             initialSeconds = customTimerInfo.studyTime | ||||
|         ) { newTime -> | ||||
|             customTimerInfo.studyTime = newTime | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun CustomEditScreenPreview() { | ||||
|     val customTimerInfo = CustomTimerInfo("custom", "my description", 25) | ||||
|     StudeezTheme { | ||||
|         CustomTimerEditScreen(customTimerInfo).invoke(onSaveClick = {}) | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +1,23 @@ | |||
| package be.ugent.sel.studeez.screens.timer_edit.editScreens | ||||
| 
 | ||||
| class EndlessTimerEditScreen { | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo | ||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||
| 
 | ||||
| class EndlessTimerEditScreen( | ||||
|     endlessTimerInfo: EndlessTimerInfo | ||||
| ): AbstractTimerEditScreen(endlessTimerInfo) { | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun EndlessEditScreenPreview() { | ||||
|     val endlessTimerInfo = EndlessTimerInfo( | ||||
|         "Forever and beyond", | ||||
|         "My endless timer description", | ||||
|     ) | ||||
|     StudeezTheme { | ||||
|         EndlessTimerEditScreen(endlessTimerInfo).invoke(onSaveClick = {}) | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.timer_overview | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.runtime.Composable | ||||
|  | @ -24,7 +25,7 @@ data class TimerOverviewActions( | |||
|     val getUserTimers: () -> Flow<List<TimerInfo>>, | ||||
|     val getDefaultTimers: () -> List<TimerInfo>, | ||||
|     val onEditClick: (TimerInfo) -> Unit, | ||||
|     val open: (String) -> Unit, | ||||
|     val onAddClick: () -> Unit, | ||||
| ) | ||||
| 
 | ||||
| fun getTimerOverviewActions( | ||||
|  | @ -34,20 +35,20 @@ fun getTimerOverviewActions( | |||
|     return TimerOverviewActions( | ||||
|         getUserTimers = viewModel::getUserTimers, | ||||
|         getDefaultTimers = viewModel::getDefaultTimers, | ||||
|         onEditClick = { viewModel.update(it) }, | ||||
|         open = open | ||||
|         onEditClick = { viewModel.update(it, open) }, | ||||
|         onAddClick = { viewModel.create(open) } | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun TimerOverviewRoute( | ||||
|     open: (String) -> Unit, | ||||
|     viewModel: TimerOverviewViewModel, | ||||
|     drawerActions: DrawerActions, | ||||
|     open: (String) -> Unit | ||||
| ) { | ||||
|     TimerOverviewScreen( | ||||
|         timerOverviewActions = getTimerOverviewActions(viewModel, open), | ||||
|         drawerActions = drawerActions, | ||||
|         drawerActions = drawerActions | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  | @ -63,34 +64,38 @@ fun TimerOverviewScreen( | |||
|         title = resources().getString(R.string.timers), | ||||
|         drawerActions = drawerActions | ||||
|     ) { | ||||
|         LazyColumn { | ||||
|             // Custom timer, select new duration each time | ||||
|             item { | ||||
|                 TimerEntry(timerInfo = CustomTimerInfo( | ||||
|                     name = resources().getString(R.string.custom_name), | ||||
|                     description = resources().getString(R.string.custom_name), | ||||
|                     studyTime = 0 | ||||
|                 )) | ||||
|             } | ||||
|             // Default Timers, cannot be edited | ||||
|             items(timerOverviewActions.getDefaultTimers()) { | ||||
|                 TimerEntry(timerInfo = it) {} | ||||
|             } | ||||
|             // User timers, can be edited | ||||
|             items(timers.value) { timerInfo -> | ||||
|                 TimerEntry( | ||||
|                     timerInfo = timerInfo, | ||||
|                 ) { | ||||
|                     StealthButton( | ||||
|                         text = R.string.edit, | ||||
|                         onClick = { timerOverviewActions.onEditClick(timerInfo) } | ||||
|                     ) | ||||
|         Column { // TODO knop beneden | ||||
|             LazyColumn { | ||||
|                 // Custom timer, select new duration each time | ||||
|                 item { | ||||
|                     TimerEntry(timerInfo = CustomTimerInfo( | ||||
|                         name = resources().getString(R.string.custom_name), | ||||
|                         description = resources().getString(R.string.custom_name), | ||||
|                         studyTime = 0 | ||||
|                     )) | ||||
|                 } | ||||
|                 // Default Timers, cannot be edited | ||||
|                 items(timerOverviewActions.getDefaultTimers()) { | ||||
|                     TimerEntry(timerInfo = it) {} | ||||
|                 } | ||||
|                 // User timers, can be edited | ||||
|                 items(timers.value) { timerInfo -> | ||||
|                     TimerEntry( | ||||
|                         timerInfo = timerInfo, | ||||
|                     ) { | ||||
|                         StealthButton( | ||||
|                             text = R.string.edit, | ||||
|                             onClick = { timerOverviewActions.onEditClick(timerInfo) } | ||||
|                         ) | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             item { | ||||
|                 BasicButton(R.string.add_timer, Modifier.basicButton()) { | ||||
|                     timerOverviewActions.open(StudeezDestinations.ADD_TIMER_SCREEN) | ||||
|                 // TODO uit lazy column | ||||
|                 item { | ||||
|                     BasicButton(R.string.add_timer, Modifier.basicButton()) { | ||||
|                         timerOverviewActions.onAddClick() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -1,9 +1,11 @@ | |||
| package be.ugent.sel.studeez.screens.timer_overview | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.EditTimerState | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.domain.ConfigurationService | ||||
| 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.screens.StudeezViewModel | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import kotlinx.coroutines.flow.Flow | ||||
|  | @ -13,6 +15,7 @@ import javax.inject.Inject | |||
| class TimerOverviewViewModel @Inject constructor( | ||||
|     private val configurationService: ConfigurationService, | ||||
|     private val timerDAO: TimerDAO, | ||||
|     private val editTimerState: EditTimerState, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|  | @ -24,7 +27,14 @@ class TimerOverviewViewModel @Inject constructor( | |||
|         return configurationService.getDefaultTimers() | ||||
|     } | ||||
| 
 | ||||
|     fun update(timerInfo: TimerInfo) = timerDAO.updateTimer(timerInfo) | ||||
|     fun update(timerInfo: TimerInfo, open: (String) -> Unit)  { | ||||
|         editTimerState.timerInfo = timerInfo | ||||
|         open(StudeezDestinations.TIMER_EDIT_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun create(open: (String) -> Unit) { | ||||
|         open(StudeezDestinations.ADD_TIMER_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) | ||||
| 
 | ||||
|  |  | |||
|  | @ -253,7 +253,7 @@ fun AddTimerScreen( | |||
|                         onClick = { | ||||
|                             if (uiState.description != "" && uiState.name != "") { | ||||
|                                 addTimerActions.addTimer() | ||||
|                                 addTimerActions.open(StudeezDestinations.TIMER_OVERVIEW_SCREEN) | ||||
|                                 addTimerActions.open(StudeezDestinations.TIMER_SCREEN) | ||||
|                             } | ||||
|                          } | ||||
|                     ) | ||||
|  |  | |||
|  | @ -1,11 +1,9 @@ | |||
| package be.ugent.sel.studeez.screens.timer_selection | ||||
| 
 | ||||
| import android.widget.TimePicker | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.collectAsState | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||
|  | @ -23,7 +21,6 @@ import kotlinx.coroutines.flow.flowOf | |||
| data class TimerSelectionActions( | ||||
|     val getAllTimers: () -> Flow<List<TimerInfo>>, | ||||
|     val startSession: (TimerInfo) -> Unit, | ||||
|     val pickDuration: (TimePicker?, Int, Int) -> Unit, | ||||
|     val customTimeStudyTime: Int | ||||
| ) | ||||
| 
 | ||||
|  | @ -34,9 +31,6 @@ fun getTimerSelectionActions( | |||
|     return TimerSelectionActions( | ||||
|         getAllTimers = viewModel::getAllTimers, | ||||
|         startSession = { viewModel.startSession(open, it) }, | ||||
|         pickDuration = { _, hour: Int, minute: Int -> | ||||
|             viewModel.customTimerStudyTime.value = hour * 60 * 60 + minute * 60 | ||||
|         }, | ||||
|         customTimeStudyTime = viewModel.customTimerStudyTime.value | ||||
|     ) | ||||
| } | ||||
|  | @ -105,10 +99,9 @@ fun CustomTimerEntry( | |||
|             ) | ||||
|         }, | ||||
|         rightButton = { | ||||
|             TimePickerButton( | ||||
|                 hoursMinutesSeconds = hms, | ||||
|                 onTimeSetListener = timerSelectionActions.pickDuration | ||||
|             ) | ||||
|             TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime -> | ||||
|                 timerInfo.studyTime = chosenTime | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
|  | @ -117,7 +110,7 @@ fun CustomTimerEntry( | |||
| @Composable | ||||
| fun TimerSelectionPreview() { | ||||
|     TimerSelectionScreen( | ||||
|         timerSelectionActions = TimerSelectionActions({ flowOf() }, {}, { _, _, _ -> {} }, 0), | ||||
|         timerSelectionActions = TimerSelectionActions({ flowOf() }, {}, 0), | ||||
|         popUp = {} | ||||
|     ) | ||||
| } | ||||
|  | @ -107,7 +107,7 @@ | |||
| 
 | ||||
|     <!-- Session --> | ||||
|     <string name="create_session_not_possible_yet">Creating sessions still needs to be implemented. Hang on tight!</string> <!-- TODO Remove this description line once implemented. --> | ||||
|    | ||||
| 
 | ||||
|     <!-- Add Timer --> | ||||
|     <string name="addTimer_description_error">Timer description cannot be empty!</string> | ||||
|     <string name="addTimer_description">Timer description</string> | ||||
|  | @ -123,4 +123,11 @@ | |||
|     <string name="addTimer_studytime_2">" minutes of studytime"</string> | ||||
|     <string name="addTimer_question">How long do you want to study?</string> | ||||
| 
 | ||||
|     <!-- Edit Timer--> | ||||
|     <string name="name">Name</string> | ||||
|     <string name="description">Description</string> | ||||
|     <string name="studyTime">Study Time</string> | ||||
|     <string name="breakTime">Break Time</string> | ||||
|     <string name="repeats">Number of Repeats</string> | ||||
| 
 | ||||
| </resources> | ||||
|  |  | |||
		Reference in a new issue
	
	 lbarraga
						lbarraga