mediaplayer out of view
This commit is contained in:
		
						commit
						46d60e100b
					
				
					 16 changed files with 326 additions and 271 deletions
				
			
		|  | @ -3,19 +3,27 @@ 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.time == 0) { |         if (!hasEnded()) { | ||||||
|             view = StudyState.DONE |  | ||||||
|         } else { |  | ||||||
|             time.minOne() |             time.minOne() | ||||||
|  |         } else { | ||||||
|  |             mediaPlayer?.setOnCompletionListener { | ||||||
|  |                 mediaPlayer!!.release() | ||||||
|  |                 mediaPlayer = null | ||||||
|  |             } | ||||||
|  |             mediaPlayer?.start() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun hasEnded(): Boolean { |     override fun hasEnded(): Boolean { | ||||||
|         return view == StudyState.DONE |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun hasCurrentCountdownEnded(): Boolean { |  | ||||||
|         return time.time == 0 |         return time.time == 0 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override fun hasCurrentCountdownEnded(): Boolean { | ||||||
|  |         return hasEnded() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T> accept(visitor: FunctionalTimerVisitor<T>): T { | ||||||
|  |         return visitor.visitFunctionalCustomTimer(this) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | @ -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 | ||||||
|  | @ -13,4 +13,8 @@ class FunctionalEndlessTimer() : FunctionalTimer(0) { | ||||||
|     override fun tick() { |     override fun tick() { | ||||||
|         time.plusOne() |         time.plusOne() | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T> accept(visitor: FunctionalTimerVisitor<T>): T { | ||||||
|  |         return visitor.visitFunctionalEndlessTimer(this) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -9,18 +9,19 @@ class FunctionalPomodoroTimer( | ||||||
|     var isInBreak = false |     var isInBreak = false | ||||||
| 
 | 
 | ||||||
|     override fun tick() { |     override fun tick() { | ||||||
|         if (time.time == 0 && breaksRemaining == 0) { |         if (hasEnded()) { | ||||||
|             view = StudyState.DONE |             mediaPlayer?.setOnCompletionListener { | ||||||
|  |                 mediaPlayer!!.release() | ||||||
|  |                 mediaPlayer = null | ||||||
|  |             } | ||||||
|  |             mediaPlayer?.start() | ||||||
|             return |             return | ||||||
|         } |         } else if (hasCurrentCountdownEnded()) { | ||||||
| 
 |             mediaPlayer?.start() | ||||||
|         if (time.time == 0) { |  | ||||||
|             if (isInBreak) { |             if (isInBreak) { | ||||||
|                 breaksRemaining-- |                 breaksRemaining-- | ||||||
|                 view = StudyState.FOCUS_REMAINING |  | ||||||
|                 time.time = studyTime |                 time.time = studyTime | ||||||
|             } else { |             } else { | ||||||
|                 view = StudyState.BREAK |  | ||||||
|                 time.time = breakTime |                 time.time = breakTime | ||||||
|             } |             } | ||||||
|             isInBreak = !isInBreak |             isInBreak = !isInBreak | ||||||
|  | @ -29,10 +30,18 @@ class FunctionalPomodoroTimer( | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun hasEnded(): Boolean { |     override fun hasEnded(): Boolean { | ||||||
|         return breaksRemaining == 0 && time.time == 0 |         return !hasBreaksRemaining() && hasCurrentCountdownEnded() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun hasBreaksRemaining(): Boolean { | ||||||
|  |         return breaksRemaining > 0 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun hasCurrentCountdownEnded(): Boolean { |     override fun hasCurrentCountdownEnded(): Boolean { | ||||||
|         return time.time == 0 |         return time.time == 0 | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T> accept(visitor: FunctionalTimerVisitor<T>): T { | ||||||
|  |         return visitor.visitFunctionalBreakTimer(this) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
| package be.ugent.sel.studeez.data.local.models.timer_functional | package be.ugent.sel.studeez.data.local.models.timer_functional | ||||||
| 
 | 
 | ||||||
|  | import android.media.MediaPlayer | ||||||
|  | 
 | ||||||
| abstract class FunctionalTimer(initialValue: Int) { | abstract class FunctionalTimer(initialValue: Int) { | ||||||
|     var time: Time = Time(initialValue) |     val time: Time = Time(initialValue) | ||||||
|     var view: StudyState = StudyState.FOCUS |     var mediaPlayer: MediaPlayer? = null | ||||||
| 
 | 
 | ||||||
|     fun getHoursMinutesSeconds(): HoursMinutesSeconds { |     fun getHoursMinutesSeconds(): HoursMinutesSeconds { | ||||||
|         return time.getAsHMS() |         return time.getAsHMS() | ||||||
|  | @ -14,8 +16,5 @@ abstract class FunctionalTimer(initialValue: Int) { | ||||||
| 
 | 
 | ||||||
|     abstract fun hasCurrentCountdownEnded(): Boolean |     abstract fun hasCurrentCountdownEnded(): Boolean | ||||||
| 
 | 
 | ||||||
|     enum class StudyState { |     abstract fun <T> accept(visitor: FunctionalTimerVisitor<T>): T | ||||||
|         FOCUS, DONE, BREAK, FOCUS_REMAINING |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | package be.ugent.sel.studeez.data.local.models.timer_functional | ||||||
|  | 
 | ||||||
|  | interface FunctionalTimerVisitor<T> { | ||||||
|  | 
 | ||||||
|  |     fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): T | ||||||
|  | 
 | ||||||
|  |     fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): T | ||||||
|  | 
 | ||||||
|  |     fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): T | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| package be.ugent.sel.studeez.screens.session | package be.ugent.sel.studeez.screens.session | ||||||
| 
 | 
 | ||||||
| import android.media.MediaPlayer |  | ||||||
| import kotlinx.coroutines.delay | import kotlinx.coroutines.delay | ||||||
| import javax.inject.Singleton | import javax.inject.Singleton | ||||||
| import kotlin.time.Duration.Companion.seconds | import kotlin.time.Duration.Companion.seconds | ||||||
|  | @ -8,11 +7,9 @@ import kotlin.time.Duration.Companion.seconds | ||||||
| @Singleton | @Singleton | ||||||
| object InvisibleSessionManager { | object InvisibleSessionManager { | ||||||
|     private var viewModel: SessionViewModel? = null |     private var viewModel: SessionViewModel? = null | ||||||
|     private var mediaplayer: MediaPlayer? = null |  | ||||||
| 
 | 
 | ||||||
|     fun setParameters(viewModel: SessionViewModel, mediaplayer: MediaPlayer) { |     fun setParameters(viewModel: SessionViewModel) { | ||||||
|         this.viewModel = viewModel |         this.viewModel = viewModel | ||||||
|         this.mediaplayer = mediaplayer |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun updateTimer() { |     suspend fun updateTimer() { | ||||||
|  | @ -20,15 +17,7 @@ object InvisibleSessionManager { | ||||||
|             while (true) { |             while (true) { | ||||||
|                 delay(1.seconds) |                 delay(1.seconds) | ||||||
|                 viewModel!!.getTimer().tick() |                 viewModel!!.getTimer().tick() | ||||||
|                 if (viewModel!!.getTimer().hasCurrentCountdownEnded() && !viewModel!!.getTimer().hasEnded()) { |  | ||||||
|                     mediaplayer?.start() |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fun removeParameters() { |  | ||||||
|         viewModel = null |  | ||||||
|         mediaplayer = null |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | @ -0,0 +1,53 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session | ||||||
|  | 
 | ||||||
|  | import android.media.MediaPlayer | ||||||
|  | import android.media.RingtoneManager | ||||||
|  | import android.net.Uri | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.platform.LocalContext | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer | ||||||
|  | import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen | ||||||
|  | import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreen | ||||||
|  | 
 | ||||||
|  | data class SessionActions( | ||||||
|  |     val getTimer: () -> FunctionalTimer, | ||||||
|  |     val getTask: () -> String, | ||||||
|  |     val startMediaPlayer: () -> Unit, | ||||||
|  |     val releaseMediaPlayer: () -> Unit, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | private fun getSessionActions( | ||||||
|  |     viewModel: SessionViewModel, | ||||||
|  |     mediaplayer: MediaPlayer, | ||||||
|  | ): SessionActions { | ||||||
|  |     return SessionActions( | ||||||
|  |         getTimer = viewModel::getTimer, | ||||||
|  |         getTask = viewModel::getTask, | ||||||
|  |         startMediaPlayer = mediaplayer::start, | ||||||
|  |         releaseMediaPlayer = mediaplayer::release, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun SessionRoute( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     viewModel: SessionViewModel, | ||||||
|  | ) { | ||||||
|  |     val context = LocalContext.current | ||||||
|  |     val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) | ||||||
|  |     val mediaplayer = MediaPlayer.create(context, uri) | ||||||
|  |     mediaplayer.isLooping = false | ||||||
|  | 
 | ||||||
|  |     viewModel.getTimer().mediaPlayer = mediaplayer | ||||||
|  | 
 | ||||||
|  |     InvisibleSessionManager.setParameters( | ||||||
|  |         viewModel = viewModel | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen()) | ||||||
|  | 
 | ||||||
|  |     sessionScreen( | ||||||
|  |         open = open, | ||||||
|  |         sessionActions = getSessionActions(viewModel, mediaplayer) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -1,206 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.session |  | ||||||
| 
 |  | ||||||
| import android.media.MediaPlayer |  | ||||||
| import android.media.RingtoneManager |  | ||||||
| import android.net.Uri |  | ||||||
| import androidx.compose.foundation.background |  | ||||||
| import androidx.compose.foundation.border |  | ||||||
| import androidx.compose.foundation.layout.Box |  | ||||||
| import androidx.compose.foundation.layout.Column |  | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth |  | ||||||
| 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.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.graphics.Color |  | ||||||
| import androidx.compose.ui.platform.LocalContext |  | ||||||
| import androidx.compose.ui.text.font.FontWeight |  | ||||||
| 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.navigation.StudeezDestinations |  | ||||||
| import androidx.compose.ui.unit.sp |  | ||||||
| import be.ugent.sel.studeez.R |  | ||||||
| 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.FunctionalTimer |  | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer.StudyState |  | ||||||
| import be.ugent.sel.studeez.resources |  | ||||||
| import kotlinx.coroutines.delay |  | ||||||
| import kotlin.time.Duration.Companion.seconds |  | ||||||
| 
 |  | ||||||
| data class SessionActions( |  | ||||||
|     val getTimer: () -> FunctionalTimer, |  | ||||||
|     val getTask: () -> String, |  | ||||||
|     val releaseMediaPlayer: () -> Unit, |  | ||||||
|     val startMediaPlayer: () -> Unit, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| fun getSessionActions( |  | ||||||
|     viewModel: SessionViewModel, |  | ||||||
|     mediaplayer: MediaPlayer, |  | ||||||
| ): SessionActions { |  | ||||||
|     return SessionActions( |  | ||||||
|         getTimer = viewModel::getTimer, |  | ||||||
|         getTask = viewModel::getTask, |  | ||||||
|         releaseMediaPlayer = mediaplayer::release, |  | ||||||
|         startMediaPlayer = mediaplayer::start, |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun SessionRoute( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     viewModel: SessionViewModel, |  | ||||||
| ) { |  | ||||||
|     val context = LocalContext.current |  | ||||||
|     val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) |  | ||||||
|     val mediaplayer = MediaPlayer.create(context, uri) |  | ||||||
|     mediaplayer.isLooping = false |  | ||||||
| 
 |  | ||||||
|     InvisibleSessionManager.setParameters( |  | ||||||
|         viewModel = viewModel, |  | ||||||
|         mediaplayer = mediaplayer |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     SessionScreen( |  | ||||||
|         open = open, |  | ||||||
|         sessionActions = getSessionActions(viewModel, mediaplayer) |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun SessionScreen( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     sessionActions: SessionActions |  | ||||||
| ) { |  | ||||||
|     Column( |  | ||||||
|         modifier = Modifier.padding(10.dp) |  | ||||||
|     ) { |  | ||||||
|         Timer( |  | ||||||
|             sessionActions = sessionActions |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         Box( |  | ||||||
|             contentAlignment = Alignment.Center, |  | ||||||
|             modifier = Modifier |  | ||||||
|                 .fillMaxWidth() |  | ||||||
|                 .padding(50.dp) |  | ||||||
|         ) { |  | ||||||
|             TextButton( |  | ||||||
|                 onClick = { |  | ||||||
|                     sessionActions.releaseMediaPlayer |  | ||||||
|                     InvisibleSessionManager.removeParameters() |  | ||||||
|                     open(StudeezDestinations.HOME_SCREEN) |  | ||||||
|                     // Vanaf hier ook naar report gaan als "end session" knop word ingedrukt |  | ||||||
|                 }, |  | ||||||
|                 modifier = Modifier |  | ||||||
|                     .padding(horizontal = 20.dp) |  | ||||||
|                     .border(1.dp, Color.Red, RoundedCornerShape(32.dp)) |  | ||||||
|                     .background(Color.Transparent) |  | ||||||
|             ) { |  | ||||||
|                 Text( |  | ||||||
|                     text = resources().getString(R.string.end_session), |  | ||||||
|                     color = Color.Red, |  | ||||||
|                     fontWeight = FontWeight.Bold, |  | ||||||
|                     fontSize = 18.sp, |  | ||||||
|                     modifier = Modifier |  | ||||||
|                         .padding(1.dp) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| private fun Timer( |  | ||||||
|     sessionActions: SessionActions |  | ||||||
| ) { |  | ||||||
|     var tikker by remember { mutableStateOf(false) } |  | ||||||
|     LaunchedEffect(tikker) { |  | ||||||
|         delay(1.seconds) |  | ||||||
|         sessionActions.getTimer().tick() |  | ||||||
|         tikker = !tikker |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (sessionActions.getTimer().hasCurrentCountdownEnded() && !sessionActions.getTimer().hasEnded()) { |  | ||||||
|         sessionActions.startMediaPlayer |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     val hms = sessionActions.getTimer().getHoursMinutesSeconds() |  | ||||||
|     Column { |  | ||||||
|         Text( |  | ||||||
|             text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}", |  | ||||||
|             modifier = Modifier |  | ||||||
|                 .fillMaxWidth() |  | ||||||
|                 .padding(50.dp), |  | ||||||
|             textAlign = TextAlign.Center, |  | ||||||
|             fontWeight = FontWeight.Bold, |  | ||||||
|             fontSize = 40.sp, |  | ||||||
|         ) |  | ||||||
|         val stateString: String = when (sessionActions.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 -> |  | ||||||
|                 (sessionActions.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let { |  | ||||||
|                     resources().getQuantityString(R.plurals.state_focus_remaining, it, it) |  | ||||||
|                 }.toString() |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Text( |  | ||||||
|             text = stateString, |  | ||||||
|             modifier = Modifier.fillMaxWidth(), |  | ||||||
|             textAlign = TextAlign.Center, |  | ||||||
|             fontWeight = FontWeight.Light, |  | ||||||
|             fontSize = 30.sp |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         Box( |  | ||||||
|             contentAlignment = Alignment.Center, |  | ||||||
|             modifier = Modifier |  | ||||||
|                 .fillMaxWidth() |  | ||||||
|                 .padding(50.dp) |  | ||||||
|         ) { |  | ||||||
|             Box( |  | ||||||
|                 contentAlignment = Alignment.Center, |  | ||||||
|                 modifier = Modifier |  | ||||||
|                     .padding(16.dp) |  | ||||||
|                     .background(Color.Blue, RoundedCornerShape(32.dp)) |  | ||||||
|             ) { |  | ||||||
|                 Text( |  | ||||||
|                     text = sessionActions.getTask(), |  | ||||||
|                     color = Color.White, |  | ||||||
|                     fontSize = 18.sp, |  | ||||||
|                     fontWeight = FontWeight.Bold, |  | ||||||
|                     modifier = Modifier |  | ||||||
|                         .padding(vertical = 4.dp, horizontal = 20.dp) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun TimerPreview() { |  | ||||||
|     Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {})) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun SessionPreview() { |  | ||||||
|     SessionScreen( |  | ||||||
|         open = {}, |  | ||||||
|         sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}) |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | @ -0,0 +1,136 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.border | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.Column | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | 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.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.text.font.FontWeight | ||||||
|  | import androidx.compose.ui.text.style.TextAlign | ||||||
|  | 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.navigation.StudeezDestinations | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | import kotlinx.coroutines.delay | ||||||
|  | import kotlin.time.Duration.Companion.seconds | ||||||
|  | 
 | ||||||
|  | abstract class AbstractSessionScreen { | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     operator fun invoke( | ||||||
|  |         open: (String) -> Unit, | ||||||
|  |         sessionActions: SessionActions, | ||||||
|  |     ) { | ||||||
|  |         Column( | ||||||
|  |             modifier = Modifier.padding(10.dp) | ||||||
|  |         ) { | ||||||
|  |             Timer( | ||||||
|  |                 sessionActions = sessionActions, | ||||||
|  |             ) | ||||||
|  |             Box( | ||||||
|  |                 contentAlignment = Alignment.Center, modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .padding(50.dp) | ||||||
|  |             ) { | ||||||
|  |                 TextButton( | ||||||
|  |                     onClick = { | ||||||
|  |                         sessionActions.releaseMediaPlayer | ||||||
|  |                         open(StudeezDestinations.HOME_SCREEN) | ||||||
|  |                         // Vanaf hier ook naar report gaan als "end session" knop word ingedrukt | ||||||
|  |                     }, | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .padding(horizontal = 20.dp) | ||||||
|  |                         .border(1.dp, Color.Red, RoundedCornerShape(32.dp)) | ||||||
|  |                         .background(Color.Transparent) | ||||||
|  |                 ) { | ||||||
|  |                     Text( | ||||||
|  |                         text = "End session", | ||||||
|  |                         color = Color.Red, | ||||||
|  |                         fontWeight = FontWeight.Bold, | ||||||
|  |                         fontSize = 18.sp, | ||||||
|  |                         modifier = Modifier.padding(1.dp) | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     fun Timer( | ||||||
|  |         sessionActions: SessionActions, | ||||||
|  |     ) { | ||||||
|  |         var tikker by remember { mutableStateOf(false) } | ||||||
|  |         LaunchedEffect(tikker) { | ||||||
|  |             delay(1.seconds) | ||||||
|  |             sessionActions.getTimer().tick() | ||||||
|  |             tikker = !tikker | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         val hms = sessionActions.getTimer().getHoursMinutesSeconds() | ||||||
|  |         Column { | ||||||
|  |             Text( | ||||||
|  |                 text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}", | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .padding(50.dp), | ||||||
|  |                 textAlign = TextAlign.Center, | ||||||
|  |                 fontWeight = FontWeight.Bold, | ||||||
|  |                 fontSize = 40.sp, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             Text( | ||||||
|  |                 text = motivationString(), | ||||||
|  |                 modifier = Modifier.fillMaxWidth(), | ||||||
|  |                 textAlign = TextAlign.Center, | ||||||
|  |                 fontWeight = FontWeight.Light, | ||||||
|  |                 fontSize = 30.sp | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             Box( | ||||||
|  |                 contentAlignment = Alignment.Center, modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .padding(50.dp) | ||||||
|  |             ) { | ||||||
|  |                 Box( | ||||||
|  |                     contentAlignment = Alignment.Center, | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .padding(16.dp) | ||||||
|  |                         .background(Color.Blue, RoundedCornerShape(32.dp)) | ||||||
|  |                 ) { | ||||||
|  |                     Text( | ||||||
|  |                         text = sessionActions.getTask(), | ||||||
|  |                         color = Color.White, | ||||||
|  |                         fontSize = 18.sp, | ||||||
|  |                         fontWeight = FontWeight.Bold, | ||||||
|  |                         modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp) | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     abstract fun motivationString(): String | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun TimerPreview() { | ||||||
|  |     val sessionScreen = object : AbstractSessionScreen() { | ||||||
|  |         @Composable | ||||||
|  |         override fun motivationString(): String = "Test" | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     sessionScreen.Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {})) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import be.ugent.sel.studeez.R | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
|  | 
 | ||||||
|  | class BreakSessionScreen( | ||||||
|  |     private val funPomoDoroTimer: FunctionalPomodoroTimer | ||||||
|  | ): AbstractSessionScreen() { | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     override fun motivationString(): String { | ||||||
|  |         if (funPomoDoroTimer.isInBreak) { | ||||||
|  |             return resources().getString(AppText.state_take_a_break) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (funPomoDoroTimer.hasEnded()) { | ||||||
|  |             return resources().getString(AppText.state_done) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return resources().getQuantityString( | ||||||
|  |             R.plurals.state_focus_remaining, | ||||||
|  |             funPomoDoroTimer.breaksRemaining, | ||||||
|  |             funPomoDoroTimer.breaksRemaining | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CustomSessionScreen( | ||||||
|  |     private val functionalTimer: FunctionalCustomTimer | ||||||
|  | ): AbstractSessionScreen() { | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     override fun motivationString(): String { | ||||||
|  |         if (functionalTimer.hasEnded()) { | ||||||
|  |             return resources().getString(AppText.state_done) | ||||||
|  |         } | ||||||
|  |         return resources().getString(AppText.state_focus) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class EndlessSessionScreen : AbstractSessionScreen() { | ||||||
|  | 
 | ||||||
|  |     @Composable | ||||||
|  |     override fun motivationString(): String { | ||||||
|  |         return resources().getString(AppText.state_focus) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | 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> { | ||||||
|  |     override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): AbstractSessionScreen = | ||||||
|  |         CustomSessionScreen(functionalCustomTimer) | ||||||
|  | 
 | ||||||
|  |     override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): AbstractSessionScreen = | ||||||
|  |         EndlessSessionScreen() | ||||||
|  | 
 | ||||||
|  |     override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): AbstractSessionScreen = | ||||||
|  |         BreakSessionScreen(functionalPomodoroTimer) | ||||||
|  | } | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.timer_functional | 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.FunctionalCustomTimer | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer |  | ||||||
| import org.junit.Assert | import org.junit.Assert | ||||||
| import org.junit.Test | import org.junit.Test | ||||||
| 
 | 
 | ||||||
|  | @ -36,9 +35,6 @@ class FunctionalCustomTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|         timer = FunctionalCustomTimer(0) |         timer = FunctionalCustomTimer(0) | ||||||
|         timer.tick() |         timer.tick() | ||||||
|         Assert.assertTrue(timer.hasEnded()) |         Assert.assertTrue(timer.hasEnded()) | ||||||
|         Assert.assertEquals( |         Assert.assertTrue(timer.hasEnded()) | ||||||
|             FunctionalTimer.StudyState.DONE, |  | ||||||
|             timer.view |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.timer_functional | 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.FunctionalEndlessTimer | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer |  | ||||||
| import org.junit.Assert | import org.junit.Assert | ||||||
| import org.junit.Test | import org.junit.Test | ||||||
| 
 | 
 | ||||||
|  | @ -37,10 +36,6 @@ class FunctionalEndlessTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|         for (i in 1..n) { |         for (i in 1..n) { | ||||||
|             timer.tick() |             timer.tick() | ||||||
|             Assert.assertFalse(timer.hasEnded()) |             Assert.assertFalse(timer.hasEnded()) | ||||||
|             Assert.assertEquals( |  | ||||||
|                 FunctionalTimer.StudyState.FOCUS, |  | ||||||
|                 timer.view |  | ||||||
|             ) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.timer_functional | 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.FunctionalPomodoroTimer | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer |  | ||||||
| import org.junit.Assert | import org.junit.Assert | ||||||
| import org.junit.Test | import org.junit.Test | ||||||
| 
 | 
 | ||||||
|  | @ -29,10 +28,6 @@ class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|             breaks, |             breaks, | ||||||
|             pomodoroTimer.breaksRemaining, |             pomodoroTimer.breaksRemaining, | ||||||
|         ) |         ) | ||||||
|         Assert.assertEquals( |  | ||||||
|             FunctionalTimer.StudyState.FOCUS, |  | ||||||
|             pomodoroTimer.view, |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|  | @ -52,10 +47,6 @@ class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|         pomodoroTimer = FunctionalPomodoroTimer(0, 0, 0) |         pomodoroTimer = FunctionalPomodoroTimer(0, 0, 0) | ||||||
|         pomodoroTimer.tick() |         pomodoroTimer.tick() | ||||||
|         Assert.assertTrue(pomodoroTimer.hasEnded()) |         Assert.assertTrue(pomodoroTimer.hasEnded()) | ||||||
|         Assert.assertEquals( |  | ||||||
|             FunctionalTimer.StudyState.DONE, |  | ||||||
|             pomodoroTimer.view, |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|  | @ -65,10 +56,6 @@ class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|         } |         } | ||||||
|         Assert.assertFalse(pomodoroTimer.hasEnded()) |         Assert.assertFalse(pomodoroTimer.hasEnded()) | ||||||
|         Assert.assertTrue(pomodoroTimer.isInBreak) |         Assert.assertTrue(pomodoroTimer.isInBreak) | ||||||
|         Assert.assertEquals( |  | ||||||
|             FunctionalTimer.StudyState.BREAK, |  | ||||||
|             pomodoroTimer.view |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|  | @ -77,10 +64,6 @@ class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|             pomodoroTimer.tick() |             pomodoroTimer.tick() | ||||||
|         } |         } | ||||||
|         Assert.assertTrue(pomodoroTimer.isInBreak) |         Assert.assertTrue(pomodoroTimer.isInBreak) | ||||||
|         Assert.assertEquals( |  | ||||||
|             FunctionalTimer.StudyState.BREAK, |  | ||||||
|             pomodoroTimer.view |  | ||||||
|         ) |  | ||||||
|         for (i in 0..breakTime) { |         for (i in 0..breakTime) { | ||||||
|             pomodoroTimer.tick() |             pomodoroTimer.tick() | ||||||
|         } |         } | ||||||
|  | @ -90,9 +73,5 @@ class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { | ||||||
|             breaksRemaining, |             breaksRemaining, | ||||||
|             pomodoroTimer.breaksRemaining |             pomodoroTimer.breaksRemaining | ||||||
|         ) |         ) | ||||||
|         Assert.assertEquals( |  | ||||||
|             FunctionalTimer.StudyState.FOCUS_REMAINING, |  | ||||||
|             pomodoroTimer.view |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
		Reference in a new issue
	
	 Rune Dyselinck
						Rune Dyselinck