fix mergeconflict when merging tasksoverview
This commit is contained in:
		
						commit
						e2ada0b9d4
					
				
					 67 changed files with 1833 additions and 281 deletions
				
			
		| 
						 | 
				
			
			@ -38,6 +38,8 @@ import be.ugent.sel.studeez.screens.profile.EditProfileRoute
 | 
			
		|||
import be.ugent.sel.studeez.screens.profile.ProfileRoute
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.SessionRoute
 | 
			
		||||
import be.ugent.sel.studeez.screens.session_recap.SessionRecapRoute
 | 
			
		||||
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.tasks.SubjectRoute
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +48,9 @@ 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.TaskAddRoute
 | 
			
		||||
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_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.ui.theme.StudeezTheme
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +101,7 @@ fun resources(): Resources {
 | 
			
		|||
@Composable
 | 
			
		||||
fun StudeezNavGraph(
 | 
			
		||||
    appState: StudeezAppstate,
 | 
			
		||||
    modifier: Modifier,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
) {
 | 
			
		||||
    val drawerViewModel: DrawerViewModel = hiltViewModel()
 | 
			
		||||
    val navBarViewModel: NavigationBarViewModel = hiltViewModel()
 | 
			
		||||
| 
						 | 
				
			
			@ -119,35 +123,13 @@ fun StudeezNavGraph(
 | 
			
		|||
        startDestination = StudeezDestinations.SPLASH_SCREEN,
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SPLASH_SCREEN) {
 | 
			
		||||
            SplashRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.LOGIN_SCREEN) {
 | 
			
		||||
            LoginRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SIGN_UP_SCREEN) {
 | 
			
		||||
            SignUpRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // NavBar
 | 
			
		||||
        composable(StudeezDestinations.HOME_SCREEN) {
 | 
			
		||||
            HomeRoute(
 | 
			
		||||
                open,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
                drawerActions = drawerActions,
 | 
			
		||||
                navigationBarActions = navigationBarActions,
 | 
			
		||||
                navigationBarActions = navigationBarActions
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +158,14 @@ fun StudeezNavGraph(
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.TASKS_SCREEN) {
 | 
			
		||||
            TaskRoute(
 | 
			
		||||
                goBack = goBack,
 | 
			
		||||
                open = open,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.ADD_TASK_FORM) {
 | 
			
		||||
            TaskAddRoute(
 | 
			
		||||
                goBack = goBack,
 | 
			
		||||
| 
						 | 
				
			
			@ -192,16 +182,14 @@ fun StudeezNavGraph(
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.TASKS_SCREEN) {
 | 
			
		||||
            TaskRoute(
 | 
			
		||||
                goBack = goBack,
 | 
			
		||||
                open = open,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SESSIONS_SCREEN) {
 | 
			
		||||
            SessionsRoute(
 | 
			
		||||
                drawerActions = drawerActions,
 | 
			
		||||
                navigationBarActions = navigationBarActions
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO Sessions screen
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.PROFILE_SCREEN) {
 | 
			
		||||
            ProfileRoute(
 | 
			
		||||
                open,
 | 
			
		||||
| 
						 | 
				
			
			@ -211,10 +199,49 @@ fun StudeezNavGraph(
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) {
 | 
			
		||||
        // Drawer
 | 
			
		||||
        composable(StudeezDestinations.TIMER_SCREEN) {
 | 
			
		||||
            TimerOverviewRoute(
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
                drawerActions = drawerActions,
 | 
			
		||||
                open = open
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SETTINGS_SCREEN) {
 | 
			
		||||
            SettingsRoute(
 | 
			
		||||
                drawerActions = drawerActions
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Login flow
 | 
			
		||||
        composable(StudeezDestinations.SPLASH_SCREEN) {
 | 
			
		||||
            SplashRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.LOGIN_SCREEN) {
 | 
			
		||||
            LoginRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SIGN_UP_SCREEN) {
 | 
			
		||||
            SignUpRoute(
 | 
			
		||||
                openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Studying flow
 | 
			
		||||
        composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
 | 
			
		||||
            TimerSelectionRoute(
 | 
			
		||||
                open,
 | 
			
		||||
                goBack,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -226,10 +253,43 @@ fun StudeezNavGraph(
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO Timers screen
 | 
			
		||||
        // TODO Settings screen
 | 
			
		||||
        composable(StudeezDestinations.SESSION_RECAP) {
 | 
			
		||||
            SessionRecapRoute(
 | 
			
		||||
                openAndPopUp = openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.ADD_TIMER_SCREEN) {
 | 
			
		||||
            AddTimerRoute(
 | 
			
		||||
                open = open,
 | 
			
		||||
                goBack = goBack,
 | 
			
		||||
                viewModel = hiltViewModel()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.TIMER_EDIT_SCREEN) {
 | 
			
		||||
            TimerEditRoute(
 | 
			
		||||
                open = open,
 | 
			
		||||
                popUp = goBack,
 | 
			
		||||
                viewModel = hiltViewModel()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Friends flow
 | 
			
		||||
        composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) {
 | 
			
		||||
            // TODO
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create & edit screens
 | 
			
		||||
        composable(StudeezDestinations.CREATE_TASK_SCREEN) {
 | 
			
		||||
            // TODO
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.CREATE_SESSION_SCREEN) {
 | 
			
		||||
            // TODO
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Edit screens
 | 
			
		||||
        composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
 | 
			
		||||
            EditProfileRoute(
 | 
			
		||||
                goBack,
 | 
			
		||||
| 
						 | 
				
			
			@ -237,20 +297,5 @@ fun StudeezNavGraph(
 | 
			
		|||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
 | 
			
		||||
            TimerSelectionRoute(
 | 
			
		||||
                open,
 | 
			
		||||
                goBack,
 | 
			
		||||
                viewModel = hiltViewModel(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(StudeezDestinations.SESSION_RECAP) {
 | 
			
		||||
            SessionRecapRoute(
 | 
			
		||||
                openAndPopUp = openAndPopUp,
 | 
			
		||||
                viewModel = hiltViewModel()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,15 @@ import androidx.compose.material.Text
 | 
			
		|||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import be.ugent.sel.studeez.StudeezApp
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.InvisibleSessionManager
 | 
			
		||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
 | 
			
		||||
import dagger.hilt.android.AndroidEntryPoint
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
var onTimerInvisible: Job? = null
 | 
			
		||||
 | 
			
		||||
@AndroidEntryPoint
 | 
			
		||||
class MainActivity : ComponentActivity() {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +36,18 @@ class MainActivity : ComponentActivity() {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onStop() {
 | 
			
		||||
        onTimerInvisible = lifecycleScope.launch {
 | 
			
		||||
            InvisibleSessionManager.updateTimer()
 | 
			
		||||
        }
 | 
			
		||||
        super.onStop()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onStart() {
 | 
			
		||||
        onTimerInvisible?.cancel()
 | 
			
		||||
        super.onStart()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,11 +27,19 @@ import androidx.compose.ui.unit.dp
 | 
			
		|||
import androidx.compose.ui.unit.sp
 | 
			
		||||
import be.ugent.sel.studeez.common.ext.basicButton
 | 
			
		||||
import be.ugent.sel.studeez.common.ext.card
 | 
			
		||||
import be.ugent.sel.studeez.common.ext.defaultButtonShape
 | 
			
		||||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +53,7 @@ fun BasicButton(
 | 
			
		|||
    Button(
 | 
			
		||||
        onClick = onClick,
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        shape = RoundedCornerShape(20.dp),
 | 
			
		||||
        shape = defaultButtonShape(),
 | 
			
		||||
        colors = colors,
 | 
			
		||||
        border = border,
 | 
			
		||||
    ) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.composable
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.animation.core.animateFloat
 | 
			
		||||
import androidx.compose.animation.core.updateTransition
 | 
			
		||||
import androidx.compose.foundation.border
 | 
			
		||||
import androidx.compose.foundation.layout.*
 | 
			
		||||
import androidx.compose.material.FloatingActionButton
 | 
			
		||||
import androidx.compose.material.Icon
 | 
			
		||||
import androidx.compose.material.IconButton
 | 
			
		||||
| 
						 | 
				
			
			@ -11,52 +13,134 @@ import androidx.compose.material.icons.filled.Add
 | 
			
		|||
import androidx.compose.material.icons.filled.Check
 | 
			
		||||
import androidx.compose.material.icons.filled.DateRange
 | 
			
		||||
import androidx.compose.material.icons.filled.Person
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.*
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.rotate
 | 
			
		||||
import androidx.compose.ui.graphics.vector.ImageVector
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
 | 
			
		||||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
const val TRANSITION = "transition"
 | 
			
		||||
val HEIGHT_DIFFERENCE = 30.dp
 | 
			
		||||
 | 
			
		||||
data class AddButtonActions(
 | 
			
		||||
    val onTaskClick: () -> Unit,
 | 
			
		||||
    val onFriendClick: () -> Unit,
 | 
			
		||||
    val onSessionClick: () -> Unit
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CollapsedAddButton() {
 | 
			
		||||
    FloatingActionButton(
 | 
			
		||||
        onClick = { /* TODO popup add options */ }
 | 
			
		||||
fun AddButton(
 | 
			
		||||
    addButtonActions: AddButtonActions
 | 
			
		||||
) {
 | 
			
		||||
    var isExpanded by remember { mutableStateOf(false) }
 | 
			
		||||
 | 
			
		||||
    // Rotate the button when expanded, normal when collapsed.
 | 
			
		||||
    val transition = updateTransition(targetState = isExpanded, label = TRANSITION)
 | 
			
		||||
    val rotate by transition.animateFloat(label = TRANSITION) { expanded -> if (expanded) 315f else 0f }
 | 
			
		||||
 | 
			
		||||
    Column(
 | 
			
		||||
        horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
        verticalArrangement = Arrangement.Top
 | 
			
		||||
    ) {
 | 
			
		||||
        Icon(imageVector = Icons.Default.Add, contentDescription = "fab")
 | 
			
		||||
        Box {
 | 
			
		||||
            // Show minis when expanded.
 | 
			
		||||
            if (isExpanded) {
 | 
			
		||||
                ExpandedAddButton(
 | 
			
		||||
                    addButtonActions = addButtonActions
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The base add button
 | 
			
		||||
        FloatingActionButton(
 | 
			
		||||
            onClick = {
 | 
			
		||||
                // Toggle expanded/collapsed.
 | 
			
		||||
                isExpanded = !isExpanded
 | 
			
		||||
            },
 | 
			
		||||
            modifier = Modifier.padding(bottom = if (isExpanded) 78.dp else 0.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
            Icon(
 | 
			
		||||
                imageVector = Icons.Default.Add,
 | 
			
		||||
                contentDescription = "fab",
 | 
			
		||||
                modifier = Modifier.rotate(rotate) // The rotation
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun ExpandedAddButton() {
 | 
			
		||||
    Row() {
 | 
			
		||||
        IconButton(onClick = { /* TODO Go to next step */ }) {
 | 
			
		||||
            Column (horizontalAlignment = Alignment.CenterHorizontally) {
 | 
			
		||||
                Icon(imageVector = Icons.Default.Check, contentDescription = "Task")
 | 
			
		||||
                Text(text = "Task")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        IconButton(onClick = { /* TODO Go to next step */ }) {
 | 
			
		||||
            Column (horizontalAlignment = Alignment.CenterHorizontally) {
 | 
			
		||||
                Icon(imageVector = Icons.Default.Person, contentDescription = "Friend")
 | 
			
		||||
                Text(text = "Friend")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        IconButton(onClick = { /* TODO Go to next step */ }) {
 | 
			
		||||
            Column (horizontalAlignment = Alignment.CenterHorizontally) {
 | 
			
		||||
                Icon(imageVector = Icons.Default.DateRange, contentDescription = "Session")
 | 
			
		||||
                Text(text = "Session")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
fun ExpandedAddButton(
 | 
			
		||||
    addButtonActions: AddButtonActions
 | 
			
		||||
) {
 | 
			
		||||
    Row {
 | 
			
		||||
        ExpandedEntry(
 | 
			
		||||
            name = AppText.task,
 | 
			
		||||
            imageVector = Icons.Default.Check,
 | 
			
		||||
            onClick = addButtonActions.onTaskClick,
 | 
			
		||||
            modifier = Modifier.padding(36.dp, HEIGHT_DIFFERENCE, 36.dp, 0.dp)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        ExpandedEntry(
 | 
			
		||||
            name = AppText.friend,
 | 
			
		||||
            imageVector = Icons.Default.Person,
 | 
			
		||||
            onClick = addButtonActions.onFriendClick
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        ExpandedEntry(
 | 
			
		||||
            name = AppText.session,
 | 
			
		||||
            imageVector = Icons.Default.DateRange,
 | 
			
		||||
            onClick = addButtonActions.onSessionClick,
 | 
			
		||||
            modifier = Modifier.padding(36.dp, HEIGHT_DIFFERENCE, 36.dp, 0.dp)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun ExpandedEntry(
 | 
			
		||||
    name: Int,
 | 
			
		||||
    imageVector: ImageVector,
 | 
			
		||||
    onClick: () -> Unit,
 | 
			
		||||
    modifier: Modifier = Modifier
 | 
			
		||||
) {
 | 
			
		||||
    IconButton(
 | 
			
		||||
        onClick = onClick,
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
    ) {
 | 
			
		||||
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
 | 
			
		||||
            Icon(
 | 
			
		||||
                imageVector = imageVector,
 | 
			
		||||
                contentDescription = resources().getString(name),
 | 
			
		||||
                // TODO Dark overlay
 | 
			
		||||
                //  tint = colors.surface
 | 
			
		||||
            )
 | 
			
		||||
            Text(
 | 
			
		||||
                text = resources().getString(name),
 | 
			
		||||
                // TODO Dark overlay
 | 
			
		||||
                //  color = colors.surface
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun CollapsedAddButtonPreview() {
 | 
			
		||||
    StudeezTheme { CollapsedAddButton() }
 | 
			
		||||
fun AddButtonPreview() {
 | 
			
		||||
    StudeezTheme { AddButton(
 | 
			
		||||
        addButtonActions = AddButtonActions({}, {}, {})
 | 
			
		||||
    )}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun ExpandedAddButtonPreview() {
 | 
			
		||||
    StudeezTheme { ExpandedAddButton() }
 | 
			
		||||
    StudeezTheme { ExpandedAddButton (
 | 
			
		||||
        addButtonActions = AddButtonActions({}, {}, {})
 | 
			
		||||
    ) }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,11 @@ fun PrimaryScreenTemplate(
 | 
			
		|||
        bottomBar = { NavigationBar(navigationBarActions) },
 | 
			
		||||
        floatingActionButtonPosition = FabPosition.Center,
 | 
			
		||||
        isFloatingActionButtonDocked = true,
 | 
			
		||||
        floatingActionButton = { CollapsedAddButton() }
 | 
			
		||||
        floatingActionButton = { AddButton(AddButtonActions(
 | 
			
		||||
            onTaskClick = navigationBarActions.onAddTaskClick,
 | 
			
		||||
            onFriendClick = navigationBarActions.onAddFriendClick,
 | 
			
		||||
            onSessionClick = navigationBarActions.onAddSessionClick
 | 
			
		||||
        )) }
 | 
			
		||||
    ) {
 | 
			
		||||
        content(it)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +74,7 @@ fun PrimaryScreenPreview() {
 | 
			
		|||
        PrimaryScreenTemplate(
 | 
			
		||||
            "Preview screen",
 | 
			
		||||
            DrawerActions({}, {}, {}, {}, {}),
 | 
			
		||||
            NavigationBarActions({ false }, {}, {}, {}, {}),
 | 
			
		||||
            NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
 | 
			
		||||
            {
 | 
			
		||||
                IconButton(onClick = { /*TODO*/ }) {
 | 
			
		||||
                    Icon(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,10 +41,12 @@ fun BasicField(
 | 
			
		|||
fun LabelledInputField(
 | 
			
		||||
    value: String,
 | 
			
		||||
    onNewValue: (String) -> Unit,
 | 
			
		||||
    @StringRes label: Int
 | 
			
		||||
    @StringRes label: Int,
 | 
			
		||||
    singleLine: Boolean = false
 | 
			
		||||
) {
 | 
			
		||||
    OutlinedTextField(
 | 
			
		||||
        value = value,
 | 
			
		||||
        singleLine = singleLine,
 | 
			
		||||
        onValueChange = onNewValue,
 | 
			
		||||
        label = { Text(text = stringResource(id = label)) },
 | 
			
		||||
        modifier = Modifier.fieldModifier()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
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.*
 | 
			
		||||
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 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(
 | 
			
		||||
    initialSeconds: Int,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    colors: ButtonColors = ButtonDefaults.buttonColors(),
 | 
			
		||||
    border: BorderStroke? = null,
 | 
			
		||||
    onTimeChosen: (Int) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    val context = LocalContext.current
 | 
			
		||||
    val timeState: MutableState<Int> = remember {
 | 
			
		||||
        mutableStateOf(initialSeconds)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Button(
 | 
			
		||||
        onClick = { pickDuration(context, onTimeChosen, timeState) },
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        shape = RoundedCornerShape(20.dp),
 | 
			
		||||
        colors = colors,
 | 
			
		||||
        border = border
 | 
			
		||||
    ) {
 | 
			
		||||
        Text(text = HoursMinutesSeconds(timeState.value).toString())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
        hms.hours,
 | 
			
		||||
        hms.minutes,
 | 
			
		||||
        true
 | 
			
		||||
    )
 | 
			
		||||
    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,5 +1,6 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.composable.drawer
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +17,7 @@ import androidx.compose.material.icons.outlined.Info
 | 
			
		|||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.vector.ImageVector
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.compose.ui.res.vectorResource
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +30,7 @@ data class DrawerActions(
 | 
			
		|||
    val onTimersClick: () -> Unit,
 | 
			
		||||
    val onSettingsClick: () -> Unit,
 | 
			
		||||
    val onLogoutClick: () -> Unit,
 | 
			
		||||
    val onAboutClick: () -> Unit,
 | 
			
		||||
    val onAboutClick: (Context) -> Unit,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun getDrawerActions(
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +43,7 @@ fun getDrawerActions(
 | 
			
		|||
        onTimersClick = { drawerViewModel.onTimersClick(open) },
 | 
			
		||||
        onSettingsClick = { drawerViewModel.onSettingsClick(open) },
 | 
			
		||||
        onLogoutClick = { drawerViewModel.onLogoutClick(openAndPopUp) },
 | 
			
		||||
        onAboutClick = { drawerViewModel.onAboutClick(open) },
 | 
			
		||||
        onAboutClick = { context -> drawerViewModel.onAboutClick(open, context = context) },
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -79,10 +81,11 @@ fun Drawer(
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val context = LocalContext.current
 | 
			
		||||
        DrawerEntry(
 | 
			
		||||
            icon = Icons.Outlined.Info,
 | 
			
		||||
            text = resources().getString(R.string.about),
 | 
			
		||||
            onClick = drawerActions.onAboutClick,
 | 
			
		||||
            onClick = { drawerActions.onAboutClick(context) },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +99,7 @@ fun DrawerEntry(
 | 
			
		|||
    Row(
 | 
			
		||||
        horizontalArrangement = Arrangement.Center,
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .clickable(onClick = { onClick() })
 | 
			
		||||
            .clickable(onClick = onClick)
 | 
			
		||||
            .fillMaxWidth()
 | 
			
		||||
    ) {
 | 
			
		||||
        Box(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,10 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.composable.drawer
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import be.ugent.sel.studeez.domain.AccountDAO
 | 
			
		||||
import be.ugent.sel.studeez.domain.LogService
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +14,8 @@ import be.ugent.sel.studeez.screens.StudeezViewModel
 | 
			
		|||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
const val REPO_URL: String = "https://github.ugent.be/SELab1/project2023-groep14/"
 | 
			
		||||
 | 
			
		||||
@HiltViewModel
 | 
			
		||||
class DrawerViewModel @Inject constructor(
 | 
			
		||||
    private val accountDAO: AccountDAO,
 | 
			
		||||
| 
						 | 
				
			
			@ -20,11 +27,11 @@ class DrawerViewModel @Inject constructor(
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fun onTimersClick(openAndPopup: (String) -> Unit) {
 | 
			
		||||
        openAndPopup(StudeezDestinations.TIMER_OVERVIEW_SCREEN)
 | 
			
		||||
        openAndPopup(StudeezDestinations.TIMER_SCREEN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onSettingsClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO
 | 
			
		||||
        open(StudeezDestinations.SETTINGS_SCREEN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onLogoutClick(openAndPopUp: (String, String) -> Unit) {
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +41,8 @@ class DrawerViewModel @Inject constructor(
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onAboutClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO
 | 
			
		||||
    fun onAboutClick(open: (String) -> Unit, context: Context) {
 | 
			
		||||
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(REPO_URL))
 | 
			
		||||
        context.startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.composable.navbar
 | 
			
		||||
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import androidx.compose.material.BottomNavigation
 | 
			
		||||
import androidx.compose.material.BottomNavigationItem
 | 
			
		||||
import androidx.compose.material.Icon
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +14,7 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.SUBJECT_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
 | 
			
		||||
| 
						 | 
				
			
			@ -22,10 +22,16 @@ import be.ugent.sel.studeez.R.string as AppText
 | 
			
		|||
 | 
			
		||||
data class NavigationBarActions(
 | 
			
		||||
    val isSelectedTab: (String) -> Boolean,
 | 
			
		||||
 | 
			
		||||
    val onHomeClick: () -> Unit,
 | 
			
		||||
    val onTasksClick: () -> Unit,
 | 
			
		||||
    val onSessionsClick: () -> Unit,
 | 
			
		||||
    val onProfileClick: () -> Unit,
 | 
			
		||||
 | 
			
		||||
    // AddButton
 | 
			
		||||
    val onAddTaskClick: () -> Unit,
 | 
			
		||||
    val onAddFriendClick: () -> Unit,
 | 
			
		||||
    val onAddSessionClick: () -> Unit
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun getNavigationBarActions(
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +55,16 @@ fun getNavigationBarActions(
 | 
			
		|||
        onProfileClick = {
 | 
			
		||||
            navigationBarViewModel.onProfileClick(open)
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        onAddTaskClick = {
 | 
			
		||||
            navigationBarViewModel.onAddTaskClick(open)
 | 
			
		||||
        },
 | 
			
		||||
        onAddFriendClick = {
 | 
			
		||||
            navigationBarViewModel.onAddFriendClick(open)
 | 
			
		||||
        },
 | 
			
		||||
        onAddSessionClick = {
 | 
			
		||||
            navigationBarViewModel.onAddSessionClick(open)
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +94,7 @@ fun NavigationBar(
 | 
			
		|||
        )
 | 
			
		||||
 | 
			
		||||
        // Hack to space the entries in the navigation bar, make space for fab
 | 
			
		||||
        BottomNavigationItem(icon = {}, onClick = {}, selected = false)
 | 
			
		||||
        BottomNavigationItem(icon = {}, onClick = {}, selected = false, enabled = false)
 | 
			
		||||
 | 
			
		||||
        BottomNavigationItem(
 | 
			
		||||
            icon = {
 | 
			
		||||
| 
						 | 
				
			
			@ -87,8 +103,7 @@ fun NavigationBar(
 | 
			
		|||
                )
 | 
			
		||||
            },
 | 
			
		||||
            label = { Text(text = resources().getString(AppText.sessions)) },
 | 
			
		||||
            // TODO selected = navigationBarActions.isSelectedTab(SESSIONS_SCREEN),
 | 
			
		||||
            selected = false,
 | 
			
		||||
            selected = navigationBarActions.isSelectedTab(SESSIONS_SCREEN),
 | 
			
		||||
            onClick = navigationBarActions.onSessionsClick
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +126,7 @@ fun NavigationBar(
 | 
			
		|||
fun NavigationBarPreview() {
 | 
			
		||||
    StudeezTheme {
 | 
			
		||||
        NavigationBar(
 | 
			
		||||
            navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}),
 | 
			
		||||
            navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +1,15 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.composable.navbar
 | 
			
		||||
 | 
			
		||||
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
 | 
			
		||||
import be.ugent.sel.studeez.domain.LogService
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.SUBJECT_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.TASKS_SCREEN
 | 
			
		||||
import be.ugent.sel.studeez.screens.StudeezViewModel
 | 
			
		||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
@HiltViewModel
 | 
			
		||||
class NavigationBarViewModel @Inject constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -23,10 +25,25 @@ class NavigationBarViewModel @Inject constructor(
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fun onSessionsClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO
 | 
			
		||||
        open(SESSIONS_SCREEN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onProfileClick(open: (String) -> Unit) {
 | 
			
		||||
        open(PROFILE_SCREEN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onAddTaskClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO open(CREATE_TASK_SCREEN)
 | 
			
		||||
        SnackbarManager.showMessage(AppText.create_task_not_possible_yet) // TODO Remove
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onAddFriendClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO open(SEARCH_FRIENDS_SCREEN)
 | 
			
		||||
        SnackbarManager.showMessage(AppText.add_friend_not_possible_yet) // TODO Remove
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onAddSessionClick(open: (String) -> Unit) {
 | 
			
		||||
        // TODO open(CREATE_SESSION_SCREEN)
 | 
			
		||||
        SnackbarManager.showMessage(AppText.create_session_not_possible_yet) // TODO Remove
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
package be.ugent.sel.studeez.common.ext
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
fun defaultButtonShape(): RoundedCornerShape {
 | 
			
		||||
    return RoundedCornerShape(20.dp)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
package be.ugent.sel.studeez.data
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +1,10 @@
 | 
			
		|||
package be.ugent.sel.studeez.data.local.models.timer_functional
 | 
			
		||||
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.SessionReport
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.sessionScreens.CustomSessionScreen
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen
 | 
			
		||||
 | 
			
		||||
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++
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,5 @@
 | 
			
		|||
package be.ugent.sel.studeez.data.local.models.timer_functional
 | 
			
		||||
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.sessionScreens.BreakSessionScreen
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen
 | 
			
		||||
 | 
			
		||||
class FunctionalPomodoroTimer(
 | 
			
		||||
    private var studyTime: Int,
 | 
			
		||||
    private var breakTime: Int, repeats: Int
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +22,7 @@ class FunctionalPomodoroTimer(
 | 
			
		|||
            }
 | 
			
		||||
            isInBreak = !isInBreak
 | 
			
		||||
        }
 | 
			
		||||
        time.minOne()
 | 
			
		||||
        time--
 | 
			
		||||
 | 
			
		||||
        if (!isInBreak) {
 | 
			
		||||
            totalStudyTime++
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,10 @@
 | 
			
		|||
package be.ugent.sel.studeez.data.local.models.timer_functional
 | 
			
		||||
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.SessionReport
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen
 | 
			
		||||
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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,21 @@
 | 
			
		|||
package be.ugent.sel.studeez.data.local.models.timer_functional
 | 
			
		||||
 | 
			
		||||
class HoursMinutesSeconds(val hours: Int, val minutes: Int, val seconds: Int) {
 | 
			
		||||
    constructor(seconds: Int) : this(
 | 
			
		||||
        hours = seconds / (60 * 60),
 | 
			
		||||
        minutes = (seconds / 60) % 60,
 | 
			
		||||
        seconds = seconds % 60,
 | 
			
		||||
data class HoursMinutesSeconds(val hours: Int, val minutes: Int, val seconds: Int) {
 | 
			
		||||
 | 
			
		||||
    constructor(sec: Int): this(
 | 
			
		||||
        hours = sec / (60 * 60),
 | 
			
		||||
        minutes = (sec / (60)) % 60,
 | 
			
		||||
        seconds = sec % 60,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fun getTotalSeconds(): Int {
 | 
			
		||||
        return (hours * 60 * 60) + (minutes * 60) + seconds
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return hours.toString().padStart(2, '0') +
 | 
			
		||||
                ":" +
 | 
			
		||||
                minutes.toString().padStart(2, '0') +
 | 
			
		||||
                ":" +
 | 
			
		||||
                seconds.toString().padStart(2, '0')
 | 
			
		||||
        val hoursString = hours.toString().padStart(2, '0')
 | 
			
		||||
        val minutesString = minutes.toString().padStart(2, '0')
 | 
			
		||||
        val secondsString = seconds.toString().padStart(2, '0')
 | 
			
		||||
        return "$hoursString:$minutesString:$secondsString"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,11 @@
 | 
			
		|||
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 {
 | 
			
		||||
        return HoursMinutesSeconds(time)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,11 +6,10 @@ import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
 | 
			
		|||
class CustomTimerInfo(
 | 
			
		||||
    name: String,
 | 
			
		||||
    description: String,
 | 
			
		||||
    private val studyTime: Int,
 | 
			
		||||
    var studyTime: Int,
 | 
			
		||||
    id: String = ""
 | 
			
		||||
):  TimerInfo(id, name, description) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun getFunctionalTimer(): FunctionalTimer {
 | 
			
		||||
        return FunctionalCustomTimer(studyTime)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -24,4 +23,8 @@ class CustomTimerInfo(
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun <T> accept(visitor: TimerInfoVisitor<T>): T {
 | 
			
		||||
        return visitor.visitCustomTimerInfo(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -22,4 +22,8 @@ class EndlessTimerInfo(
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun <T> accept(visitor: TimerInfoVisitor<T>): T {
 | 
			
		||||
        return visitor.visitEndlessTimerInfo(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,13 +2,14 @@ package be.ugent.sel.studeez.data.local.models.timer_info
 | 
			
		|||
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVisitor
 | 
			
		||||
 | 
			
		||||
class PomodoroTimerInfo(
 | 
			
		||||
    name: String,
 | 
			
		||||
    description: String,
 | 
			
		||||
    private val studyTime: Int,
 | 
			
		||||
    private val breakTime: Int,
 | 
			
		||||
    private val repeats: Int,
 | 
			
		||||
    var studyTime: Int,
 | 
			
		||||
    var breakTime: Int,
 | 
			
		||||
    val repeats: Int,
 | 
			
		||||
    id: String = ""
 | 
			
		||||
):  TimerInfo(id, name, description) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,4 +29,8 @@ class PomodoroTimerInfo(
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun <T> accept(visitor: TimerInfoVisitor<T>): T {
 | 
			
		||||
        return visitor.visitBreakTimerInfo(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,8 +7,8 @@ import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
 | 
			
		|||
 */
 | 
			
		||||
abstract class TimerInfo(
 | 
			
		||||
    val id: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val description: String
 | 
			
		||||
    var name: String,
 | 
			
		||||
    var description: String
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ abstract class TimerInfo(
 | 
			
		|||
     * TODO implementaties hebben nog hardgecodeerde strings.
 | 
			
		||||
     */
 | 
			
		||||
    abstract fun asJson(): Map<String, Any>
 | 
			
		||||
    abstract fun <T> accept(visitor: TimerInfoVisitor<T>): T
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
package be.ugent.sel.studeez.data.local.models.timer_info
 | 
			
		||||
 | 
			
		||||
interface TimerInfoVisitor<T> {
 | 
			
		||||
 | 
			
		||||
    fun visitCustomTimerInfo(customTimerInfo: CustomTimerInfo): T
 | 
			
		||||
 | 
			
		||||
    fun visitEndlessTimerInfo(endlessTimerInfo: EndlessTimerInfo): T
 | 
			
		||||
 | 
			
		||||
    fun visitBreakTimerInfo(pomodoroTimerInfo: PomodoroTimerInfo): T
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +1,40 @@
 | 
			
		|||
package be.ugent.sel.studeez.navigation
 | 
			
		||||
 | 
			
		||||
object StudeezDestinations {
 | 
			
		||||
    const val SPLASH_SCREEN = "splash"
 | 
			
		||||
    const val SIGN_UP_SCREEN = "signup"
 | 
			
		||||
    const val LOGIN_SCREEN = "login"
 | 
			
		||||
 | 
			
		||||
    // NavBar
 | 
			
		||||
    const val HOME_SCREEN = "home"
 | 
			
		||||
    const val TIMER_OVERVIEW_SCREEN = "timer_overview"
 | 
			
		||||
    const val SUBJECT_SCREEN = "subjects"
 | 
			
		||||
    const val SESSIONS_SCREEN = "sessions"
 | 
			
		||||
    const val PROFILE_SCREEN = "profile"
 | 
			
		||||
 | 
			
		||||
    // Drawer
 | 
			
		||||
    const val TIMER_SCREEN = "timer_overview"
 | 
			
		||||
    const val SETTINGS_SCREEN = "settings"
 | 
			
		||||
 | 
			
		||||
    // Login flow
 | 
			
		||||
    const val SPLASH_SCREEN = "splash"
 | 
			
		||||
    const val LOGIN_SCREEN = "login"
 | 
			
		||||
    const val SIGN_UP_SCREEN = "signup"
 | 
			
		||||
 | 
			
		||||
    // 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"
 | 
			
		||||
 | 
			
		||||
    const val SUBJECT_SCREEN = "subjects"
 | 
			
		||||
    const val ADD_SUBJECT_FORM = "add_subject"
 | 
			
		||||
    const val EDIT_SUBJECT_FORM = "edit_subject"
 | 
			
		||||
    const val TASKS_SCREEN = "tasks"
 | 
			
		||||
    const val ADD_TASK_FORM = "add_task"
 | 
			
		||||
    const val EDIT_TASK_FORM = "edit_task"
 | 
			
		||||
 | 
			
		||||
    //    const val SESSIONS_SCREEN = "sessions"
 | 
			
		||||
    const val PROFILE_SCREEN = "profile"
 | 
			
		||||
    // Friends flow
 | 
			
		||||
    const val SEARCH_FRIENDS_SCREEN = "search_friends"
 | 
			
		||||
 | 
			
		||||
//    const val TIMERS_SCREEN = "timers"
 | 
			
		||||
//    const val SETTINGS_SCREEN = "settings"
 | 
			
		||||
 | 
			
		||||
    // Edit screens
 | 
			
		||||
    // Create & edit screens
 | 
			
		||||
    const val CREATE_TASK_SCREEN = "create_task"
 | 
			
		||||
    const val CREATE_SESSION_SCREEN = "create_session"
 | 
			
		||||
    const val EDIT_PROFILE_SCREEN = "edit_profile"
 | 
			
		||||
 | 
			
		||||
    const val ADD_TIMER_SCREEN = "add_timer"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -33,13 +33,13 @@ fun HomeRoute(
 | 
			
		|||
fun HomeScreen(
 | 
			
		||||
    onStartSessionClick: () -> Unit,
 | 
			
		||||
    drawerActions: DrawerActions,
 | 
			
		||||
    navigationBarActions: NavigationBarActions,
 | 
			
		||||
    navigationBarActions: NavigationBarActions
 | 
			
		||||
) {
 | 
			
		||||
    PrimaryScreenTemplate(
 | 
			
		||||
        title = resources().getString(R.string.home),
 | 
			
		||||
        drawerActions = drawerActions,
 | 
			
		||||
        navigationBarActions = navigationBarActions,
 | 
			
		||||
        barAction = { FriendsAction() }
 | 
			
		||||
        // TODO barAction = { FriendsAction() }
 | 
			
		||||
    ) {
 | 
			
		||||
        BasicButton(R.string.start_session, Modifier.basicButton()) {
 | 
			
		||||
            onStartSessionClick()
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +63,6 @@ fun HomeScreenPreview() {
 | 
			
		|||
    HomeScreen(
 | 
			
		||||
        onStartSessionClick = {},
 | 
			
		||||
        drawerActions = DrawerActions({}, {}, {}, {}, {}),
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {})
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {})
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable
 | 
			
		|||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.hilt.navigation.compose.hiltViewModel
 | 
			
		||||
import be.ugent.sel.studeez.R
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.BasicTextButton
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.LabelledInputField
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +63,10 @@ fun EditProfileScreen(
 | 
			
		|||
            BasicTextButton(
 | 
			
		||||
                text = R.string.save,
 | 
			
		||||
                Modifier.textButton(),
 | 
			
		||||
                action = editProfileActions.onSaveClick
 | 
			
		||||
                action = {
 | 
			
		||||
                    editProfileActions.onSaveClick()
 | 
			
		||||
                    goBack()
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            BasicTextButton(
 | 
			
		||||
                text = R.string.delete_profile,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,6 +88,6 @@ fun ProfileScreenPreview() {
 | 
			
		|||
    ProfileScreen(
 | 
			
		||||
        profileActions = ProfileActions({ null }, {}),
 | 
			
		||||
        drawerActions = DrawerActions({}, {}, {}, {}, {}),
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {})
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {})
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.session
 | 
			
		||||
 | 
			
		||||
import android.media.MediaPlayer
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import javax.inject.Singleton
 | 
			
		||||
import kotlin.time.Duration.Companion.seconds
 | 
			
		||||
 | 
			
		||||
@Singleton
 | 
			
		||||
object InvisibleSessionManager {
 | 
			
		||||
    private var viewModel: SessionViewModel? = null
 | 
			
		||||
    private lateinit var mediaPlayer: MediaPlayer
 | 
			
		||||
 | 
			
		||||
    fun setParameters(viewModel: SessionViewModel, mediaplayer: MediaPlayer) {
 | 
			
		||||
        this.viewModel = viewModel
 | 
			
		||||
        this.mediaPlayer = mediaplayer
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun updateTimer() {
 | 
			
		||||
        viewModel?.let {
 | 
			
		||||
            while (!it.getTimer().hasEnded()) {
 | 
			
		||||
                delay(1.seconds)
 | 
			
		||||
                it.getTimer().tick()
 | 
			
		||||
                if (it.getTimer().hasCurrentCountdownEnded()) {
 | 
			
		||||
                    mediaPlayer.start()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreen
 | 
			
		|||
data class SessionActions(
 | 
			
		||||
    val getTimer: () -> FunctionalTimer,
 | 
			
		||||
    val getTask: () -> String,
 | 
			
		||||
    val prepareMediaPlayer: () -> Unit,
 | 
			
		||||
    val startMediaPlayer: () -> Unit,
 | 
			
		||||
    val releaseMediaPlayer: () -> Unit,
 | 
			
		||||
    val endSession: () -> Unit
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,8 +26,8 @@ private fun getSessionActions(
 | 
			
		|||
        getTimer = viewModel::getTimer,
 | 
			
		||||
        getTask = viewModel::getTask,
 | 
			
		||||
        endSession = { viewModel.endSession(openAndPopUp) },
 | 
			
		||||
        prepareMediaPlayer = mediaplayer::prepareAsync,
 | 
			
		||||
        releaseMediaPlayer = mediaplayer::release
 | 
			
		||||
        startMediaPlayer = mediaplayer::start,
 | 
			
		||||
        releaseMediaPlayer = mediaplayer::release,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,26 +39,15 @@ fun SessionRoute(
 | 
			
		|||
) {
 | 
			
		||||
    val context = LocalContext.current
 | 
			
		||||
    val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
 | 
			
		||||
    val mediaplayer = MediaPlayer()
 | 
			
		||||
    mediaplayer.setDataSource(context, uri)
 | 
			
		||||
    mediaplayer.setOnCompletionListener {
 | 
			
		||||
        mediaplayer.stop()
 | 
			
		||||
        //if (timerEnd) {
 | 
			
		||||
//            mediaplayer.release()
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
    mediaplayer.setOnPreparedListener {
 | 
			
		||||
//        mediaplayer.start()
 | 
			
		||||
    }
 | 
			
		||||
    val mediaplayer = MediaPlayer.create(context, uri)
 | 
			
		||||
    mediaplayer.isLooping = false
 | 
			
		||||
 | 
			
		||||
    val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen())
 | 
			
		||||
    InvisibleSessionManager.setParameters(
 | 
			
		||||
        viewModel = viewModel,
 | 
			
		||||
        mediaplayer = mediaplayer
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    //val sessionScreen = when (val timer = viewModel.getTimer()) {
 | 
			
		||||
    //    is FunctionalCustomTimer -> CustomSessionScreen(timer)
 | 
			
		||||
    //    is FunctionalPomodoroTimer -> BreakSessionScreen(timer)
 | 
			
		||||
    //    is FunctionalEndlessTimer -> EndlessSessionScreen()
 | 
			
		||||
    //    else -> throw java.lang.IllegalArgumentException("Unknown Timer")
 | 
			
		||||
    //}
 | 
			
		||||
    val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen(mediaplayer))
 | 
			
		||||
 | 
			
		||||
    sessionScreen(
 | 
			
		||||
        open = open,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,12 @@ import androidx.compose.foundation.layout.padding
 | 
			
		|||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.material.TextButton
 | 
			
		||||
import androidx.compose.runtime.*
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
| 
						 | 
				
			
			@ -19,16 +24,12 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.compose.ui.unit.sp
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
 | 
			
		||||
import be.ugent.sel.studeez.screens.session.SessionActions
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlin.time.Duration.Companion.seconds
 | 
			
		||||
 | 
			
		||||
abstract class AbstractSessionScreen {
 | 
			
		||||
 | 
			
		||||
    var timerEnd = false
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    operator fun invoke(
 | 
			
		||||
        open: (String) -> Unit,
 | 
			
		||||
| 
						 | 
				
			
			@ -75,22 +76,10 @@ abstract class AbstractSessionScreen {
 | 
			
		|||
        LaunchedEffect(tikker) {
 | 
			
		||||
            delay(1.seconds)
 | 
			
		||||
            sessionActions.getTimer().tick()
 | 
			
		||||
            callMediaPlayer()
 | 
			
		||||
            tikker = !tikker
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            sessionActions.getTimer().hasCurrentCountdownEnded() && !sessionActions.getTimer()
 | 
			
		||||
                .hasEnded()
 | 
			
		||||
        ) {
 | 
			
		||||
//        sessionActions.prepareMediaPlayer()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!timerEnd && sessionActions.getTimer().hasEnded()) {
 | 
			
		||||
//        sessionActions.prepareMediaPlayer()
 | 
			
		||||
             timerEnd =
 | 
			
		||||
                 true // Placeholder, vanaf hier moet het report opgestart worden en de sessie afgesloten
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val hms = sessionActions.getTimer().getHoursMinutesSeconds()
 | 
			
		||||
        Column {
 | 
			
		||||
            Text(
 | 
			
		||||
| 
						 | 
				
			
			@ -137,6 +126,8 @@ abstract class AbstractSessionScreen {
 | 
			
		|||
    @Composable
 | 
			
		||||
    abstract fun motivationString(): String
 | 
			
		||||
 | 
			
		||||
    abstract fun callMediaPlayer()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
| 
						 | 
				
			
			@ -145,6 +136,7 @@ fun TimerPreview() {
 | 
			
		|||
    val sessionScreen = object : AbstractSessionScreen() {
 | 
			
		||||
        @Composable
 | 
			
		||||
        override fun motivationString(): String = "Test"
 | 
			
		||||
        override fun callMediaPlayer() {}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    sessionScreen.Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}, {}))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.session.sessionScreens
 | 
			
		||||
 | 
			
		||||
import android.media.MediaPlayer
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import be.ugent.sel.studeez.R
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +8,8 @@ import be.ugent.sel.studeez.resources
 | 
			
		|||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
class BreakSessionScreen(
 | 
			
		||||
    private val funPomoDoroTimer: FunctionalPomodoroTimer
 | 
			
		||||
    private val funPomoDoroTimer: FunctionalPomodoroTimer,
 | 
			
		||||
    private var mediaplayer: MediaPlayer?
 | 
			
		||||
): AbstractSessionScreen() {
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
| 
						 | 
				
			
			@ -27,4 +29,17 @@ class BreakSessionScreen(
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun callMediaPlayer() {
 | 
			
		||||
        if (funPomoDoroTimer.hasEnded()) {
 | 
			
		||||
            mediaplayer?.let { it: MediaPlayer ->
 | 
			
		||||
                it.setOnCompletionListener {
 | 
			
		||||
                    it.release()
 | 
			
		||||
                    mediaplayer = null
 | 
			
		||||
                }
 | 
			
		||||
                it.start()
 | 
			
		||||
            }
 | 
			
		||||
        } else if (funPomoDoroTimer.hasCurrentCountdownEnded()) {
 | 
			
		||||
            mediaplayer?.start()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.session.sessionScreens
 | 
			
		||||
 | 
			
		||||
import android.media.MediaPlayer
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +8,8 @@ import be.ugent.sel.studeez.R.string as AppText
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class CustomSessionScreen(
 | 
			
		||||
    private val functionalTimer: FunctionalCustomTimer
 | 
			
		||||
    private val functionalTimer: FunctionalCustomTimer,
 | 
			
		||||
    private var mediaplayer: MediaPlayer?
 | 
			
		||||
): AbstractSessionScreen() {
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
| 
						 | 
				
			
			@ -18,4 +20,16 @@ class CustomSessionScreen(
 | 
			
		|||
        return resources().getString(AppText.state_focus)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun callMediaPlayer() {
 | 
			
		||||
        if (functionalTimer.hasEnded()) {
 | 
			
		||||
            mediaplayer?.let { it: MediaPlayer ->
 | 
			
		||||
                it.setOnCompletionListener {
 | 
			
		||||
                    it.release()
 | 
			
		||||
                    mediaplayer = null
 | 
			
		||||
                }
 | 
			
		||||
                it.start()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -11,4 +11,6 @@ class EndlessSessionScreen : AbstractSessionScreen() {
 | 
			
		|||
    override fun motivationString(): String {
 | 
			
		||||
        return resources().getString(AppText.state_focus)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun callMediaPlayer() {}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +1,18 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.session.sessionScreens
 | 
			
		||||
 | 
			
		||||
import android.media.MediaPlayer
 | 
			
		||||
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.FunctionalPomodoroTimer
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVisitor
 | 
			
		||||
 | 
			
		||||
class GetSessionScreen : FunctionalTimerVisitor<AbstractSessionScreen> {
 | 
			
		||||
class GetSessionScreen(private val mediaplayer: MediaPlayer?) : FunctionalTimerVisitor<AbstractSessionScreen> {
 | 
			
		||||
    override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): AbstractSessionScreen =
 | 
			
		||||
        CustomSessionScreen(functionalCustomTimer)
 | 
			
		||||
        CustomSessionScreen(functionalCustomTimer, mediaplayer)
 | 
			
		||||
 | 
			
		||||
    override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): AbstractSessionScreen =
 | 
			
		||||
        EndlessSessionScreen()
 | 
			
		||||
 | 
			
		||||
    override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): AbstractSessionScreen =
 | 
			
		||||
        BreakSessionScreen(functionalPomodoroTimer)
 | 
			
		||||
        BreakSessionScreen(functionalPomodoroTimer, mediaplayer)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ fun getSessionRecapActions(
 | 
			
		|||
    return SessionRecapActions(
 | 
			
		||||
        viewModel::getSessionReport,
 | 
			
		||||
        {viewModel.saveSession(openAndPopUp)},
 | 
			
		||||
        {viewModel.saveSession(openAndPopUp)}
 | 
			
		||||
        {viewModel.discardSession(openAndPopUp)}
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -47,8 +47,10 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi
 | 
			
		|||
    val sessionReport: SessionReport = sessionRecapActions.getSessionReport()
 | 
			
		||||
    val studyTime: Int = sessionReport.studyTime
 | 
			
		||||
    val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS()
 | 
			
		||||
    Column {
 | 
			
		||||
        Text(text = "You studied: ${hms.hours} : ${hms.minutes} : ${hms.seconds}")
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
    ) {
 | 
			
		||||
        Text(text = "You studied: $hms")
 | 
			
		||||
 | 
			
		||||
        BasicButton(
 | 
			
		||||
            R.string.save, Modifier.basicButton()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import javax.inject.Inject
 | 
			
		|||
 | 
			
		||||
@HiltViewModel
 | 
			
		||||
class SessionRecapViewModel @Inject constructor(
 | 
			
		||||
    private val sessionReportState: SessionReportState,
 | 
			
		||||
    sessionReportState: SessionReportState,
 | 
			
		||||
    private val sessionDAO: SessionDAO,
 | 
			
		||||
    logService: LogService
 | 
			
		||||
) : StudeezViewModel(logService) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.sessions
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun SessionsRoute(
 | 
			
		||||
    // viewModel: SessionsViewModel,
 | 
			
		||||
    drawerActions: DrawerActions,
 | 
			
		||||
    navigationBarActions: NavigationBarActions
 | 
			
		||||
) {
 | 
			
		||||
    SessionsScreen(
 | 
			
		||||
        drawerActions = drawerActions,
 | 
			
		||||
        navigationBarActions = navigationBarActions
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun SessionsScreen(
 | 
			
		||||
    drawerActions: DrawerActions,
 | 
			
		||||
    navigationBarActions: NavigationBarActions
 | 
			
		||||
) {
 | 
			
		||||
    PrimaryScreenTemplate(
 | 
			
		||||
        title = resources().getString(AppText.upcoming_sessions),
 | 
			
		||||
        drawerActions = drawerActions,
 | 
			
		||||
        navigationBarActions = navigationBarActions
 | 
			
		||||
    ) {
 | 
			
		||||
        Text(
 | 
			
		||||
            text = resources().getString(AppText.sessions_temp_description),
 | 
			
		||||
            modifier = Modifier.fillMaxSize(),
 | 
			
		||||
            textAlign = TextAlign.Center
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.settings
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.DrawerScreenTemplate
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import be.ugent.sel.studeez.R.string as AppText
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun SettingsRoute(
 | 
			
		||||
    // viewModel: SettingsViewModel,
 | 
			
		||||
    drawerActions: DrawerActions
 | 
			
		||||
) {
 | 
			
		||||
    SettingsScreen(
 | 
			
		||||
        drawerActions = drawerActions
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun SettingsScreen(
 | 
			
		||||
    drawerActions: DrawerActions
 | 
			
		||||
) {
 | 
			
		||||
    DrawerScreenTemplate(
 | 
			
		||||
        title = resources().getString(AppText.settings),
 | 
			
		||||
        drawerActions = drawerActions
 | 
			
		||||
    ) {
 | 
			
		||||
        Text(
 | 
			
		||||
            text = resources().getString(AppText.settings_temp_description),
 | 
			
		||||
            modifier = Modifier.fillMaxSize(),
 | 
			
		||||
            textAlign = TextAlign.Center
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ fun SubjectScreen(
 | 
			
		|||
fun SubjectScreenPreview() {
 | 
			
		||||
    SubjectScreen(
 | 
			
		||||
        drawerActions = DrawerActions({}, {}, {}, {}, {}),
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}),
 | 
			
		||||
        navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
 | 
			
		||||
        addSubject = {},
 | 
			
		||||
        getSubjects = { flowOf() },
 | 
			
		||||
        onViewSubject = {},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.timer_edit
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
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.Composable
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import 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
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                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.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.timer_edit.editScreens
 | 
			
		||||
 | 
			
		||||
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 = {})
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.timer_edit.editScreens
 | 
			
		||||
 | 
			
		||||
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 = {})
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.timer_edit.editScreens
 | 
			
		||||
 | 
			
		||||
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 = {})
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +16,7 @@ import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
 | 
			
		|||
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.TimerInfo
 | 
			
		||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.flowOf
 | 
			
		||||
| 
						 | 
				
			
			@ -24,15 +25,18 @@ data class TimerOverviewActions(
 | 
			
		|||
    val getUserTimers: () -> Flow<List<TimerInfo>>,
 | 
			
		||||
    val getDefaultTimers: () -> List<TimerInfo>,
 | 
			
		||||
    val onEditClick: (TimerInfo) -> Unit,
 | 
			
		||||
    val onAddClick: () -> Unit,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun getTimerOverviewActions(
 | 
			
		||||
    viewModel: TimerOverviewViewModel,
 | 
			
		||||
    open: (String) -> Unit,
 | 
			
		||||
): TimerOverviewActions {
 | 
			
		||||
    return TimerOverviewActions(
 | 
			
		||||
        getUserTimers = viewModel::getUserTimers,
 | 
			
		||||
        getDefaultTimers = viewModel::getDefaultTimers,
 | 
			
		||||
        onEditClick = { viewModel.update(it) },
 | 
			
		||||
        onEditClick = { viewModel.update(it, open) },
 | 
			
		||||
        onAddClick = { viewModel.create(open) }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,10 +44,11 @@ fun getTimerOverviewActions(
 | 
			
		|||
fun TimerOverviewRoute(
 | 
			
		||||
    viewModel: TimerOverviewViewModel,
 | 
			
		||||
    drawerActions: DrawerActions,
 | 
			
		||||
    open: (String) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    TimerOverviewScreen(
 | 
			
		||||
        timerOverviewActions = getTimerOverviewActions(viewModel),
 | 
			
		||||
        drawerActions = drawerActions,
 | 
			
		||||
        timerOverviewActions = getTimerOverviewActions(viewModel, open),
 | 
			
		||||
        drawerActions = drawerActions
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,8 +64,16 @@ fun TimerOverviewScreen(
 | 
			
		|||
        title = resources().getString(R.string.timers),
 | 
			
		||||
        drawerActions = drawerActions
 | 
			
		||||
    ) {
 | 
			
		||||
        Column {
 | 
			
		||||
        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) {}
 | 
			
		||||
| 
						 | 
				
			
			@ -77,9 +90,13 @@ fun TimerOverviewScreen(
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BasicButton(R.string.add_timer, Modifier.basicButton()) {
 | 
			
		||||
                // TODO
 | 
			
		||||
 | 
			
		||||
                // TODO uit lazy column
 | 
			
		||||
                item {
 | 
			
		||||
                    BasicButton(R.string.add_timer, Modifier.basicButton()) {
 | 
			
		||||
                        timerOverviewActions.onAddClick()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +112,9 @@ fun TimerOverviewPreview() {
 | 
			
		|||
        timerOverviewActions = TimerOverviewActions(
 | 
			
		||||
            { flowOf() },
 | 
			
		||||
            { listOf(customTimer, customTimer) },
 | 
			
		||||
            {}),
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
        ),
 | 
			
		||||
        drawerActions = DrawerActions({}, {}, {}, {}, {})
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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_SCREEN)
 | 
			
		||||
                            }
 | 
			
		||||
                         }
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun AddTimerScreenPreview() { StudeezTheme {
 | 
			
		||||
    AddTimerScreen(
 | 
			
		||||
        addTimerActions = AddTimerActions({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}),
 | 
			
		||||
        uiState = AddTimerUiState()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +8,11 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import be.ugent.sel.studeez.R
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.StealthButton
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.TimePickerButton
 | 
			
		||||
import be.ugent.sel.studeez.common.composable.TimerEntry
 | 
			
		||||
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.TimerInfo
 | 
			
		||||
import be.ugent.sel.studeez.resources
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +21,7 @@ import kotlinx.coroutines.flow.flowOf
 | 
			
		|||
data class TimerSelectionActions(
 | 
			
		||||
    val getAllTimers: () -> Flow<List<TimerInfo>>,
 | 
			
		||||
    val startSession: (TimerInfo) -> Unit,
 | 
			
		||||
    val customTimeStudyTime: Int
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun getTimerSelectionActions(
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +31,7 @@ fun getTimerSelectionActions(
 | 
			
		|||
    return TimerSelectionActions(
 | 
			
		||||
        getAllTimers = viewModel::getAllTimers,
 | 
			
		||||
        startSession = { viewModel.startSession(open, it) },
 | 
			
		||||
        customTimeStudyTime = viewModel.customTimerStudyTime.value
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +58,11 @@ fun TimerSelectionScreen(
 | 
			
		|||
        popUp = popUp
 | 
			
		||||
    ) {
 | 
			
		||||
        LazyColumn {
 | 
			
		||||
            // Custom timer with duration selection button
 | 
			
		||||
            item {
 | 
			
		||||
                CustomTimerEntry(timerSelectionActions)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // All timers
 | 
			
		||||
            items(timers.value) { timerInfo ->
 | 
			
		||||
                TimerEntry(
 | 
			
		||||
| 
						 | 
				
			
			@ -68,11 +79,38 @@ fun TimerSelectionScreen(
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CustomTimerEntry(
 | 
			
		||||
    timerSelectionActions: TimerSelectionActions
 | 
			
		||||
) {
 | 
			
		||||
    val timerInfo = CustomTimerInfo(
 | 
			
		||||
        name = resources().getString(R.string.custom_name),
 | 
			
		||||
        description = resources().getString(R.string.custom_description),
 | 
			
		||||
        studyTime = timerSelectionActions.customTimeStudyTime
 | 
			
		||||
    )
 | 
			
		||||
    val hms: HoursMinutesSeconds = Time(timerInfo.studyTime).getAsHMS()
 | 
			
		||||
 | 
			
		||||
    TimerEntry(
 | 
			
		||||
        timerInfo = timerInfo,
 | 
			
		||||
        leftButton = {
 | 
			
		||||
            StealthButton(
 | 
			
		||||
                text = R.string.start,
 | 
			
		||||
                onClick = { timerSelectionActions.startSession(timerInfo) }
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        rightButton = {
 | 
			
		||||
            TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime ->
 | 
			
		||||
                timerInfo.studyTime = chosenTime
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun TimerSelectionPreview() {
 | 
			
		||||
    TimerSelectionScreen(
 | 
			
		||||
        timerSelectionActions = TimerSelectionActions({ flowOf() }, {}),
 | 
			
		||||
        timerSelectionActions = TimerSelectionActions({ flowOf() }, {}, 0),
 | 
			
		||||
        popUp = {}
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,9 @@
 | 
			
		|||
package be.ugent.sel.studeez.screens.timer_selection
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.MutableState
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import be.ugent.sel.studeez.data.SelectedTimerState
 | 
			
		||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
 | 
			
		||||
import be.ugent.sel.studeez.domain.LogService
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +21,8 @@ class TimerSelectionViewModel @Inject constructor(
 | 
			
		|||
    logService: LogService
 | 
			
		||||
) : StudeezViewModel(logService) {
 | 
			
		||||
 | 
			
		||||
    var customTimerStudyTime: MutableState<Int> = mutableStateOf(0)
 | 
			
		||||
 | 
			
		||||
    fun getAllTimers() : Flow<List<TimerInfo>> {
 | 
			
		||||
        return timerDAO.getAllTimers()
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,9 @@ import androidx.compose.ui.graphics.Color
 | 
			
		|||
private val DarkColorPalette = darkColors(
 | 
			
		||||
    primary = Blue100,
 | 
			
		||||
    primaryVariant = Blue120,
 | 
			
		||||
    secondary = Yellow100
 | 
			
		||||
    secondary = Yellow100,
 | 
			
		||||
 | 
			
		||||
    onPrimary = Color.White
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private val LightColorPalette = lightColors(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in a new issue