diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml
index c9ff4ce..80fd6a4 100644
--- a/.idea/androidTestResultsUserPreferences.xml
+++ b/.idea/androidTestResultsUserPreferences.xml
@@ -291,6 +291,19 @@
+
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index b65e193..bd5886b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -123,9 +123,6 @@ dependencies {
implementation 'com.google.firebase:firebase-firestore-ktx'
implementation 'com.google.firebase:firebase-perf-ktx'
implementation 'com.google.firebase:firebase-config-ktx'
-
- // Colorpicker
- implementation 'com.github.skydoves:colorpicker-compose:1.0.2'
}
// Allow references to generate code
diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt
deleted file mode 100644
index dde1287..0000000
--- a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-package be.ugent.sel.studeez
-
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-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.screens.session.SessionActions
-import be.ugent.sel.studeez.screens.session.sessionScreens.BreakSessionScreen
-import be.ugent.sel.studeez.screens.session.sessionScreens.CustomSessionScreen
-import be.ugent.sel.studeez.screens.session.sessionScreens.EndlessSessionScreen
-import org.junit.Assert
-import org.junit.Rule
-import org.junit.Test
-
-
-class SessionScreenTest {
- @get:Rule
- val composeTestRule = createComposeRule()
-
- @Test
- fun customSessionScreenTest() {
- var endSession = false
-
- composeTestRule.setContent {
- CustomSessionScreen(
- functionalTimer = FunctionalCustomTimer(0),
- mediaplayer = null
- ).invoke(
- open = {},
- sessionActions = SessionActions(
- {FunctionalCustomTimer(0)},
- { "" },
- {}, {}, {endSession = true}
- )
- )
- }
-
- composeTestRule.waitForIdle()
-
- composeTestRule
- .onNodeWithText(
- "end session",
- substring = true,
- ignoreCase = true
- )
- .assertExists()
- .performClick()
-
-
- Assert.assertTrue(endSession)
- }
-
- @Test
- fun endlessSessionScreenTest() {
- var endSession = false
-
- composeTestRule.setContent {
- EndlessSessionScreen()
- .invoke(
- open = {},
- sessionActions = SessionActions(
- {FunctionalEndlessTimer()},
- { "" },
- {}, {}, {endSession = true}
- )
- )
- }
-
- composeTestRule.waitForIdle()
-
- composeTestRule
- .onNodeWithText(
- "end session",
- substring = true,
- ignoreCase = true
- )
- .assertExists()
- .performClick()
-
-
- Assert.assertTrue(endSession)
- }
-
- @Test
- fun breakSessionScreenTest() {
- var endSession = false
-
- composeTestRule.setContent {
- BreakSessionScreen(
- funPomoDoroTimer = FunctionalPomodoroTimer(0, 0, 0),
- mediaplayer = null
- )
- .invoke(
- open = {},
- sessionActions = SessionActions(
- {FunctionalPomodoroTimer(0, 0, 0)},
- { "" },
- {}, {}, {endSession = true}
- )
- )
- }
-
- composeTestRule.waitForIdle()
-
- composeTestRule
- .onNodeWithText(
- "end session",
- substring = true,
- ignoreCase = true
- )
- .assertExists()
- .performClick()
-
-
- Assert.assertTrue(endSession)
- }
-}
diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt
index 22c2f3e..d4b5c68 100644
--- a/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt
+++ b/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt
@@ -117,12 +117,15 @@ class SubjectScreenTest {
onAddSubject = { add = true },
onViewSubject = { view = true },
getStudyTime = { flowOf() },
+ getCompletedTaskCount = { flowOf() },
+ getTaskCount = { flowOf() },
uiState = SubjectUiState.Succes(
listOf(
Subject(
+ id = "",
name = "Test Subject",
argb_color = 0xFFFFD200,
- taskCount = 5, taskCompletedCount = 2,
+ archived = false
)
)
)
diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/TimerScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/TimerScreenTest.kt
deleted file mode 100644
index 40c5e4e..0000000
--- a/app/src/androidTest/java/be/ugent/sel/studeez/TimerScreenTest.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package be.ugent.sel.studeez
-
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import be.ugent.sel.studeez.data.local.models.timer_info.EndlessTimerInfo
-import be.ugent.sel.studeez.screens.timer_form.form_screens.EndlessTimerFormScreen
-import org.junit.Rule
-import org.junit.Test
-
-class TimerScreenTest {
- @get:Rule
- val composeTestRule = createComposeRule()
-
- @Test
- fun timerFormScreenTest() {
- var save = false
-
- composeTestRule.setContent {
- EndlessTimerFormScreen(EndlessTimerInfo("", ""))
- .invoke(onSaveClick = {save = true})
- }
-
- composeTestRule.waitForIdle()
-
- composeTestRule
- .onNodeWithText(
- text = "save",
- substring = true,
- ignoreCase = true
- )
- .assertExists()
- .performClick()
-
- assert(save)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt
index bc40ead..ea2b52d 100644
--- a/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt
@@ -2,7 +2,6 @@ package be.ugent.sel.studeez.common.composable
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt
new file mode 100644
index 0000000..1fbcfb2
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt
@@ -0,0 +1,22 @@
+package be.ugent.sel.studeez.common.composable
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+@Composable
+fun FormComposable(
+ title: String,
+ popUp: () -> Unit,
+ content: @Composable () -> Unit,
+) {
+ SecondaryScreenTemplate(title = title, popUp = popUp) {
+ Box(
+ modifier = Modifier.verticalScroll(rememberScrollState()),
+ ) {
+ content()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt
new file mode 100644
index 0000000..39e7272
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt
@@ -0,0 +1,39 @@
+package be.ugent.sel.studeez.common.composable
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun ImageBackgroundButton(
+ paint: Painter,
+ str: String,
+ background2: Color,
+ setBackground1: (Color) -> Unit,
+ setBackground2: (Color) -> Unit
+) {
+ Image(
+ painter = paint,
+ str,
+ modifier = Modifier
+ .clickable {
+ if (background2 == Color.Transparent) {
+ setBackground1(Color.LightGray)
+ setBackground2(Color.Transparent)
+ } else {
+ setBackground2(Color.Transparent)
+ }
+ }
+ .border(
+ width = 2.dp,
+ color = background2,
+ shape = RoundedCornerShape(16.dp)
+ )
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
index aadcee3..47bdec5 100644
--- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
@@ -3,7 +3,6 @@ package be.ugent.sel.studeez.common.composable
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
@@ -22,7 +21,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
-import kotlin.math.sin
import be.ugent.sel.studeez.R.drawable as AppIcon
import be.ugent.sel.studeez.R.string as AppText
@@ -47,7 +45,7 @@ fun LabelledInputField(
value: String,
onNewValue: (String) -> Unit,
@StringRes label: Int,
- singleLine: Boolean = false
+ singleLine: Boolean = true
) {
OutlinedTextField(
value = value,
@@ -119,7 +117,9 @@ fun LabeledErrorTextField(
initialValue: String,
@StringRes label: Int,
singleLine: Boolean = false,
- errorText: Int,
+ isValid: MutableState = remember { mutableStateOf(true) },
+ isFirst: MutableState = remember { mutableStateOf(false) },
+ @StringRes errorText: Int,
keyboardType: KeyboardType,
predicate: (String) -> Boolean,
onNewCorrectValue: (String) -> Unit
@@ -128,31 +128,28 @@ fun LabeledErrorTextField(
mutableStateOf(initialValue)
}
- var isValid by remember {
- mutableStateOf(predicate(value))
- }
-
Column {
OutlinedTextField(
modifier = modifier.fieldModifier(),
value = value,
onValueChange = { newText ->
+ isFirst.value = false
value = newText
- isValid = predicate(value)
- if (isValid) {
+ isValid.value = predicate(value)
+ if (isValid.value) {
onNewCorrectValue(newText)
}
},
singleLine = singleLine,
label = { Text(text = stringResource(id = label)) },
- isError = !isValid,
+ isError = !isValid.value && !isFirst.value,
keyboardOptions = KeyboardOptions(
keyboardType = keyboardType,
imeAction = ImeAction.Done
)
)
- if (!isValid) {
+ if (!isValid.value && !isFirst.value) {
Text(
modifier = Modifier.padding(start = 16.dp),
text = stringResource(id = errorText),
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt
index 5db2af3..c6631ce 100644
--- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt
@@ -31,9 +31,13 @@ import be.ugent.sel.studeez.R.string as AppText
fun SubjectEntry(
subject: Subject,
onViewSubject: () -> Unit,
+ getTaskCount: () -> Flow,
+ getCompletedTaskCount: () -> Flow,
getStudyTime: () -> Flow,
) {
val studytime by getStudyTime().collectAsState(initial = 0)
+ val taskCount by getTaskCount().collectAsState(initial = 0)
+ val completedTaskCount by getCompletedTaskCount().collectAsState(initial = 0)
Card(
modifier = Modifier
.fillMaxWidth()
@@ -80,7 +84,7 @@ fun SubjectEntry(
imageVector = Icons.Default.List,
contentDescription = stringResource(id = AppText.tasks)
)
- Text(text = "${subject.taskCompletedCount}/${subject.taskCount}")
+ Text(text = "${completedTaskCount}/${taskCount}")
}
}
}
@@ -104,11 +108,11 @@ fun SubjectEntryPreview() {
subject = Subject(
name = "Test Subject",
argb_color = 0xFFFFD200,
- taskCount = 5,
- taskCompletedCount = 2,
),
onViewSubject = {},
- getStudyTime = { flowOf() }
+ getTaskCount = { flowOf() },
+ getCompletedTaskCount = { flowOf() },
+ getStudyTime = { flowOf() },
)
}
@@ -121,6 +125,8 @@ fun OverflowSubjectEntryPreview() {
argb_color = 0xFFFFD200,
),
onViewSubject = {},
- getStudyTime = { flowOf() }
+ getTaskCount = { flowOf() },
+ getCompletedTaskCount = { flowOf() },
+ getStudyTime = { flowOf() },
)
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt b/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt
new file mode 100644
index 0000000..87ce226
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt
@@ -0,0 +1,10 @@
+package be.ugent.sel.studeez.common.ext
+
+import androidx.compose.ui.graphics.Color
+import kotlin.random.Random
+
+fun Color.Companion.generateRandomArgb(): Long {
+ val random = Random
+ val mask: Long = (0x000000FFL shl random.nextInt(0, 3)).inv()
+ return random.nextLong(0xFF000000L, 0xFFFFFFFFL) and mask
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt
index 74ebe9f..88c48c0 100644
--- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt
@@ -1,17 +1,12 @@
package be.ugent.sel.studeez.data.local.models.task
import com.google.firebase.firestore.DocumentId
-import com.google.firebase.firestore.Exclude
data class Subject(
@DocumentId val id: String = "",
val name: String = "",
val argb_color: Long = 0,
var archived: Boolean = false,
- @get:Exclude @set:Exclude
- var taskCount: Int = 0,
- @get:Exclude @set:Exclude
- var taskCompletedCount: Int = 0,
)
object SubjectDocument {
diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt
index 765fbcd..f5237d6 100644
--- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt
@@ -6,14 +6,13 @@ class FunctionalPomodoroTimer(
val repeats: Int
) : FunctionalTimer(studyTime) {
- var breaksRemaining = repeats
+ var breaksRemaining = repeats - 1
var isInBreak = false
override fun tick() {
if (hasEnded()) {
return
}
-
if (hasCurrentCountdownEnded()) {
if (isInBreak) {
breaksRemaining--
diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt
index bad8106..d887ef5 100644
--- a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt
@@ -13,8 +13,10 @@ interface SubjectDAO {
fun updateSubject(newSubject: Subject)
- suspend fun getTaskCount(subject: Subject): Int
- suspend fun getCompletedTaskCount(subject: Subject): Int
+ suspend fun archiveSubject(subject: Subject)
+
+ fun getTaskCount(subject: Subject): Flow
+ fun getCompletedTaskCount(subject: Subject): Flow
fun getStudyTime(subject: Subject): Flow
suspend fun getSubject(subjectId: String): Subject?
diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt
index b023986..915a7f9 100644
--- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt
@@ -2,10 +2,11 @@ package be.ugent.sel.studeez.domain.implementation
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.task.SubjectDocument
+import be.ugent.sel.studeez.data.local.models.task.Task
+import be.ugent.sel.studeez.data.local.models.task.TaskDocument
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.SubjectDAO
import be.ugent.sel.studeez.domain.TaskDAO
-import com.google.firebase.firestore.AggregateSource
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.Query
@@ -15,6 +16,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
+import kotlin.collections.count
class FireBaseSubjectDAO @Inject constructor(
private val firestore: FirebaseFirestore,
@@ -26,13 +28,6 @@ class FireBaseSubjectDAO @Inject constructor(
.subjectNotArchived()
.snapshots()
.map { it.toObjects(Subject::class.java) }
- .map { subjects ->
- subjects.map { subject ->
- subject.taskCount = getTaskCount(subject)
- subject.taskCompletedCount = getCompletedTaskCount(subject)
- subject
- }
- }
}
override suspend fun getSubject(subjectId: String): Subject? {
@@ -51,23 +46,26 @@ class FireBaseSubjectDAO @Inject constructor(
currentUserSubjectsCollection().document(newSubject.id).set(newSubject)
}
- override suspend fun getTaskCount(subject: Subject): Int {
- return subjectTasksCollection(subject)
+ override suspend fun archiveSubject(subject: Subject) {
+ currentUserSubjectsCollection().document(subject.id).update(SubjectDocument.archived, true)
+ currentUserSubjectsCollection().document(subject.id)
+ .collection(FireBaseCollections.TASK_COLLECTION)
.taskNotArchived()
- .count()
- .get(AggregateSource.SERVER)
- .await()
- .count.toInt()
+ .get().await()
+ .documents
+ .forEach {
+ it.reference.update(TaskDocument.archived, true)
+ }
}
- override suspend fun getCompletedTaskCount(subject: Subject): Int {
- return subjectTasksCollection(subject)
- .taskNotArchived()
- .taskNotCompleted()
- .count()
- .get(AggregateSource.SERVER)
- .await()
- .count.toInt()
+ override fun getTaskCount(subject: Subject): Flow {
+ return taskDAO.getTasks(subject)
+ .map(List::count)
+ }
+
+ override fun getCompletedTaskCount(subject: Subject): Flow {
+ return taskDAO.getTasks(subject)
+ .map { tasks -> tasks.count { it.completed && !it.archived } }
}
override fun getStudyTime(subject: Subject): Flow {
diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt
index 37085f1..0a0e8b4 100644
--- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt
@@ -25,9 +25,9 @@ import be.ugent.sel.studeez.screens.settings.SettingsRoute
import be.ugent.sel.studeez.screens.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashRoute
import be.ugent.sel.studeez.screens.subjects.SubjectRoute
-import be.ugent.sel.studeez.screens.tasks.TaskRoute
import be.ugent.sel.studeez.screens.subjects.form.SubjectCreateRoute
import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute
+import be.ugent.sel.studeez.screens.tasks.TaskRoute
import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute
import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute
import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute
@@ -51,6 +51,7 @@ fun StudeezNavGraph(
val open: (String) -> Unit = { appState.navigate(it) }
val openAndPopUp: (String, String) -> Unit =
{ route, popUp -> appState.navigateAndPopUp(route, popUp) }
+ val clearAndNavigate: (route: String) -> Unit = { route -> appState.clearAndNavigate(route) }
val drawerActions: DrawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp)
val navigationBarActions: NavigationBarActions =
@@ -200,7 +201,7 @@ fun StudeezNavGraph(
composable(StudeezDestinations.SESSION_RECAP) {
SessionRecapRoute(
- openAndPopUp = openAndPopUp,
+ clearAndNavigate = clearAndNavigate,
viewModel = hiltViewModel()
)
}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt
index 9051fa8..948c4c4 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt
@@ -1,6 +1,9 @@
package be.ugent.sel.studeez.screens.session
+import android.content.Context
import android.media.MediaPlayer
+import android.media.RingtoneManager
+import android.net.Uri
import kotlinx.coroutines.delay
import javax.inject.Singleton
import kotlin.time.Duration.Companion.seconds
@@ -10,9 +13,11 @@ object InvisibleSessionManager {
private var viewModel: SessionViewModel? = null
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.mediaPlayer = mediaplayer
}
suspend fun updateTimer() {
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt
index 084ff43..aeaf544 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt
@@ -1,33 +1,24 @@
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
+import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreenComposable
data class SessionActions(
val getTimer: () -> FunctionalTimer,
val getTask: () -> String,
- val startMediaPlayer: () -> Unit,
- val releaseMediaPlayer: () -> Unit,
val endSession: () -> Unit
)
private fun getSessionActions(
viewModel: SessionViewModel,
openAndPopUp: (String, String) -> Unit,
- mediaplayer: MediaPlayer,
): SessionActions {
return SessionActions(
getTimer = viewModel::getTimer,
getTask = viewModel::getTask,
endSession = { viewModel.endSession(openAndPopUp) },
- startMediaPlayer = mediaplayer::start,
- releaseMediaPlayer = mediaplayer::release,
)
}
@@ -37,20 +28,12 @@ fun SessionRoute(
openAndPopUp: (String, 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
- )
+ InvisibleSessionManager.setParameters(viewModel = viewModel, context = LocalContext.current)
- 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(
- open = open,
- sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer)
- )
+ sessionScreen()
}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt
new file mode 100644
index 0000000..14fae19
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt
@@ -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)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt
deleted file mode 100644
index 08a8a72..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt
+++ /dev/null
@@ -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" }, {}, {}, {}))
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt
deleted file mode 100644
index 9c59b46..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt
+++ /dev/null
@@ -1,93 +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.material.Text
-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.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.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,
- private var mediaplayer: MediaPlayer?
-): AbstractSessionScreen() {
-
- @Composable
- override fun MidSection() {
- Dots()
- }
-
- @Composable
- fun Dots() {
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center,
- ) {
- repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) {
- Dot(color = Color.DarkGray)
- }
- if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray)
- repeat(funPomoDoroTimer.breaksRemaining - 1) {
- 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()
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt
new file mode 100644
index 0000000..42ec4f7
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt
@@ -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)
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt
deleted file mode 100644
index 7fc60bc..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt
+++ /dev/null
@@ -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()
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt
new file mode 100644
index 0000000..a0c385c
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt
@@ -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)
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt
deleted file mode 100644
index be67cff..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt
+++ /dev/null
@@ -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() {}
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt
new file mode 100644
index 0000000..4f1dbe3
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt
@@ -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)
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt
deleted file mode 100644
index 98b2d5e..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt
+++ /dev/null
@@ -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 {
- 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)
-}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt
new file mode 100644
index 0000000..47ca52e
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt
@@ -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
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt
new file mode 100644
index 0000000..c94d2a5
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt
@@ -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)
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt
new file mode 100644
index 0000000..2a29403
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt
@@ -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)
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt
index 2d06e0b..3a1e85f 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt
@@ -1,13 +1,24 @@
package be.ugent.sel.studeez.screens.session_recap
-import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.*
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+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.R
import be.ugent.sel.studeez.common.composable.BasicButton
+import be.ugent.sel.studeez.common.composable.ImageBackgroundButton
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
@@ -21,24 +32,24 @@ data class SessionRecapActions(
fun getSessionRecapActions(
viewModel: SessionRecapViewModel,
- openAndPopUp: (String, String) -> Unit,
+ clearAndNavigate: (String) -> Unit,
): SessionRecapActions {
return SessionRecapActions(
viewModel::getSessionReport,
- {viewModel.saveSession(openAndPopUp)},
- {viewModel.discardSession(openAndPopUp)}
+ { viewModel.saveSession(clearAndNavigate) },
+ { viewModel.discardSession(clearAndNavigate) }
)
}
@Composable
fun SessionRecapRoute(
- openAndPopUp: (String, String) -> Unit,
+ clearAndNavigate: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: SessionRecapViewModel,
) {
SessionRecapScreen(
modifier = modifier,
- getSessionRecapActions(viewModel, openAndPopUp)
+ getSessionRecapActions(viewModel, clearAndNavigate)
)
}
@@ -47,21 +58,88 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi
val sessionReport: SessionReport = sessionRecapActions.getSessionReport()
val studyTime: Int = sessionReport.studyTime
val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS()
+ val (background1, setBackground1) = remember { mutableStateOf(Color.Transparent) }
+ val (background2, setBackground2) = remember { mutableStateOf(Color.Transparent) }
Column(
modifier = modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceBetween
) {
- Text(text = "You studied: $hms")
+ Text(
+ text = stringResource(R.string.congrats, hms),
+ modifier = Modifier
+ .fillMaxWidth(),
+ textAlign = TextAlign.Center,
+ fontWeight = FontWeight.Light,
+ fontSize = 30.sp,
- BasicButton(
- R.string.save, Modifier.basicButton()
+ )
+
+ Column(
+ modifier = Modifier.fillMaxWidth()
) {
- sessionRecapActions.saveSession()
+ Text(
+ text = stringResource(R.string.how_did_it_go),
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center,
+ fontWeight = FontWeight.Light,
+ fontSize = 30.sp
+ )
+
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.CenterHorizontally)
+ ) {
+ ImageBackgroundButton(
+ paint = painterResource(id = R.drawable.mood_1),
+ str = stringResource(id = R.string.good),
+ background2 = background2,
+ setBackground1 = setBackground2,
+ setBackground2 = setBackground1
+ )
+
+ ImageBackgroundButton(
+ paint = painterResource(id = R.drawable.mood_2),
+ str = stringResource(id = R.string.bad),
+ background2 = background1,
+ setBackground1 = setBackground1,
+ setBackground2 = setBackground2
+ )
+ }
}
- BasicButton(
- R.string.discard, Modifier.basicButton(),
- colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
- ) {
- sessionRecapActions.discardSession()
+
+ Column {
+ BasicButton(
+ R.string.save, Modifier.basicButton()
+ ) {
+ sessionRecapActions.saveSession()
+ }
+ BasicButton(
+ R.string.discard, Modifier.basicButton(),
+ colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
+ ) {
+ sessionRecapActions.discardSession()
+ }
}
}
}
+
+@Preview
+@Composable
+fun SessionRecapScreenPreview() {
+ SessionRecapScreen(
+ modifier = Modifier,
+ sessionRecapActions = SessionRecapActions(
+ { SessionReport(
+ studyTime = 100,
+ ) },
+ {},
+ {},
+ )
+ )
+}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt
index c11e28f..bf11b93 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt
@@ -24,15 +24,15 @@ class SessionRecapViewModel @Inject constructor(
return selectedSessionReport()
}
- fun saveSession(open: (String, String) -> Unit) {
+ fun saveSession(open: (String) -> Unit) {
sessionDAO.saveSession(getSessionReport())
val newTask =
selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime)
taskDAO.updateTask(newTask)
- open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
+ open(StudeezDestinations.HOME_SCREEN)
}
- fun discardSession(open: (String, String) -> Unit) {
- open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
+ fun discardSession(open: (String) -> Unit) {
+ open(StudeezDestinations.HOME_SCREEN)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt
index ab2cff4..852c0e5 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt
@@ -36,6 +36,8 @@ fun SubjectRoute(
navigationBarActions = navigationBarActions,
onAddSubject = { viewModel.onAddSubject(open) },
onViewSubject = { viewModel.onViewSubject(it, open) },
+ getTaskCount = viewModel::getTaskCount,
+ getCompletedTaskCount = viewModel::getCompletedTaskCount,
getStudyTime = viewModel::getStudyTime,
uiState,
)
@@ -47,6 +49,8 @@ fun SubjectScreen(
navigationBarActions: NavigationBarActions,
onAddSubject: () -> Unit,
onViewSubject: (Subject) -> Unit,
+ getTaskCount: (Subject) -> Flow,
+ getCompletedTaskCount: (Subject) -> Flow,
getStudyTime: (Subject) -> Flow,
uiState: SubjectUiState,
) {
@@ -76,6 +80,8 @@ fun SubjectScreen(
SubjectEntry(
subject = it,
onViewSubject = { onViewSubject(it) },
+ getTaskCount = { getTaskCount(it) },
+ getCompletedTaskCount = { getCompletedTaskCount(it) },
getStudyTime = { getStudyTime(it) },
)
}
@@ -94,13 +100,14 @@ fun SubjectScreenPreview() {
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
onAddSubject = {},
onViewSubject = {},
+ getTaskCount = { flowOf() },
+ getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() },
uiState = SubjectUiState.Succes(
listOf(
Subject(
name = "Test Subject",
argb_color = 0xFFFFD200,
- taskCount = 5, taskCompletedCount = 2,
)
)
)
@@ -115,7 +122,9 @@ fun SubjectScreenLoadingPreview() {
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
onAddSubject = {},
onViewSubject = {},
+ getTaskCount = { flowOf() },
+ getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() },
- uiState = SubjectUiState.Loading
+ uiState = SubjectUiState.Loading,
)
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt
index c158529..19ba396 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt
@@ -30,6 +30,14 @@ class SubjectViewModel @Inject constructor(
open(StudeezDestinations.ADD_SUBJECT_FORM)
}
+ fun getTaskCount(subject: Subject): Flow {
+ return subjectDAO.getTaskCount(subject)
+ }
+
+ fun getCompletedTaskCount(subject: Subject): Flow {
+ return subjectDAO.getCompletedTaskCount(subject)
+ }
+
fun getStudyTime(subject: Subject): Flow {
return subjectDAO.getStudyTime(subject)
}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt
index 19e6816..196ad3f 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt
@@ -2,20 +2,27 @@ package be.ugent.sel.studeez.screens.subjects.form
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
-import androidx.compose.material.OutlinedTextField
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton
-import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
+import be.ugent.sel.studeez.common.composable.FormComposable
+import be.ugent.sel.studeez.common.composable.LabelledInputField
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
+import be.ugent.sel.studeez.common.ext.generateRandomArgb
import be.ugent.sel.studeez.resources
+import kotlinx.coroutines.launch
import be.ugent.sel.studeez.R.string as AppText
@Composable
@@ -31,7 +38,7 @@ fun SubjectCreateRoute(
uiState = uiState,
onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange,
- onColorChange = {},
+ onColorChange = viewModel::onColorChange,
)
}
@@ -42,16 +49,19 @@ fun SubjectEditRoute(
viewModel: SubjectEditFormViewModel,
) {
val uiState by viewModel.uiState
+ val coroutineScope = rememberCoroutineScope()
SubjectForm(
title = AppText.edit_subject,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange,
- onColorChange = {},
+ onColorChange = viewModel::onColorChange,
) {
DeleteButton(text = AppText.delete_subject) {
- viewModel.onDelete(openAndPopUp)
+ coroutineScope.launch {
+ viewModel.onDelete(openAndPopUp)
+ }
}
}
}
@@ -63,21 +73,21 @@ fun SubjectForm(
uiState: SubjectFormUiState,
onConfirm: () -> Unit,
onNameChange: (String) -> Unit,
- onColorChange: (Color) -> Unit,
+ onColorChange: (Long) -> Unit,
extraButton: @Composable () -> Unit = {},
) {
- SecondaryScreenTemplate(
+ FormComposable(
title = resources().getString(title),
popUp = goBack,
) {
Column {
- OutlinedTextField(
+ LabelledInputField(
singleLine = true,
value = uiState.name,
- onValueChange = onNameChange,
- placeholder = { Text(stringResource(id = AppText.name)) },
- modifier = Modifier.fieldModifier(),
+ onNewValue = onNameChange,
+ label = AppText.name,
)
+ ColorPicker(onColorChange, uiState)
BasicButton(
text = AppText.confirm,
modifier = Modifier.basicButton(),
@@ -88,6 +98,24 @@ fun SubjectForm(
}
}
+@Composable
+fun ColorPicker(
+ onColorChange: (Long) -> Unit,
+ uiState: SubjectFormUiState,
+) {
+ Button(
+ onClick = { onColorChange(Color.generateRandomArgb()) },
+ modifier = Modifier.fieldModifier(),
+ colors = ButtonDefaults.buttonColors(
+ backgroundColor = Color(uiState.color),
+ contentColor = Color.White,
+ ),
+ shape = RoundedCornerShape(4.dp),
+ ) {
+ Text(text = stringResource(id = AppText.regenerate_color))
+ }
+}
+
@Preview
@Composable
fun AddSubjectFormPreview() {
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt
index 9fdba01..10a18e8 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt
@@ -1,6 +1,9 @@
package be.ugent.sel.studeez.screens.subjects.form
+import androidx.compose.ui.graphics.Color
+import be.ugent.sel.studeez.common.ext.generateRandomArgb
+
data class SubjectFormUiState(
val name: String = "",
- val color: Long = 0xFFFFD200,
+ val color: Long = Color.generateRandomArgb(),
)
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt
index 533123b..84162d0 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt
@@ -6,6 +6,7 @@ import be.ugent.sel.studeez.data.SelectedSubject
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.SubjectDAO
+import be.ugent.sel.studeez.domain.TaskDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -59,6 +60,7 @@ class SubjectCreateFormViewModel @Inject constructor(
@HiltViewModel
class SubjectEditFormViewModel @Inject constructor(
subjectDAO: SubjectDAO,
+ private val taskDAO: TaskDAO,
selectedSubject: SelectedSubject,
logService: LogService,
) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) {
@@ -69,17 +71,19 @@ class SubjectEditFormViewModel @Inject constructor(
)
)
- fun onDelete(openAndPopUp: (String, String) -> Unit) {
- subjectDAO.updateSubject(selectedSubject().copy(archived = true))
+ suspend fun onDelete(openAndPopUp: (String, String) -> Unit) {
+ subjectDAO.archiveSubject(selectedSubject())
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
fun onEdit(openAndPopUp: (String, String) -> Unit) {
- val newSubject = selectedSubject().copy(
- name = name,
- argb_color = color,
+ selectedSubject.set(
+ selectedSubject().copy(
+ name = name,
+ argb_color = color,
+ )
)
- subjectDAO.updateSubject(newSubject)
+ subjectDAO.updateSubject(selectedSubject())
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt
index 92302ea..79c744d 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt
@@ -11,7 +11,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton
-import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
+import be.ugent.sel.studeez.common.composable.FormComposable
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
@@ -62,7 +62,7 @@ fun TaskForm(
onNameChange: (String) -> Unit,
extraButton: @Composable () -> Unit = {}
) {
- SecondaryScreenTemplate(
+ FormComposable(
title = resources().getString(title),
popUp = goBack,
) {
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt
deleted file mode 100644
index 0afe2a0..0000000
--- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-package be.ugent.sel.studeez.screens.timer_form
-
-import androidx.annotation.StringRes
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
-import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
-import be.ugent.sel.studeez.data.local.models.timer_info.PomodoroTimerInfo
-import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
-import be.ugent.sel.studeez.R.string as AppText
-
-@Composable
-fun TimerAddRoute(
- popUp: () -> Unit,
- viewModel: TimerFormViewModel
-) {
- TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) {
- viewModel.saveTimer(it, goBack = popUp)
- }
-}
-
-@Composable
-fun TimerEditRoute(
- popUp: () -> Unit,
- viewModel: TimerFormViewModel
-) {
- TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) {
- viewModel.editTimer(it, goBack = popUp)
- }
-}
-
-@Composable
-fun TimerFormScreen(
- popUp: () -> Unit,
- getTimerInfo: () -> TimerInfo,
- @StringRes label: Int,
- onConfirmClick: (TimerInfo) -> Unit
-) {
- val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen())
-
- SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) {
- timerFormScreen(onConfirmClick)
- }
-}
-
-@Preview
-@Composable
-fun AddTimerPreview() {
- TimerFormScreen(
- popUp = { },
- getTimerInfo = { PomodoroTimerInfo("", "", 0, 0, 0) },
- label = AppText.add_timer,
- onConfirmClick = {}
- )
-}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt
new file mode 100644
index 0000000..c69e929
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt
@@ -0,0 +1,68 @@
+package be.ugent.sel.studeez.screens.timer_form
+
+import androidx.annotation.StringRes
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import be.ugent.sel.studeez.common.composable.DeleteButton
+import be.ugent.sel.studeez.common.composable.FormComposable
+import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
+import be.ugent.sel.studeez.R.string as AppText
+
+@Composable
+fun TimerAddRoute(
+ popUp: () -> Unit,
+ viewModel: TimerFormViewModel
+) {
+
+
+ TimerFormScreen(
+ popUp = popUp,
+ getTimerInfo = viewModel::getTimerInfo,
+ extraButton= { },
+ AppText.add_timer
+ ) {
+ viewModel.saveTimer(it, goBack = {popUp(); popUp()})
+
+ }
+}
+
+@Composable
+fun TimerEditRoute(
+ popUp: () -> Unit,
+ viewModel: TimerFormViewModel
+) {
+
+ @Composable
+ fun deleteButton() {
+ DeleteButton(text = AppText.delete_timer) {
+ viewModel.deleteTimer(viewModel.getTimerInfo(), popUp)
+ }
+ }
+
+ TimerFormScreen(
+ popUp = popUp,
+ getTimerInfo = viewModel::getTimerInfo,
+ extraButton= { deleteButton() },
+ AppText.edit_timer
+ ) {
+ viewModel.editTimer(it, goBack = popUp)
+ }
+}
+
+@Composable
+fun TimerFormScreen(
+ popUp: () -> Unit,
+ getTimerInfo: () -> TimerInfo,
+ extraButton: @Composable () -> Unit,
+ @StringRes label: Int,
+ onConfirmClick: (TimerInfo) -> Unit
+) {
+ val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen())
+
+ FormComposable(
+ title = stringResource(id = label),
+ popUp = popUp
+ ) {
+ timerFormScreen(onConfirmClick, extraButton)
+ }
+}
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt
index 8a0a4d4..c34cd06 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt
@@ -23,6 +23,11 @@ class TimerFormViewModel @Inject constructor(
goBack()
}
+ fun deleteTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
+ timerDAO.deleteTimer(timerInfo)
+ goBack()
+ }
+
fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
timerDAO.saveTimer(timerInfo)
goBack()
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt
index 5f4a17b..9560dd1 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt
@@ -1,69 +1,84 @@
package be.ugent.sel.studeez.screens.timer_form.form_screens
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.input.KeyboardType
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
-import be.ugent.sel.studeez.common.composable.LabelledInputField
+import be.ugent.sel.studeez.common.composable.LabeledErrorTextField
import be.ugent.sel.studeez.common.ext.basicButton
+import be.ugent.sel.studeez.common.snackbar.SnackbarManager
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.R.string as AppText
abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) {
+ protected val valids = mutableMapOf(
+ "name" to mutableStateOf(textPredicate(timerInfo.name)),
+ "description" to mutableStateOf(textPredicate(timerInfo.description))
+ )
+
+ protected val firsts = mutableMapOf(
+ "name" to mutableStateOf(true),
+ "description" to mutableStateOf(true)
+ )
+
+
@Composable
- operator fun invoke(onSaveClick: (TimerInfo) -> Unit) {
+ operator fun invoke(
+ onSaveClick: (TimerInfo) -> Unit,
+ extraButton: @Composable () -> Unit = {},
+ ) {
- var name by remember { mutableStateOf(timerInfo.name) }
- var description by remember { mutableStateOf(timerInfo.description) }
-
- // This shall rerun whenever name and description change
- timerInfo.name = name
- timerInfo.description = description
-
- Column(
- verticalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()),
- ) {
- Column(
- modifier = Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
-
- // Fields that every timer shares (ommited id)
- LabelledInputField(
- value = name,
- onNewValue = { name = it },
- label = R.string.name
- )
-
- LabelledInputField(
- value = description,
- onNewValue = { description = it },
- label = AppText.description,
- singleLine = false
- )
-
- ExtraFields()
+ Column {
+ // Fields that every timer shares (ommited id)
+ LabeledErrorTextField(
+ initialValue = timerInfo.name,
+ label = R.string.name,
+ errorText = AppText.name_error,
+ isValid = valids.getValue("name"),
+ isFirst = firsts.getValue("name"),
+ keyboardType = KeyboardType.Text,
+ predicate = { it.isNotBlank() }
+ ) { correctName ->
+ timerInfo.name = correctName
}
+
+ LabeledErrorTextField(
+ initialValue = timerInfo.description,
+ label = R.string.description,
+ errorText = AppText.description_error,
+ isValid = valids.getValue("description"),
+ isFirst = firsts.getValue("description"),
+ singleLine = false,
+ keyboardType = KeyboardType.Text,
+ predicate = { textPredicate(it) }
+ ) { correctName ->
+ timerInfo.description = correctName
+ }
+
+ ExtraFields()
+
BasicButton(R.string.save, Modifier.basicButton()) {
- onSaveClick(timerInfo)
+ if (valids.all { it.component2().value }) { // All fields are valid
+ onSaveClick(timerInfo)
+ } else {
+ firsts.map {
+ it.component2().value = false
+ } // dont mask error because its not been filled out yet
+ SnackbarManager.showMessage(AppText.fill_out_error)
+ }
}
+ extraButton()
}
}
+ private fun textPredicate(text: String): Boolean {
+ return text.isNotBlank()
+ }
+
@Composable
open fun ExtraFields() {
// By default no extra fields, unless overwritten by subclass.
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt
index 12d07a4..c87bd7b 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt
@@ -15,6 +15,8 @@ class BreakTimerFormScreen(
private val breakTimerInfo: PomodoroTimerInfo
): AbstractTimerFormScreen(breakTimerInfo) {
+
+
@Composable
override fun ExtraFields() {
// If the user presses the OK button on the timepicker, the time in the button should change
@@ -26,12 +28,17 @@ class BreakTimerFormScreen(
breakTimerInfo.breakTime = newTime
}
+ valids["repeats"] = remember {mutableStateOf(true)}
+ firsts["repeats"] = remember { mutableStateOf(true) }
+
LabeledErrorTextField(
initialValue = breakTimerInfo.repeats.toString(),
label = R.string.repeats,
errorText = AppText.repeats_error,
+ isValid = valids.getValue("repeats"),
+ isFirst = firsts.getValue("repeats"),
keyboardType = KeyboardType.Decimal,
- predicate = { it.matches(Regex("[1-9]+\\d*")) }
+ predicate = { isNumber(it) }
) { correctlyTypedInt ->
breakTimerInfo.repeats = correctlyTypedInt.toInt()
}
@@ -39,6 +46,10 @@ class BreakTimerFormScreen(
}
}
+fun isNumber(text: String): Boolean {
+ return text.matches(Regex("[1-9]+\\d*"))
+}
+
@Preview
@Composable
fun BreakEditScreenPreview() {
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt
index 5e251f2..3663e4a 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt
@@ -1,7 +1,6 @@
package be.ugent.sel.studeez.screens.timer_form.timer_type_select
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -9,6 +8,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.data.local.models.timer_info.*
@@ -38,7 +38,10 @@ fun TimerTypeSelectScreen(
) {
TimerType.values().forEach { timerType ->
val default: TimerInfo = defaultTimerInfo.getValue(timerType)
- Button(onClick = { viewModel.onTimerTypeChosen(default, open) }) {
+ Button(
+ onClick = { viewModel.onTimerTypeChosen(default, open) },
+ modifier = Modifier.fillMaxWidth().padding(5.dp)
+ ) {
Text(text = timerType.name)
}
}
diff --git a/app/src/main/res/drawable/mood_1.xml b/app/src/main/res/drawable/mood_1.xml
new file mode 100644
index 0000000..bf009f2
--- /dev/null
+++ b/app/src/main/res/drawable/mood_1.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/mood_2.xml b/app/src/main/res/drawable/mood_2.xml
new file mode 100644
index 0000000..0fd3daa
--- /dev/null
+++ b/app/src/main/res/drawable/mood_2.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 70c4558..a5f350e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -46,6 +46,7 @@
Delete Subject
Delete Task
View
+ Regenerate Color
Looks like you found the sessions screen! In here, your upcoming studying sessions with friends will be listed. You can accept invites or edit your own.
@@ -69,8 +70,15 @@
Timers
+ Delete Timer
Edit
Add timer
+
+ Name should not be blank
+ Description should not be blank
+ Fill out all the fields correctly!
+
+
Select time
Focus!
@@ -149,4 +157,11 @@
Break Time
Number of Repeats
+
+ "Congratulations! You studied: %s"
+ How did it go?
+ Good
+ Bad
+
+
diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt
index 06b1d42..8f1cca7 100644
--- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt
+++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt
@@ -1,6 +1,5 @@
package be.ugent.sel.studeez.timer_functional
-import android.media.MediaPlayer
import be.ugent.sel.studeez.data.SelectedSessionReport
import be.ugent.sel.studeez.data.SelectedTask
import be.ugent.sel.studeez.data.SelectedTimer
@@ -22,13 +21,12 @@ import org.mockito.kotlin.mock
class InvisibleSessionManagerTest {
private var selectedTimer: SelectedTimer = SelectedTimer()
private lateinit var viewModel: SessionViewModel
- private var mediaPlayer: MediaPlayer = mock()
@Test
fun InvisibleEndlessTimerTest() = runTest {
selectedTimer.set(FunctionalEndlessTimer())
viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl())
- InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
+ InvisibleSessionManager.setParameters(viewModel, mock())
val test = launch {
InvisibleSessionManager.updateTimer()
@@ -50,7 +48,7 @@ class InvisibleSessionManagerTest {
val repeats = 1
selectedTimer.set(FunctionalPomodoroTimer(studyTime, breakTime, repeats))
viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl())
- InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
+ InvisibleSessionManager.setParameters(viewModel, mock())
val test = launch {
InvisibleSessionManager.updateTimer()
@@ -83,7 +81,7 @@ class InvisibleSessionManagerTest {
fun InvisibleCustomTimerTest() = runTest {
selectedTimer.set(FunctionalCustomTimer(5))
viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl())
- InvisibleSessionManager.setParameters(viewModel, mediaPlayer)
+ InvisibleSessionManager.setParameters(viewModel, mock())
val test = launch {
InvisibleSessionManager.updateTimer()