Merge pull request #96 from SELab1/addTimer

Add timer
This commit is contained in:
Tibo De Peuter 2023-05-03 22:47:10 +02:00 committed by GitHub Enterprise
commit 6bcc4ce4c4
13 changed files with 482 additions and 39 deletions

2
.idea/misc.xml generated
View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

@ -33,6 +33,7 @@ import be.ugent.sel.studeez.screens.settings.SettingsRoute
import be.ugent.sel.studeez.screens.sign_up.SignUpRoute import be.ugent.sel.studeez.screens.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashRoute import be.ugent.sel.studeez.screens.splash.SplashRoute
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
@ -140,6 +141,7 @@ fun StudeezNavGraph(
TimerOverviewRoute( TimerOverviewRoute(
viewModel = hiltViewModel(), viewModel = hiltViewModel(),
drawerActions = drawerActions, drawerActions = drawerActions,
open = open
) )
} }
@ -195,6 +197,14 @@ fun StudeezNavGraph(
) )
} }
composable(StudeezDestinations.ADD_TIMER_SCREEN) {
AddTimerRoute(
open = open,
goBack = goBack,
viewModel = hiltViewModel()
)
}
// Friends flow // Friends flow
composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) {
// TODO // TODO

View file

@ -16,7 +16,14 @@ import be.ugent.sel.studeez.common.ext.defaultButtonShape
@Composable @Composable
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) { fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
TextButton(onClick = action, modifier = modifier) { Text(text = stringResource(text)) } TextButton(
onClick = action,
modifier = modifier
) {
Text(
text = stringResource(text)
)
}
} }
@Composable @Composable

View file

@ -0,0 +1,24 @@
package be.ugent.sel.studeez.common.composable.navbar
import android.app.TimePickerDialog
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
@Composable
fun BasicTimePicker(
onHoursChange: (Int) -> Unit,
onMinutesChange: (Int) -> Unit,
Hours: Int,
Minutes: Int,
): TimePickerDialog {
return TimePickerDialog(
LocalContext.current,
{ _, mHour: Int, mMinute: Int ->
onHoursChange(mHour)
onMinutesChange(mMinute)
},
Hours,
Minutes,
true
)
}

View file

@ -28,4 +28,6 @@ object StudeezDestinations {
const val CREATE_TASK_SCREEN = "create_task" const val CREATE_TASK_SCREEN = "create_task"
const val CREATE_SESSION_SCREEN = "create_session" const val CREATE_SESSION_SCREEN = "create_session"
const val EDIT_PROFILE_SCREEN = "edit_profile" const val EDIT_PROFILE_SCREEN = "edit_profile"
const val ADD_TIMER_SCREEN = "add_timer"
} }

View file

@ -47,7 +47,9 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi
val sessionReport: SessionReport = sessionRecapActions.getSessionReport() val sessionReport: SessionReport = sessionRecapActions.getSessionReport()
val studyTime: Int = sessionReport.studyTime val studyTime: Int = sessionReport.studyTime
val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS() val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS()
Column { Column(
modifier = modifier
) {
Text(text = "You studied: ${hms.hours} : ${hms.minutes} : ${hms.seconds}") Text(text = "You studied: ${hms.hours} : ${hms.minutes} : ${hms.seconds}")
BasicButton( BasicButton(

View file

@ -11,7 +11,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SessionRecapViewModel @Inject constructor( class SessionRecapViewModel @Inject constructor(
private val sessionReportState: SessionReportState, sessionReportState: SessionReportState,
private val sessionDAO: SessionDAO, private val sessionDAO: SessionDAO,
logService: LogService logService: LogService
) : StudeezViewModel(logService) { ) : StudeezViewModel(logService) {

View file

@ -1,6 +1,5 @@
package be.ugent.sel.studeez.screens.timer_overview 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.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -16,6 +15,7 @@ import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
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.CustomTimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo
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.navigation.StudeezDestinations
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -24,25 +24,29 @@ 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 open: (String) -> Unit,
) )
fun getTimerOverviewActions( fun getTimerOverviewActions(
viewModel: TimerOverviewViewModel, viewModel: TimerOverviewViewModel,
open: (String) -> Unit,
): TimerOverviewActions { ): TimerOverviewActions {
return TimerOverviewActions( return TimerOverviewActions(
getUserTimers = viewModel::getUserTimers, getUserTimers = viewModel::getUserTimers,
getDefaultTimers = viewModel::getDefaultTimers, getDefaultTimers = viewModel::getDefaultTimers,
onEditClick = { viewModel.update(it) }, onEditClick = { viewModel.update(it) },
open = open
) )
} }
@Composable @Composable
fun TimerOverviewRoute( fun TimerOverviewRoute(
open: (String) -> Unit,
viewModel: TimerOverviewViewModel, viewModel: TimerOverviewViewModel,
drawerActions: DrawerActions, drawerActions: DrawerActions,
) { ) {
TimerOverviewScreen( TimerOverviewScreen(
timerOverviewActions = getTimerOverviewActions(viewModel), timerOverviewActions = getTimerOverviewActions(viewModel, open),
drawerActions = drawerActions, drawerActions = drawerActions,
) )
} }
@ -59,36 +63,35 @@ fun TimerOverviewScreen(
title = resources().getString(R.string.timers), title = resources().getString(R.string.timers),
drawerActions = drawerActions drawerActions = drawerActions
) { ) {
Column { LazyColumn {
LazyColumn { // Custom timer, select new duration each time
// Custom timer, select new duration each time item {
item { TimerEntry(timerInfo = CustomTimerInfo(
TimerEntry(timerInfo = CustomTimerInfo( name = resources().getString(R.string.custom_name),
name = resources().getString(R.string.custom_name), description = resources().getString(R.string.custom_name),
description = resources().getString(R.string.custom_name), studyTime = 0
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) }
)
}
}
} }
BasicButton(R.string.add_timer, Modifier.basicButton()) { // Default Timers, cannot be edited
// TODO 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)
}
} }
} }
} }
@ -104,7 +107,9 @@ fun TimerOverviewPreview() {
timerOverviewActions = TimerOverviewActions( timerOverviewActions = TimerOverviewActions(
{ flowOf() }, { flowOf() },
{ listOf(customTimer, customTimer) }, { listOf(customTimer, customTimer) },
{}), {},
{}
),
drawerActions = DrawerActions({}, {}, {}, {}, {}) drawerActions = DrawerActions({}, {}, {}, {}, {})
) )
} }

View file

@ -0,0 +1,12 @@
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",
)

View file

@ -0,0 +1,91 @@
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)
}
}

View file

@ -0,0 +1,274 @@
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_OVERVIEW_SCREEN)
}
}
)
}
}
}
}
}
@Preview
@Composable
fun AddTimerScreenPreview() { StudeezTheme {
AddTimerScreen(
addTimerActions = AddTimerActions({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}),
uiState = AddTimerUiState()
)
}
}

View file

@ -107,5 +107,20 @@
<!-- Session --> <!-- 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. --> <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>
<string name="addTimer_name_error">Timer name cannot be empty!</string>
<string name="addTimer_name">Timer name</string>
<string name="addTimer_timepicker">Open Time Picker</string>
<string name="breakTime_1">" hours and "</string>
<string name="breakTime_2">" minutes of breaktime"</string>
<string name="addTimer_break_1">" break"</string>
<string name="addTimer_break_s">" breaks"</string>
<string name="addTimer_break_question">With breaks?</string>
<string name="addTimer_studytime_1">" hours and "</string>
<string name="addTimer_studytime_2">" minutes of studytime"</string>
<string name="addTimer_question">How long do you want to study?</string>
</resources> </resources>

View file

@ -2,6 +2,7 @@ package be.ugent.sel.studeez.timer_functional
import android.media.MediaPlayer import android.media.MediaPlayer
import be.ugent.sel.studeez.data.SelectedTimerState import be.ugent.sel.studeez.data.SelectedTimerState
import be.ugent.sel.studeez.data.SessionReportState
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
@ -24,7 +25,7 @@ class InvisibleSessionManagerTest {
@Test @Test
fun InvisibleEndlessTimerTest() = runTest { fun InvisibleEndlessTimerTest() = runTest {
timerState.selectedTimer = FunctionalEndlessTimer() timerState.selectedTimer = FunctionalEndlessTimer()
viewModel = SessionViewModel(timerState, mock()) viewModel = SessionViewModel(timerState, SessionReportState(), mock())
InvisibleSessionManager.setParameters(viewModel, mediaPlayer) InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
val test = launch { val test = launch {
@ -46,7 +47,7 @@ class InvisibleSessionManagerTest {
val breakTime = 5 val breakTime = 5
val repeats = 1 val repeats = 1
timerState.selectedTimer = FunctionalPomodoroTimer(studyTime, breakTime, repeats) timerState.selectedTimer = FunctionalPomodoroTimer(studyTime, breakTime, repeats)
viewModel = SessionViewModel(timerState, mock()) viewModel = SessionViewModel(timerState, SessionReportState(), mock())
InvisibleSessionManager.setParameters(viewModel, mediaPlayer) InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
val test = launch { val test = launch {
@ -79,7 +80,7 @@ class InvisibleSessionManagerTest {
@Test @Test
fun InvisibleCustomTimerTest() = runTest { fun InvisibleCustomTimerTest() = runTest {
timerState.selectedTimer = FunctionalCustomTimer(5) timerState.selectedTimer = FunctionalCustomTimer(5)
viewModel = SessionViewModel(timerState, mock()) viewModel = SessionViewModel(timerState, SessionReportState(), mock())
InvisibleSessionManager.setParameters(viewModel, mediaPlayer) InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
val test = launch { val test = launch {