Merge pull request #91 from SELab1/session_recap

Session recap
This commit is contained in:
Tibo De Peuter 2023-04-28 12:33:30 +02:00 committed by GitHub Enterprise
commit 1d53752a30
14 changed files with 160 additions and 8 deletions

3
.idea/misc.xml generated
View file

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

View file

@ -37,6 +37,7 @@ import be.ugent.sel.studeez.screens.log_in.LoginRoute
import be.ugent.sel.studeez.screens.profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.EditProfileRoute
import be.ugent.sel.studeez.screens.profile.ProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute
import be.ugent.sel.studeez.screens.session.SessionRoute import be.ugent.sel.studeez.screens.session.SessionRoute
import be.ugent.sel.studeez.screens.session_recap.SessionRecapRoute
import be.ugent.sel.studeez.screens.sign_up.SignUpRoute import be.ugent.sel.studeez.screens.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashRoute import be.ugent.sel.studeez.screens.splash.SplashRoute
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute
@ -166,6 +167,7 @@ fun StudeezNavGraph(
composable(StudeezDestinations.SESSION_SCREEN) { composable(StudeezDestinations.SESSION_SCREEN) {
SessionRoute( SessionRoute(
open, open,
openAndPopUp,
viewModel = hiltViewModel() viewModel = hiltViewModel()
) )
} }
@ -189,5 +191,12 @@ fun StudeezNavGraph(
viewModel = hiltViewModel(), viewModel = hiltViewModel(),
) )
} }
composable(StudeezDestinations.SESSION_RECAP) {
SessionRecapRoute(
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel()
)
}
} }
} }

View file

@ -0,0 +1,14 @@
package be.ugent.sel.studeez.data
import be.ugent.sel.studeez.data.local.models.SessionReport
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 SessionReportState @Inject constructor(){
var sessionReport: SessionReport? = null
}

View file

@ -1,5 +1,6 @@
package be.ugent.sel.studeez.data.local.models.timer_functional 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.CustomSessionScreen
import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen
@ -8,6 +9,7 @@ class FunctionalCustomTimer(studyTime: Int) : FunctionalTimer(studyTime) {
override fun tick() { override fun tick() {
if (!hasEnded()) { if (!hasEnded()) {
time.minOne() time.minOne()
totalStudyTime++
} }
} }

View file

@ -12,6 +12,7 @@ class FunctionalEndlessTimer : FunctionalTimer(0) {
override fun tick() { override fun tick() {
time.plusOne() time.plusOne()
totalStudyTime++
} }
override fun <T> accept(visitor: FunctionalTimerVisitor<T>): T { override fun <T> accept(visitor: FunctionalTimerVisitor<T>): T {

View file

@ -26,6 +26,10 @@ class FunctionalPomodoroTimer(
isInBreak = !isInBreak isInBreak = !isInBreak
} }
time.minOne() time.minOne()
if (!isInBreak) {
totalStudyTime++
}
} }
override fun hasEnded(): Boolean { override fun hasEnded(): Boolean {

View file

@ -1,7 +1,12 @@
package be.ugent.sel.studeez.data.local.models.timer_functional 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) { abstract class FunctionalTimer(initialValue: Int) {
val time: Time = Time(initialValue) val time: Time = Time(initialValue)
var totalStudyTime: Int = 0
fun getHoursMinutesSeconds(): HoursMinutesSeconds { fun getHoursMinutesSeconds(): HoursMinutesSeconds {
return time.getAsHMS() return time.getAsHMS()
@ -13,5 +18,12 @@ abstract class FunctionalTimer(initialValue: Int) {
abstract fun hasCurrentCountdownEnded(): Boolean abstract fun hasCurrentCountdownEnded(): Boolean
fun getSessionReport(): SessionReport {
return SessionReport(
studyTime = totalStudyTime,
endTime = Timestamp.now()
)
}
abstract fun <T> accept(visitor: FunctionalTimerVisitor<T>): T abstract fun <T> accept(visitor: FunctionalTimerVisitor<T>): T
} }

View file

@ -9,6 +9,7 @@ object StudeezDestinations {
const val TIMER_OVERVIEW_SCREEN = "timer_overview" const val TIMER_OVERVIEW_SCREEN = "timer_overview"
const val TIMER_SELECTION_SCREEN = "timer_selection" const val TIMER_SELECTION_SCREEN = "timer_selection"
const val SESSION_SCREEN = "session" const val SESSION_SCREEN = "session"
const val SESSION_RECAP = "session_recap"
// const val TASKS_SCREEN = "tasks" // const val TASKS_SCREEN = "tasks"
// const val SESSIONS_SCREEN = "sessions" // const val SESSIONS_SCREEN = "sessions"
const val PROFILE_SCREEN = "profile" const val PROFILE_SCREEN = "profile"

View file

@ -14,23 +14,27 @@ data class SessionActions(
val getTask: () -> String, val getTask: () -> String,
val prepareMediaPlayer: () -> Unit, val prepareMediaPlayer: () -> Unit,
val releaseMediaPlayer: () -> Unit, val releaseMediaPlayer: () -> Unit,
val endSession: () -> Unit
) )
private fun getSessionActions( private fun getSessionActions(
viewModel: SessionViewModel, viewModel: SessionViewModel,
openAndPopUp: (String, String) -> Unit,
mediaplayer: MediaPlayer, mediaplayer: MediaPlayer,
): SessionActions { ): SessionActions {
return SessionActions( return SessionActions(
getTimer = viewModel::getTimer, getTimer = viewModel::getTimer,
getTask = viewModel::getTask, getTask = viewModel::getTask,
endSession = { viewModel.endSession(openAndPopUp) },
prepareMediaPlayer = mediaplayer::prepareAsync, prepareMediaPlayer = mediaplayer::prepareAsync,
releaseMediaPlayer = mediaplayer::release, releaseMediaPlayer = mediaplayer::release
) )
} }
@Composable @Composable
fun SessionRoute( fun SessionRoute(
open: (String) -> Unit, open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SessionViewModel, viewModel: SessionViewModel,
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -58,6 +62,6 @@ fun SessionRoute(
sessionScreen( sessionScreen(
open = open, open = open,
sessionActions = getSessionActions(viewModel, mediaplayer) sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer)
) )
} }

View file

@ -1,20 +1,21 @@
package be.ugent.sel.studeez.screens.session package be.ugent.sel.studeez.screens.session
import be.ugent.sel.studeez.data.SelectedTimerState
import be.ugent.sel.studeez.data.SessionReportState
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.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel import be.ugent.sel.studeez.screens.StudeezViewModel
import be.ugent.sel.studeez.data.SelectedTimerState
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
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, private val selectedTimerState: SelectedTimerState,
private val sessionReportState: SessionReportState,
logService: LogService logService: LogService
) : StudeezViewModel(logService) { ) : StudeezViewModel(logService) {
private val timer: FunctionalTimer = FunctionalPomodoroTimer(15, 5, 3)
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 {
@ -24,4 +25,9 @@ class SessionViewModel @Inject constructor(
fun getTask(): String { fun getTask(): String {
return task return task
} }
fun endSession(openAndPopUp: (String, String) -> Unit) {
sessionReportState.sessionReport = getTimer().getSessionReport()
openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN)
}
} }

View file

@ -47,8 +47,7 @@ abstract class AbstractSessionScreen {
TextButton( TextButton(
onClick = { onClick = {
sessionActions.releaseMediaPlayer sessionActions.releaseMediaPlayer
open(StudeezDestinations.HOME_SCREEN) sessionActions.endSession()
// Vanaf hier ook naar report gaan als "end session" knop word ingedrukt
}, },
modifier = Modifier modifier = Modifier
.padding(horizontal = 20.dp) .padding(horizontal = 20.dp)

View file

@ -0,0 +1,65 @@
package be.ugent.sel.studeez.screens.session_recap
import androidx.compose.foundation.layout.Column
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.data.local.models.SessionReport
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.data.local.models.timer_functional.Time
data class SessionRecapActions(
val getSessionReport: () -> SessionReport,
val saveSession: () -> Unit,
val discardSession: () -> Unit
)
fun getSessionRecapActions(
viewModel: SessionRecapViewModel,
openAndPopUp: (String, String) -> Unit,
): SessionRecapActions {
return SessionRecapActions(
viewModel::getSessionReport,
{viewModel.saveSession(openAndPopUp)},
{viewModel.saveSession(openAndPopUp)}
)
}
@Composable
fun SessionRecapRoute(
openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier,
viewModel: SessionRecapViewModel,
) {
SessionRecapScreen(
modifier = modifier,
getSessionRecapActions(viewModel, openAndPopUp)
)
}
@Composable
fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActions) {
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}")
BasicButton(
R.string.save, Modifier.basicButton()
) {
sessionRecapActions.saveSession()
}
BasicButton(
R.string.discard, Modifier.basicButton(),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
) {
sessionRecapActions.discardSession()
}
}
}

View file

@ -0,0 +1,33 @@
package be.ugent.sel.studeez.screens.session_recap
import be.ugent.sel.studeez.data.SessionReportState
import be.ugent.sel.studeez.data.local.models.SessionReport
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.SessionDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class SessionRecapViewModel @Inject constructor(
private val sessionReportState: SessionReportState,
private val sessionDAO: SessionDAO,
logService: LogService
) : StudeezViewModel(logService) {
private val report: SessionReport = sessionReportState.sessionReport!!
fun getSessionReport(): SessionReport {
return report
}
fun saveSession(open: (String, String) -> Unit) {
sessionDAO.saveSession(getSessionReport())
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
}
fun discardSession(open: (String, String) -> Unit) {
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
}
}

View file

@ -10,6 +10,7 @@
<!-- Actions --> <!-- Actions -->
<string name="confirm">Confirm</string> <string name="confirm">Confirm</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="discard">Discard</string>
<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>