Merge pull request #120 from SELab1/abstract2composition_refactor_session_screen
Abstract2composition refactor session screen
This commit is contained in:
		
						commit
						19a073f8c6
					
				
					 14 changed files with 393 additions and 340 deletions
				
			
		|  | @ -1,6 +1,10 @@ | ||||||
| package be.ugent.sel.studeez.screens.session | package be.ugent.sel.studeez.screens.session | ||||||
| 
 | 
 | ||||||
|  | import android.annotation.SuppressLint | ||||||
|  | import android.content.Context | ||||||
| import android.media.MediaPlayer | import android.media.MediaPlayer | ||||||
|  | import android.media.RingtoneManager | ||||||
|  | import android.net.Uri | ||||||
| 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 | ||||||
|  | @ -10,9 +14,11 @@ object InvisibleSessionManager { | ||||||
|     private var viewModel: SessionViewModel? = null |     private var viewModel: SessionViewModel? = null | ||||||
|     private lateinit var mediaPlayer: MediaPlayer |     private lateinit var mediaPlayer: MediaPlayer | ||||||
| 
 | 
 | ||||||
|     fun setParameters(viewModel: SessionViewModel, mediaplayer: MediaPlayer) { |     fun setParameters(viewModel: SessionViewModel, context: Context) { | ||||||
|  |         val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) | ||||||
|  |         this.mediaPlayer = MediaPlayer.create(context, uri) | ||||||
|  |         this.mediaPlayer.isLooping = false | ||||||
|         this.viewModel = viewModel |         this.viewModel = viewModel | ||||||
|         this.mediaPlayer = mediaplayer |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun updateTimer() { |     suspend fun updateTimer() { | ||||||
|  |  | ||||||
|  | @ -1,33 +1,24 @@ | ||||||
| package be.ugent.sel.studeez.screens.session | 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.runtime.Composable | ||||||
| import androidx.compose.ui.platform.LocalContext | import androidx.compose.ui.platform.LocalContext | ||||||
| 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.screens.session.sessionScreens.AbstractSessionScreen | import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreenComposable | ||||||
| import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreen |  | ||||||
| 
 | 
 | ||||||
| data class SessionActions( | data class SessionActions( | ||||||
|     val getTimer: () -> FunctionalTimer, |     val getTimer: () -> FunctionalTimer, | ||||||
|     val getTask: () -> String, |     val getTask: () -> String, | ||||||
|     val startMediaPlayer: () -> Unit, |  | ||||||
|     val releaseMediaPlayer: () -> Unit, |  | ||||||
|     val endSession: () -> Unit |     val endSession: () -> Unit | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| private fun getSessionActions( | private fun getSessionActions( | ||||||
|     viewModel: SessionViewModel, |     viewModel: SessionViewModel, | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     mediaplayer: MediaPlayer, |  | ||||||
| ): SessionActions { | ): SessionActions { | ||||||
|     return SessionActions( |     return SessionActions( | ||||||
|         getTimer = viewModel::getTimer, |         getTimer = viewModel::getTimer, | ||||||
|         getTask = viewModel::getTask, |         getTask = viewModel::getTask, | ||||||
|         endSession = { viewModel.endSession(openAndPopUp) }, |         endSession = { viewModel.endSession(openAndPopUp) }, | ||||||
|         startMediaPlayer = mediaplayer::start, |  | ||||||
|         releaseMediaPlayer = mediaplayer::release, |  | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -37,20 +28,12 @@ fun SessionRoute( | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     viewModel: SessionViewModel, |     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( |     InvisibleSessionManager.setParameters(viewModel = viewModel, context = LocalContext.current) | ||||||
|         viewModel = viewModel, |  | ||||||
|         mediaplayer = mediaplayer |  | ||||||
|     ) |  | ||||||
| 
 | 
 | ||||||
|     val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen(mediaplayer)) |     val soundPlayer = SoundPlayer(LocalContext.current) | ||||||
|  |     val sessionActions = getSessionActions(viewModel, openAndPopUp) | ||||||
|  |     val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(soundPlayer, open, sessionActions)) | ||||||
| 
 | 
 | ||||||
|     sessionScreen( |     sessionScreen() | ||||||
|         open = open, |  | ||||||
|         sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) |  | ||||||
|     ) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,29 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session | ||||||
|  | 
 | ||||||
|  | import android.content.Context | ||||||
|  | import android.media.MediaPlayer | ||||||
|  | import android.media.RingtoneManager | ||||||
|  | 
 | ||||||
|  | class SoundPlayer(private val context: Context) { | ||||||
|  | 
 | ||||||
|  |     var oldValue: Boolean = false | ||||||
|  |     var mediaPlayer: MediaPlayer = initPlayer() | ||||||
|  | 
 | ||||||
|  |     fun playOn(newValue: Boolean) { | ||||||
|  |         if (oldValue != newValue) { | ||||||
|  |             mediaPlayer.start() | ||||||
|  |             mediaPlayer.setOnCompletionListener { | ||||||
|  |                 mediaPlayer = initPlayer() | ||||||
|  |             } | ||||||
|  |             oldValue = newValue | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     private fun initPlayer(): MediaPlayer { | ||||||
|  |         return  MediaPlayer.create( | ||||||
|  |             context, | ||||||
|  |             RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,150 +0,0 @@ | ||||||
| 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.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 |  | ||||||
| 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.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 |  | ||||||
|                         sessionActions.endSession() |  | ||||||
|                     }, |  | ||||||
|                     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() |  | ||||||
|             callMediaPlayer() |  | ||||||
|             tikker = !tikker |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         val hms = sessionActions.getTimer().getHoursMinutesSeconds() |  | ||||||
|         Column { |  | ||||||
|             Text( |  | ||||||
|                 text = hms.toString(), |  | ||||||
|                 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 |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|             MidSection() |  | ||||||
| 
 |  | ||||||
|             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 |  | ||||||
| 
 |  | ||||||
|     @Composable |  | ||||||
|     open fun MidSection() { |  | ||||||
|         // Default has no midsection, unless overwritten. |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     abstract fun callMediaPlayer() |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun TimerPreview() { |  | ||||||
|     val sessionScreen = object : AbstractSessionScreen() { |  | ||||||
|         @Composable |  | ||||||
|         override fun motivationString(): String = "Test" |  | ||||||
|         override fun callMediaPlayer() {} |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
|     sessionScreen.Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}, {})) |  | ||||||
| } |  | ||||||
|  | @ -1,96 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.session.sessionScreens |  | ||||||
| 
 |  | ||||||
| import android.media.MediaPlayer |  | ||||||
| import androidx.compose.foundation.background |  | ||||||
| import androidx.compose.foundation.layout.* |  | ||||||
| import androidx.compose.foundation.shape.CircleShape |  | ||||||
| import androidx.compose.runtime.Composable |  | ||||||
| import androidx.compose.ui.Alignment |  | ||||||
| import androidx.compose.ui.Modifier |  | ||||||
| import androidx.compose.ui.draw.clip |  | ||||||
| import androidx.compose.ui.graphics.Color |  | ||||||
| import androidx.compose.ui.tooling.preview.Preview |  | ||||||
| import androidx.compose.ui.unit.dp |  | ||||||
| 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, |  | ||||||
|     private var mediaplayer: MediaPlayer? |  | ||||||
| ) : AbstractSessionScreen() { |  | ||||||
| 
 |  | ||||||
|     @Composable |  | ||||||
|     override fun MidSection() { |  | ||||||
|         Dots() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Composable |  | ||||||
|     fun Dots() { |  | ||||||
|         Row( |  | ||||||
|             modifier = Modifier.fillMaxWidth(), |  | ||||||
|             verticalAlignment = Alignment.CenterVertically, |  | ||||||
|             horizontalArrangement = Arrangement.Center, |  | ||||||
|         ) { |  | ||||||
|             if (funPomoDoroTimer.hasEnded()) { |  | ||||||
|                 repeat(funPomoDoroTimer.repeats) { |  | ||||||
|                     Dot(Color.Green) |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) { |  | ||||||
|                     Dot(color = Color.DarkGray) |  | ||||||
|                 } |  | ||||||
|                 if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) |  | ||||||
|                 repeat(funPomoDoroTimer.breaksRemaining) { |  | ||||||
|                     Dot(color = Color.Gray) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Composable |  | ||||||
|     private fun Dot(color: Color) { |  | ||||||
|         Box( |  | ||||||
|             modifier = Modifier |  | ||||||
|                 .padding(5.dp) |  | ||||||
|                 .size(10.dp) |  | ||||||
|                 .clip(CircleShape) |  | ||||||
|                 .background(color) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @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().getString(AppText.state_focus) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun callMediaPlayer() { |  | ||||||
|         if (funPomoDoroTimer.hasEnded()) { |  | ||||||
|             mediaplayer?.let { it: MediaPlayer -> |  | ||||||
|                 it.setOnCompletionListener { |  | ||||||
|                     it.release() |  | ||||||
|                     mediaplayer = null |  | ||||||
|                 } |  | ||||||
|                 it.start() |  | ||||||
|             } |  | ||||||
|         } else if (funPomoDoroTimer.hasCurrentCountdownEnded()) { |  | ||||||
|             mediaplayer?.start() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun MidsectionPreview() { |  | ||||||
|     val funPomoDoroTimer = FunctionalPomodoroTimer(15, 60, 5) |  | ||||||
|     val breakSessionScreen = BreakSessionScreen(funPomoDoroTimer, MediaPlayer()) |  | ||||||
|     breakSessionScreen.MidSection() |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.* | ||||||
|  | import androidx.compose.foundation.shape.CircleShape | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.draw.clip | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | 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.screens.session.SessionActions | ||||||
|  | import be.ugent.sel.studeez.screens.session.SoundPlayer | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun BreakSessionScreenComposable( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     sessionActions: SessionActions, | ||||||
|  |     pomodoroTimer: FunctionalPomodoroTimer, | ||||||
|  |     soundPlayer: SoundPlayer, | ||||||
|  | ) { | ||||||
|  |     SessionScreen( | ||||||
|  |         open = open, | ||||||
|  |         sessionActions = sessionActions, | ||||||
|  |         midSection = { Dots(pomodoroTimer = pomodoroTimer) }, | ||||||
|  |         callMediaPlayer = { soundPlayer.playOn(pomodoroTimer.hasCurrentCountdownEnded()) }, | ||||||
|  |         motivationString = { motivationString (pomodoroTimer = pomodoroTimer) } | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | private fun Dots(pomodoroTimer: FunctionalPomodoroTimer): Int { | ||||||
|  |     Row( | ||||||
|  |         modifier = Modifier.fillMaxWidth(), | ||||||
|  |         verticalAlignment = Alignment.CenterVertically, | ||||||
|  |         horizontalArrangement = Arrangement.Center, | ||||||
|  |     ) { | ||||||
|  |         if (pomodoroTimer.hasEnded()) { | ||||||
|  |             repeat(pomodoroTimer.repeats) { | ||||||
|  |                 Dot(Color.Green) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining - 1) { | ||||||
|  |                 Dot(color = Color.DarkGray) | ||||||
|  |             } | ||||||
|  |             if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) | ||||||
|  |             repeat(pomodoroTimer.breaksRemaining) { | ||||||
|  |                 Dot(color = Color.Gray) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return pomodoroTimer.breaksRemaining | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | private fun Dot(color: Color) { | ||||||
|  |     Box(modifier = Modifier | ||||||
|  |         .padding(5.dp) | ||||||
|  |         .size(10.dp) | ||||||
|  |         .clip(CircleShape) | ||||||
|  |         .background(color)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { | ||||||
|  |     if (pomodoroTimer.isInBreak) { | ||||||
|  |         return resources().getString(R.string.state_take_a_break) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (pomodoroTimer.hasEnded()) { | ||||||
|  |         return resources().getString(R.string.state_done) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return resources().getString(R.string.state_focus) | ||||||
|  | } | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| 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 |  | ||||||
| import be.ugent.sel.studeez.R.string as AppText |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CustomSessionScreen( |  | ||||||
|     private val functionalTimer: FunctionalCustomTimer, |  | ||||||
|     private var mediaplayer: MediaPlayer? |  | ||||||
| ): AbstractSessionScreen() { |  | ||||||
| 
 |  | ||||||
|     @Composable |  | ||||||
|     override fun motivationString(): String { |  | ||||||
|         if (functionalTimer.hasEnded()) { |  | ||||||
|             return resources().getString(AppText.state_done) |  | ||||||
|         } |  | ||||||
|         return resources().getString(AppText.state_focus) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun callMediaPlayer() { |  | ||||||
|         if (functionalTimer.hasEnded()) { |  | ||||||
|             mediaplayer?.let { it: MediaPlayer -> |  | ||||||
|                 it.setOnCompletionListener { |  | ||||||
|                     it.release() |  | ||||||
|                     mediaplayer = null |  | ||||||
|                 } |  | ||||||
|                 it.start() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | 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.FunctionalCustomTimer | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | import be.ugent.sel.studeez.screens.session.SoundPlayer | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun CustomTimerSessionScreenComposable( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     sessionActions: SessionActions, | ||||||
|  |     customTimer: FunctionalCustomTimer, | ||||||
|  |     soundPlayer: SoundPlayer | ||||||
|  | ) { | ||||||
|  |     SessionScreen( | ||||||
|  |         open = open, | ||||||
|  |         callMediaPlayer = { soundPlayer.playOn(customTimer.hasEnded()) }, | ||||||
|  |         sessionActions = sessionActions | ||||||
|  |     ) { | ||||||
|  |         motivationString(customTimer = customTimer) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | private fun motivationString(customTimer: FunctionalCustomTimer): String { | ||||||
|  |     if (customTimer.hasEnded()) { | ||||||
|  |         return resources().getString(R.string.state_done) | ||||||
|  |     } | ||||||
|  |     return resources().getString(R.string.state_focus) | ||||||
|  | } | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| 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) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override fun callMediaPlayer() {} |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import be.ugent.sel.studeez.R | ||||||
|  | import be.ugent.sel.studeez.resources | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun EndlessTimerSessionScreenComposable( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     sessionActions: SessionActions, | ||||||
|  | ) { | ||||||
|  |     SessionScreen( | ||||||
|  |         open = open, | ||||||
|  |         sessionActions = sessionActions | ||||||
|  |     ) { | ||||||
|  |         motivationString() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | private fun motivationString(): String { | ||||||
|  |     return resources().getString(R.string.state_focus) | ||||||
|  | } | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| 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(private val mediaplayer: MediaPlayer?) : FunctionalTimerVisitor<AbstractSessionScreen> { |  | ||||||
|     override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): AbstractSessionScreen = |  | ||||||
|         CustomSessionScreen(functionalCustomTimer, mediaplayer) |  | ||||||
| 
 |  | ||||||
|     override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): AbstractSessionScreen = |  | ||||||
|         EndlessSessionScreen() |  | ||||||
| 
 |  | ||||||
|     override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): AbstractSessionScreen = |  | ||||||
|         BreakSessionScreen(functionalPomodoroTimer, mediaplayer) |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | 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.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 | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | import be.ugent.sel.studeez.screens.session.SoundPlayer | ||||||
|  | 
 | ||||||
|  | class GetSessionScreenComposable( | ||||||
|  |     private val soundPlayer: SoundPlayer, | ||||||
|  |     private val open: (String) -> Unit, | ||||||
|  |     private val sessionActions: SessionActions | ||||||
|  |     ) : | ||||||
|  |     FunctionalTimerVisitor<@Composable () -> Unit> { | ||||||
|  | 
 | ||||||
|  |     override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): @Composable () -> Unit { | ||||||
|  |         return { CustomTimerSessionScreenComposable( | ||||||
|  |                 open = open, | ||||||
|  |                 sessionActions = sessionActions, | ||||||
|  |                 soundPlayer = soundPlayer, | ||||||
|  |                 customTimer = functionalCustomTimer, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): @Composable () -> Unit { | ||||||
|  |         return { | ||||||
|  |             EndlessTimerSessionScreenComposable( | ||||||
|  |                 open = open, | ||||||
|  |                 sessionActions = sessionActions, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): @Composable () -> Unit { | ||||||
|  |         return { | ||||||
|  |             BreakSessionScreenComposable( | ||||||
|  |                 open = open, | ||||||
|  |                 sessionActions = sessionActions, | ||||||
|  |                 soundPlayer = soundPlayer, | ||||||
|  |                 pomodoroTimer = functionalPomodoroTimer | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,73 @@ | ||||||
|  | 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.Composable | ||||||
|  | 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.unit.dp | ||||||
|  | import androidx.compose.ui.unit.sp | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun SessionScreen( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     sessionActions: SessionActions, | ||||||
|  |     callMediaPlayer: () -> Unit = {}, | ||||||
|  |     midSection: @Composable () -> Int = {0}, | ||||||
|  |     motivationString: @Composable () -> String, | ||||||
|  | 
 | ||||||
|  | ) { | ||||||
|  |     Column( | ||||||
|  |         modifier = Modifier.padding(10.dp) | ||||||
|  |     ) { | ||||||
|  |         Timer( | ||||||
|  |             sessionActions = sessionActions, | ||||||
|  |             callMediaPlayer = callMediaPlayer, | ||||||
|  |             motivationString = motivationString, | ||||||
|  |             MidSection = midSection | ||||||
|  |         ) | ||||||
|  |         Box( | ||||||
|  |             contentAlignment = Alignment.Center, modifier = Modifier | ||||||
|  |                 .fillMaxWidth() | ||||||
|  |                 .padding(50.dp) | ||||||
|  |         ) { | ||||||
|  |             EndSessionButton(sessionActions = sessionActions) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun EndSessionButton(sessionActions: SessionActions) { | ||||||
|  |     TextButton( | ||||||
|  |         onClick = { | ||||||
|  |             sessionActions.endSession() | ||||||
|  |         }, | ||||||
|  |         modifier = Modifier | ||||||
|  |             .padding(horizontal = 20.dp) | ||||||
|  |             .border(1.dp, Color.Red, RoundedCornerShape(32.dp)) | ||||||
|  |             .background(Color.Transparent) | ||||||
|  |     ) { | ||||||
|  |         EndsessionText() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun EndsessionText() { | ||||||
|  |     Text( | ||||||
|  |         text = "End session", | ||||||
|  |         color = Color.Red, | ||||||
|  |         fontWeight = FontWeight.Bold, | ||||||
|  |         fontSize = 18.sp, | ||||||
|  |         modifier = Modifier.padding(1.dp) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,95 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.session.sessionScreens | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | 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.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.unit.dp | ||||||
|  | import androidx.compose.ui.unit.sp | ||||||
|  | import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | ||||||
|  | import be.ugent.sel.studeez.screens.session.SessionActions | ||||||
|  | import kotlinx.coroutines.delay | ||||||
|  | import kotlin.time.Duration.Companion.seconds | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun Timer( | ||||||
|  |     sessionActions: SessionActions, | ||||||
|  |     callMediaPlayer: () -> Unit, | ||||||
|  |     motivationString: @Composable () -> String, | ||||||
|  |     MidSection: @Composable () -> Int | ||||||
|  | ) { | ||||||
|  |     var tikker by remember { mutableStateOf(false) } | ||||||
|  |     LaunchedEffect(tikker) { | ||||||
|  |         delay(1.seconds) | ||||||
|  |         sessionActions.getTimer().tick() | ||||||
|  |         callMediaPlayer() | ||||||
|  |         tikker = !tikker | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     val hms = sessionActions.getTimer().getHoursMinutesSeconds() | ||||||
|  |     Column { | ||||||
|  | 
 | ||||||
|  |         TimerClock(hms) | ||||||
|  |         MotivationText(text = motivationString()) | ||||||
|  |         MidSection() | ||||||
|  | 
 | ||||||
|  |         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)) | ||||||
|  |             ) { | ||||||
|  |                 TaskText(taskName = sessionActions.getTask()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun TimerClock(hms: HoursMinutesSeconds) { | ||||||
|  |     Text( | ||||||
|  |         text = hms.toString(), | ||||||
|  |         modifier = Modifier | ||||||
|  |             .fillMaxWidth() | ||||||
|  |             .padding(50.dp), | ||||||
|  |         textAlign = TextAlign.Center, | ||||||
|  |         fontWeight = FontWeight.Bold, | ||||||
|  |         fontSize = 40.sp, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun MotivationText(text: String) { | ||||||
|  |     Text( | ||||||
|  |         text = text, | ||||||
|  |         modifier = Modifier.fillMaxWidth(), | ||||||
|  |         textAlign = TextAlign.Center, | ||||||
|  |         fontWeight = FontWeight.Light, | ||||||
|  |         fontSize = 30.sp | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun TaskText(taskName: String) { | ||||||
|  |     Text( | ||||||
|  |         text = taskName, | ||||||
|  |         color = Color.White, | ||||||
|  |         fontSize = 18.sp, | ||||||
|  |         fontWeight = FontWeight.Bold, | ||||||
|  |         modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp) | ||||||
|  |     ) | ||||||
|  | } | ||||||
		Reference in a new issue
	
	 lbarraga
						lbarraga