Merge branch 'development' into sessionscreen_ui
This commit is contained in:
commit
9d2c53d4e6
26 changed files with 477 additions and 79 deletions
|
@ -26,6 +26,7 @@ import be.ugent.sel.studeez.screens.profile.ProfileScreen
|
||||||
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
|
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
|
||||||
import be.ugent.sel.studeez.screens.splash.SplashScreen
|
import be.ugent.sel.studeez.screens.splash.SplashScreen
|
||||||
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen
|
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen
|
||||||
|
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionScreen
|
||||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
|
@ -130,4 +131,8 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
|
||||||
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
|
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
|
||||||
EditProfileScreen(goBack, openAndPopUp)
|
EditProfileScreen(goBack, openAndPopUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
|
||||||
|
TimerSelectionScreen(open, openAndPopUp)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package be.ugent.sel.studeez.data
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to communicate the SelectedTimer from the selection screen to the session screen.
|
||||||
|
* Because this is a singleton-class the view-models of both screens observe the same data.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class SelectedTimerState @Inject constructor(){
|
||||||
|
var selectedTimer: FunctionalTimer? = null
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
package be.ugent.sel.studeez.data.local.models
|
package be.ugent.sel.studeez.data.local.models
|
||||||
|
|
||||||
data class User(val id: String = "", val isAnonymous: Boolean = true)
|
data class User(val id: String = "")
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
|
|
||||||
class FunctionalCustomTimer(studyTime: Int): FunctionalTimer(studyTime) {
|
class FunctionalCustomTimer(studyTime: Int) : FunctionalTimer(studyTime) {
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
if (time.getTime() == 0) {
|
if (time.time == 0) {
|
||||||
view = "Done!"
|
view = StudyState.DONE
|
||||||
} else {
|
} else {
|
||||||
time.minOne()
|
time.minOne()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasEnded(): Boolean {
|
override fun hasEnded(): Boolean {
|
||||||
return time.getTime() == 0
|
return time.time == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
|
|
||||||
class FunctionalEndlessTimer() : FunctionalTimer(0){
|
class FunctionalEndlessTimer() : FunctionalTimer(0) {
|
||||||
|
|
||||||
override fun hasEnded(): Boolean {
|
override fun hasEnded(): Boolean {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -3,25 +3,25 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
class FunctionalPomodoroTimer(
|
class FunctionalPomodoroTimer(
|
||||||
private var studyTime: Int,
|
private var studyTime: Int,
|
||||||
private var breakTime: Int, repeats: Int
|
private var breakTime: Int, repeats: Int
|
||||||
): FunctionalTimer(studyTime) {
|
) : FunctionalTimer(studyTime) {
|
||||||
|
|
||||||
private var breaksRemaining = repeats
|
var breaksRemaining = repeats
|
||||||
private var isInBreak = false
|
var isInBreak = false
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
if (time.getTime() == 0 && breaksRemaining == 0){
|
if (time.time == 0 && breaksRemaining == 0) {
|
||||||
view = "Done!"
|
view = StudyState.DONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time.getTime() == 0) {
|
if (time.time == 0) {
|
||||||
if (isInBreak) {
|
if (isInBreak) {
|
||||||
breaksRemaining--
|
breaksRemaining--
|
||||||
view = "Focus! ($breaksRemaining breaks remaining)"
|
view = StudyState.FOCUS_REMAINING
|
||||||
time.setTime(studyTime)
|
time.time = studyTime
|
||||||
} else {
|
} else {
|
||||||
view = "Take a break!"
|
view = StudyState.BREAK
|
||||||
time.setTime(breakTime)
|
time.time = breakTime
|
||||||
}
|
}
|
||||||
isInBreak = !isInBreak
|
isInBreak = !isInBreak
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,6 @@ class FunctionalPomodoroTimer(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasEnded(): Boolean {
|
override fun hasEnded(): Boolean {
|
||||||
return breaksRemaining == 0 && time.getTime() == 0
|
return breaksRemaining == 0 && time.time == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +1,13 @@
|
||||||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
|
|
||||||
abstract class FunctionalTimer(initialValue: Int) {
|
abstract class FunctionalTimer(initialValue: Int) {
|
||||||
protected val time: Time = Time(initialValue)
|
val time: Time = Time(initialValue)
|
||||||
protected var view: String = "Focus"
|
var view: StudyState = StudyState.FOCUS
|
||||||
|
|
||||||
fun getHoursMinutesSeconds(): HoursMinutesSeconds {
|
fun getHoursMinutesSeconds(): HoursMinutesSeconds {
|
||||||
return time.getAsHMS()
|
return time.getAsHMS()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getViewString(): String {
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun tick()
|
abstract fun tick()
|
||||||
|
|
||||||
abstract fun hasEnded(): Boolean
|
abstract fun hasEnded(): Boolean
|
||||||
|
@ -19,4 +15,9 @@ abstract class FunctionalTimer(initialValue: Int) {
|
||||||
fun hasCurrentCountdownEnded(): Boolean {
|
fun hasCurrentCountdownEnded(): Boolean {
|
||||||
return time.getTime() == 0
|
return time.getTime() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class StudyState {
|
||||||
|
FOCUS, DONE, BREAK, FOCUS_REMAINING
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
|
|
||||||
class Time(initialTime: Int) {
|
class Time(initialTime: Int) {
|
||||||
|
|
||||||
private var time = initialTime
|
var time = initialTime
|
||||||
|
|
||||||
fun minOne() {
|
fun minOne() {
|
||||||
time--
|
time--
|
||||||
|
@ -12,14 +12,6 @@ class Time(initialTime: Int) {
|
||||||
time++
|
time++
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTime(newTime: Int) {
|
|
||||||
time = newTime
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTime(): Int {
|
|
||||||
return time
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAsHMS(): HoursMinutesSeconds {
|
fun getAsHMS(): HoursMinutesSeconds {
|
||||||
val hours: Int = time / (60 * 60)
|
val hours: Int = time / (60 * 60)
|
||||||
val minutes: Int = (time / (60)) % 60
|
val minutes: Int = (time / (60)) % 60
|
||||||
|
|
|
@ -3,7 +3,7 @@ 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.FunctionalPomodoroTimer
|
||||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
|
||||||
class BreakTimerInfo(
|
class PomodoroTimerInfo(
|
||||||
name: String,
|
name: String,
|
||||||
description: String,
|
description: String,
|
||||||
private val studyTime: Int,
|
private val studyTime: Int,
|
|
@ -39,7 +39,7 @@ class FirebaseAccountDAO @Inject constructor(
|
||||||
get() = callbackFlow {
|
get() = callbackFlow {
|
||||||
val listener =
|
val listener =
|
||||||
FirebaseAuth.AuthStateListener { auth ->
|
FirebaseAuth.AuthStateListener { auth ->
|
||||||
this.trySend(auth.currentUser?.let { User(it.uid, it.isAnonymous) } ?: User())
|
this.trySend(auth.currentUser?.let { User(it.uid) } ?: User())
|
||||||
}
|
}
|
||||||
auth.addAuthStateListener(listener)
|
auth.addAuthStateListener(listener)
|
||||||
awaitClose { auth.removeAuthStateListener(listener) }
|
awaitClose { auth.removeAuthStateListener(listener) }
|
||||||
|
|
|
@ -30,7 +30,7 @@ class ToTimerConverter {
|
||||||
it.studyTime,
|
it.studyTime,
|
||||||
it.id
|
it.id
|
||||||
) },
|
) },
|
||||||
TimerType.BREAK to TimerFactory { BreakTimerInfo(
|
TimerType.BREAK to TimerFactory { PomodoroTimerInfo(
|
||||||
it.name,
|
it.name,
|
||||||
it.description,
|
it.description,
|
||||||
it.studyTime,
|
it.studyTime,
|
||||||
|
|
|
@ -5,8 +5,9 @@ object StudeezDestinations {
|
||||||
const val SIGN_UP_SCREEN = "signup"
|
const val SIGN_UP_SCREEN = "signup"
|
||||||
const val LOGIN_SCREEN = "login"
|
const val LOGIN_SCREEN = "login"
|
||||||
|
|
||||||
const val HOME_SCREEN = "home"
|
const val HOME_SCREEN = "home"
|
||||||
const val TIMER_OVERVIEW_SCREEN = "timer_overview"
|
const val TIMER_OVERVIEW_SCREEN = "timer_overview"
|
||||||
|
const val TIMER_SELECTION_SCREEN = "timer_selection"
|
||||||
const val SESSION_SCREEN = "session"
|
const val SESSION_SCREEN = "session"
|
||||||
// const val TASKS_SCREEN = "tasks"
|
// const val TASKS_SCREEN = "tasks"
|
||||||
// const val SESSIONS_SCREEN = "sessions"
|
// const val SESSIONS_SCREEN = "sessions"
|
||||||
|
|
|
@ -15,14 +15,7 @@ class HomeViewModel @Inject constructor(
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
fun onStartSessionClick(openAndPopUp: (String) -> Unit) {
|
fun onStartSessionClick(open: (String) -> Unit) {
|
||||||
openAndPopUp(StudeezDestinations.SESSION_SCREEN)
|
open(StudeezDestinations.TIMER_SELECTION_SCREEN)
|
||||||
}
|
|
||||||
|
|
||||||
fun onLogoutClick(openAndPopup: (String, String) -> Unit) {
|
|
||||||
launchCatching {
|
|
||||||
accountDAO.signOut()
|
|
||||||
openAndPopup(LOGIN_SCREEN, HOME_SCREEN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,16 +14,28 @@ import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TextButton
|
import androidx.compose.material.TextButton
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
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.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
import be.ugent.sel.studeez.R
|
||||||
|
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.FunctionalTimer.StudyState
|
||||||
|
import be.ugent.sel.studeez.resources
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
var timerEnd = false
|
var timerEnd = false
|
||||||
|
|
||||||
|
@ -31,7 +43,6 @@ var timerEnd = false
|
||||||
fun SessionScreen(
|
fun SessionScreen(
|
||||||
open: (String) -> Unit,
|
open: (String) -> Unit,
|
||||||
openAndPopUp: (String, String) -> Unit,
|
openAndPopUp: (String, String) -> Unit,
|
||||||
viewModel: SessionViewModel = hiltViewModel()
|
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||||
|
@ -86,9 +97,9 @@ fun SessionScreen(
|
||||||
private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) {
|
private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) {
|
||||||
var tikker by remember { mutableStateOf(false) }
|
var tikker by remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(tikker) {
|
LaunchedEffect(tikker) {
|
||||||
delay(1000)
|
delay(1.seconds)
|
||||||
viewModel.getTimer().tick()
|
viewModel.getTimer().tick()
|
||||||
tikker = !tikker
|
ticker = !ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) {
|
if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) {
|
||||||
|
@ -111,8 +122,18 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 40.sp,
|
fontSize = 40.sp,
|
||||||
)
|
)
|
||||||
|
val stateString: String = when (viewModel.getTimer().view) {
|
||||||
|
StudyState.DONE -> resources().getString(R.string.state_done)
|
||||||
|
StudyState.FOCUS -> resources().getString(R.string.state_focus)
|
||||||
|
StudyState.BREAK -> resources().getString(R.string.state_take_a_break)
|
||||||
|
StudyState.FOCUS_REMAINING ->
|
||||||
|
(viewModel.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
|
||||||
|
resources().getQuantityString(R.plurals.state_focus_remaining, it, it)
|
||||||
|
}.toString()
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = viewModel.getTimer().getViewString(),
|
text = stateString,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontWeight = FontWeight.Light,
|
fontWeight = FontWeight.Light,
|
||||||
|
@ -142,4 +163,6 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
package be.ugent.sel.studeez.screens.session
|
package be.ugent.sel.studeez.screens.session
|
||||||
|
|
||||||
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.FunctionalTimer
|
||||||
import be.ugent.sel.studeez.domain.LogService
|
import be.ugent.sel.studeez.domain.LogService
|
||||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||||
|
import be.ugent.sel.studeez.data.SelectedTimerState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SessionViewModel @Inject constructor(
|
class SessionViewModel @Inject constructor(
|
||||||
|
private val selectedTimerState: SelectedTimerState,
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ class SessionViewModel @Inject constructor(
|
||||||
private val task : String = "No task selected" // placeholder for tasks implementation
|
private val task : String = "No task selected" // placeholder for tasks implementation
|
||||||
|
|
||||||
fun getTimer() : FunctionalTimer {
|
fun getTimer() : FunctionalTimer {
|
||||||
return timer
|
return selectedTimerState.selectedTimer!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTask(): String {
|
fun getTask(): String {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package be.ugent.sel.studeez.screens.timer_overview
|
package be.ugent.sel.studeez.screens.timer_overview
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
@ -46,12 +47,12 @@ fun TimerOverviewScreen(
|
||||||
) {
|
) {
|
||||||
// Default Timers, cannot be edited
|
// Default Timers, cannot be edited
|
||||||
items(viewModel.getDefaultTimers()) {
|
items(viewModel.getDefaultTimers()) {
|
||||||
TimerEntry(timerInfo = it, canEdit = false)
|
TimerEntry(timerInfo = it, canDisplay = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// User timers, can be edited
|
// User timers, can be edited
|
||||||
items(timers.value) {
|
items(timers.value) {
|
||||||
TimerEntry(timerInfo = it, true) { timerInfo ->
|
TimerEntry(timerInfo = it, true, R.string.edit) { timerInfo ->
|
||||||
viewModel.update(timerInfo)
|
viewModel.update(timerInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +66,12 @@ fun TimerOverviewScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimerEntry(timerInfo: TimerInfo, canEdit: Boolean, update: (TimerInfo) -> Unit = {}) {
|
fun TimerEntry(
|
||||||
|
timerInfo: TimerInfo,
|
||||||
|
canDisplay: Boolean,
|
||||||
|
@StringRes buttonName: Int = -1,
|
||||||
|
buttonFunction: (TimerInfo) -> Unit = {}
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
@ -83,9 +89,9 @@ fun TimerEntry(timerInfo: TimerInfo, canEdit: Boolean, update: (TimerInfo) -> Un
|
||||||
fontSize = 15.sp
|
fontSize = 15.sp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (canEdit) {
|
if (canDisplay) {
|
||||||
BasicButton(R.string.edit, Modifier.card()) {
|
BasicButton(buttonName, Modifier.card()) {
|
||||||
// TODO
|
buttonFunction(timerInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package be.ugent.sel.studeez.screens.timer_selection
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import be.ugent.sel.studeez.R
|
||||||
|
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
||||||
|
import be.ugent.sel.studeez.resources
|
||||||
|
import be.ugent.sel.studeez.screens.timer_overview.TimerEntry
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimerSelectionScreen(
|
||||||
|
open: (String) -> Unit,
|
||||||
|
openAndPopUp: (String, String) -> Unit,
|
||||||
|
viewModel: TimerSelectionViewModel = hiltViewModel()
|
||||||
|
) {
|
||||||
|
|
||||||
|
val timers = viewModel.getAllTimers().collectAsState(initial = emptyList())
|
||||||
|
|
||||||
|
PrimaryScreenTemplate(
|
||||||
|
title = resources().getString(R.string.timers),
|
||||||
|
open = open,
|
||||||
|
openAndPopUp = openAndPopUp,
|
||||||
|
) {
|
||||||
|
|
||||||
|
LazyColumn(verticalArrangement = Arrangement.spacedBy(7.dp)) {
|
||||||
|
|
||||||
|
// All timers
|
||||||
|
items(timers.value) {
|
||||||
|
TimerEntry(
|
||||||
|
timerInfo = it,
|
||||||
|
canDisplay = true,
|
||||||
|
buttonName = R.string.start
|
||||||
|
) { timerInfo ->
|
||||||
|
viewModel.startSession(open, timerInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package be.ugent.sel.studeez.screens.timer_selection
|
||||||
|
|
||||||
|
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
|
||||||
|
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 TimerSelectionViewModel @Inject constructor(
|
||||||
|
private val timerDAO: TimerDAO,
|
||||||
|
private val selectedTimerState: SelectedTimerState,
|
||||||
|
logService: LogService
|
||||||
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
|
fun getAllTimers() : Flow<List<TimerInfo>> {
|
||||||
|
return timerDAO.getAllTimers()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startSession(open: (String) -> Unit, timerInfo: TimerInfo) {
|
||||||
|
selectedTimerState.selectedTimer = timerInfo.getFunctionalTimer()
|
||||||
|
open(StudeezDestinations.SESSION_SCREEN)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
package be.ugent.sel.studeez.screens.timers
|
|
||||||
|
|
||||||
class TimerScreen {
|
|
||||||
}
|
|
|
@ -13,6 +13,7 @@
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="go_back">Go back</string>
|
<string name="go_back">Go back</string>
|
||||||
<string name="next">Next</string>
|
<string name="next">Next</string>
|
||||||
|
<string name="start">Start</string>
|
||||||
|
|
||||||
<!-- Messages -->
|
<!-- Messages -->
|
||||||
<string name="success">Success!</string>
|
<string name="success">Success!</string>
|
||||||
|
@ -63,6 +64,14 @@
|
||||||
<string name="timers">Timers</string>
|
<string name="timers">Timers</string>
|
||||||
<string name="edit">Edit</string>
|
<string name="edit">Edit</string>
|
||||||
<string name="add_timer">Add timer</string>
|
<string name="add_timer">Add timer</string>
|
||||||
|
<string name="state_focus">Focus!</string>
|
||||||
|
<plurals name="state_focus_remaining">
|
||||||
|
<item quantity="zero">Focus one more time!</item>
|
||||||
|
<item quantity="one">Focus! (%d break remaining)</item>
|
||||||
|
<item quantity="other">Focus! (%d breaks remaining)</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="state_done">Done!</string>
|
||||||
|
<string name="state_take_a_break">Take a break!</string>
|
||||||
|
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package be.ugent.sel.studeez
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
fun addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package be.ugent.sel.studeez.timer_functional
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalCustomTimerUnitTest : FunctionalTimerUnitTest() {
|
||||||
|
override fun setTimer() {
|
||||||
|
timer = FunctionalCustomTimer(time)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testOneTick() {
|
||||||
|
timer.tick()
|
||||||
|
Assert.assertEquals(
|
||||||
|
time - 1,
|
||||||
|
timer.time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun multipleTicks() {
|
||||||
|
val n = 10
|
||||||
|
for (i in 1..n) {
|
||||||
|
timer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
time - n,
|
||||||
|
timer.time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testEnded() {
|
||||||
|
timer = FunctionalCustomTimer(0)
|
||||||
|
timer.tick()
|
||||||
|
Assert.assertTrue(timer.hasEnded())
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.DONE,
|
||||||
|
timer.view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package be.ugent.sel.studeez.timer_functional
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalEndlessTimerUnitTest : FunctionalTimerUnitTest() {
|
||||||
|
override fun setTimer() {
|
||||||
|
timer = FunctionalEndlessTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testOneTick() {
|
||||||
|
timer.tick()
|
||||||
|
Assert.assertEquals(
|
||||||
|
1,
|
||||||
|
timer.time.time
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun multipleTicks() {
|
||||||
|
val n = 10
|
||||||
|
for (i in 1..n) {
|
||||||
|
timer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
n,
|
||||||
|
timer.time.time
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testEnded() {
|
||||||
|
val n = 1000
|
||||||
|
for (i in 1..n) {
|
||||||
|
timer.tick()
|
||||||
|
Assert.assertFalse(timer.hasEnded())
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.FOCUS,
|
||||||
|
timer.view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package be.ugent.sel.studeez.timer_functional
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() {
|
||||||
|
private val breakTime = 10
|
||||||
|
private val breaks = 2
|
||||||
|
override val hours = 0
|
||||||
|
override val minutes = 0
|
||||||
|
override val seconds = 10
|
||||||
|
private lateinit var pomodoroTimer: FunctionalPomodoroTimer
|
||||||
|
|
||||||
|
override fun setTimer() {
|
||||||
|
pomodoroTimer = FunctionalPomodoroTimer(time, breakTime, breaks)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testOneTick() {
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
Assert.assertEquals(
|
||||||
|
time - 1,
|
||||||
|
pomodoroTimer.time.time,
|
||||||
|
)
|
||||||
|
Assert.assertFalse(pomodoroTimer.isInBreak)
|
||||||
|
Assert.assertEquals(
|
||||||
|
breaks,
|
||||||
|
pomodoroTimer.breaksRemaining,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.FOCUS,
|
||||||
|
pomodoroTimer.view,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun multipleTicks() {
|
||||||
|
val n = 10
|
||||||
|
for (i in 1..n) {
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
time - n,
|
||||||
|
pomodoroTimer.time.time
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
override fun testEnded() {
|
||||||
|
pomodoroTimer = FunctionalPomodoroTimer(0, 0, 0)
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
Assert.assertTrue(pomodoroTimer.hasEnded())
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.DONE,
|
||||||
|
pomodoroTimer.view,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun switchToBreak() {
|
||||||
|
for (i in 0..10) {
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertFalse(pomodoroTimer.hasEnded())
|
||||||
|
Assert.assertTrue(pomodoroTimer.isInBreak)
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.BREAK,
|
||||||
|
pomodoroTimer.view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun switchToStudying() {
|
||||||
|
for (i in 0..time) {
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertTrue(pomodoroTimer.isInBreak)
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.BREAK,
|
||||||
|
pomodoroTimer.view
|
||||||
|
)
|
||||||
|
for (i in 0..breakTime) {
|
||||||
|
pomodoroTimer.tick()
|
||||||
|
}
|
||||||
|
Assert.assertFalse(pomodoroTimer.isInBreak)
|
||||||
|
val breaksRemaining = breaks - 1
|
||||||
|
Assert.assertEquals(
|
||||||
|
breaksRemaining,
|
||||||
|
pomodoroTimer.breaksRemaining
|
||||||
|
)
|
||||||
|
Assert.assertEquals(
|
||||||
|
FunctionalTimer.StudyState.FOCUS_REMAINING,
|
||||||
|
pomodoroTimer.view
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package be.ugent.sel.studeez.timer_functional
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
abstract class FunctionalTimerUnitTest {
|
||||||
|
protected lateinit var timer: FunctionalTimer
|
||||||
|
protected open val hours = 4
|
||||||
|
protected open val minutes = 20
|
||||||
|
protected open val seconds = 39
|
||||||
|
protected var time: Int = 0
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
time = seconds + minutes * 60 + hours * 60 * 60
|
||||||
|
setTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timer-property should be set to the right implementation in this method.
|
||||||
|
*/
|
||||||
|
abstract fun setTimer()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
abstract fun testOneTick()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
abstract fun multipleTicks()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
abstract fun testEnded()
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package be.ugent.sel.studeez.timer_functional
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.Time
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class TimeUnitTest {
|
||||||
|
private val hours = 4
|
||||||
|
private val minutes = 20
|
||||||
|
private val seconds = 39
|
||||||
|
private val time: Time = Time(seconds + minutes * 60 + hours * 60 * 60)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun formatTime() {
|
||||||
|
Assert.assertEquals(
|
||||||
|
HoursMinutesSeconds(
|
||||||
|
hours.toString().padStart(2, '0'),
|
||||||
|
minutes.toString().padStart(2, '0'),
|
||||||
|
seconds.toString().padStart(2, '0'),
|
||||||
|
),
|
||||||
|
time.getAsHMS(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getTime() {
|
||||||
|
Assert.assertEquals(
|
||||||
|
seconds + minutes * 60 + hours * 60 * 60,
|
||||||
|
time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun minOne() {
|
||||||
|
time.minOne()
|
||||||
|
Assert.assertEquals(
|
||||||
|
(seconds + minutes * 60 + hours * 60 * 60) - 1,
|
||||||
|
time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun plusOne() {
|
||||||
|
time.plusOne()
|
||||||
|
Assert.assertEquals(
|
||||||
|
(seconds + minutes * 60 + hours * 60 * 60) + 1,
|
||||||
|
time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun minMultiple() {
|
||||||
|
val n = 10
|
||||||
|
for (i in 1 .. n) {
|
||||||
|
time.minOne()
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
(seconds + minutes * 60 + hours * 60 * 60) - n,
|
||||||
|
time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun plusMultiple() {
|
||||||
|
val n = 10
|
||||||
|
for (i in 1 .. n) {
|
||||||
|
time.plusOne()
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
(seconds + minutes * 60 + hours * 60 * 60) + n,
|
||||||
|
time.time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue