From d6b2c17f29148636aaa2cef4c8e98a4ce13ceb48 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:31:11 +0200 Subject: [PATCH 01/68] bug fix: singleline was by default false --- .../ugent/sel/studeez/common/composable/TextFieldComposable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..e3c54ef 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 @@ -47,7 +47,7 @@ fun LabelledInputField( value: String, onNewValue: (String) -> Unit, @StringRes label: Int, - singleLine: Boolean = false + singleLine: Boolean = true ) { OutlinedTextField( value = value, From 9bc64be1ee6657fab3054ca67e1f9463ac3e4950 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:32:08 +0200 Subject: [PATCH 02/68] #110 added extra button in parameters of formscreen --- .../screens/timer_form/TimerFormRoute.kt | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) 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 index 0323dc2..7cef001 100644 --- 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 @@ -3,6 +3,7 @@ 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.SecondaryScreenTemplate import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.R.string as AppText @@ -12,8 +13,16 @@ fun TimerAddRoute( popUp: () -> Unit, viewModel: TimerFormViewModel ) { - TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) { - viewModel.saveTimer(it, goBack = popUp) + + + TimerFormScreen( + popUp = popUp, + getTimerInfo = viewModel::getTimerInfo, + extraButton= { }, + AppText.add_timer + ) { + viewModel.saveTimer(it, goBack = {popUp(); popUp()}) + } } @@ -22,7 +31,20 @@ fun TimerEditRoute( popUp: () -> Unit, viewModel: TimerFormViewModel ) { - TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) { + + @Composable + fun deleteButton() { + DeleteButton(text = AppText.delete_subject) { + viewModel.deleteTimer(viewModel.getTimerInfo(), popUp) + } + } + + TimerFormScreen( + popUp = popUp, + getTimerInfo = viewModel::getTimerInfo, + extraButton= { deleteButton() }, + AppText.edit_timer + ) { viewModel.editTimer(it, goBack = popUp) } } @@ -31,12 +53,13 @@ fun TimerEditRoute( fun TimerFormScreen( popUp: () -> Unit, getTimerInfo: () -> TimerInfo, + extraButton: @Composable () -> Unit, @StringRes label: Int, onConfirmClick: (TimerInfo) -> Unit ) { val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen()) SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) { - timerFormScreen(onConfirmClick) + timerFormScreen(onConfirmClick, extraButton) } } From d9294155824a5e31692bdf11a1780f3441198da1 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:32:36 +0200 Subject: [PATCH 03/68] #110 added delete timer function in viewmodel --- .../sel/studeez/screens/timer_form/TimerFormViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) 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() From 88ebbe4de1c6119beb7658cac036db28036ba294 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:33:15 +0200 Subject: [PATCH 04/68] #110 extrabutton added (used for delete) --- .../form_screens/AbstractTimerFormScreen.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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..168a2f8 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 @@ -23,7 +23,10 @@ import be.ugent.sel.studeez.R.string as AppText abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { @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) } @@ -34,7 +37,9 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column( verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()), + modifier = Modifier + .fillMaxHeight() + .verticalScroll(rememberScrollState()), ) { Column( modifier = Modifier.fillMaxWidth(), @@ -45,7 +50,7 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { LabelledInputField( value = name, onNewValue = { name = it }, - label = R.string.name + label = R.string.name, ) LabelledInputField( @@ -58,8 +63,12 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { ExtraFields() } - BasicButton(R.string.save, Modifier.basicButton()) { - onSaveClick(timerInfo) + + Column { + BasicButton(R.string.save, Modifier.basicButton()) { + onSaveClick(timerInfo) + } + extraButton() } } } From 74d29205624303dafd53e44a30a7c4a39c899391 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:33:34 +0200 Subject: [PATCH 05/68] fix previews for extra button --- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 2 +- .../screens/timer_form/form_screens/CustomTimerFormScreen.kt | 2 +- .../screens/timer_form/form_screens/EndlessTimerFormScreen.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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..21e862d 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 @@ -50,6 +50,6 @@ fun BreakEditScreenPreview() { 5 ) StudeezTheme { - BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) + BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}, {}) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt index 27c0657..b0000a0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt @@ -29,6 +29,6 @@ class CustomTimerFormScreen( fun CustomEditScreenPreview() { val customTimerInfo = CustomTimerInfo("custom", "my description", 25) StudeezTheme { - CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}) + CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}, extraButton = {}) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt index 9009fff..e096946 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt @@ -18,6 +18,6 @@ fun EndlessEditScreenPreview() { "My endless timer description", ) StudeezTheme { - EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}) + EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}, {}) } } \ No newline at end of file From 81e777f21ddc78b54c50e98b083920d7583de2b8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:13:48 +0200 Subject: [PATCH 06/68] #63 Add bio to profile --- .../sel/studeez/data/local/models/User.kt | 8 ++- .../be/ugent/sel/studeez/domain/UserDAO.kt | 10 ++- .../domain/implementation/FirebaseUserDAO.kt | 21 +++++-- .../screens/profile/ProfileEditScreen.kt | 61 ++++++++++++------- .../screens/profile/ProfileEditUiState.kt | 3 +- .../screens/profile/ProfileEditViewModel.kt | 16 ++++- .../studeez/screens/profile/ProfileScreen.kt | 42 +++++++++---- .../screens/profile/ProfileViewModel.kt | 8 ++- .../screens/sign_up/SignUpViewModel.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 124 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt index 2fba2ce..a92bebb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt @@ -1,3 +1,9 @@ package be.ugent.sel.studeez.data.local.models -data class User(val id: String = "") +import com.google.firebase.firestore.DocumentId + +data class User( + @DocumentId val id: String = "", + val username: String = "", + val biography: String = "" +) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index b96cf17..619c77b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,9 +1,15 @@ package be.ugent.sel.studeez.domain +import be.ugent.sel.studeez.data.local.models.User + interface UserDAO { - suspend fun getUsername(): String? - suspend fun save(newUsername: String) + suspend fun getUser(): User + + suspend fun saveUser( + newUsername: String, + newBiography: String = "" + ) /** * Delete all references to this user in the database. Similar to the deleteCascade in diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 3158b88..b9421de 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -2,6 +2,7 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference @@ -14,12 +15,22 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUsername(): String? { - return currentUserDocument().get().await().getString("username") + override suspend fun getUser(): User { + val userDocument = currentUserDocument().get().await() + return User( + username = userDocument.getString(USERNAME_FIELD) ?: "", + biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + ) } - override suspend fun save(newUsername: String) { - currentUserDocument().set(mapOf("username" to newUsername)) + override suspend fun saveUser( + newUsername: String, + newBiography: String + ) { + currentUserDocument().set(mapOf( + USERNAME_FIELD to newUsername, + BIOGRAPHY_FIELD to newBiography + )) } private fun currentUserDocument(): DocumentReference = @@ -27,6 +38,8 @@ class FirebaseUserDAO @Inject constructor( companion object { private const val USER_COLLECTION = "users" + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" } override suspend fun deleteUserReferences() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt index c6fcbaf..950595f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt @@ -1,20 +1,21 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicTextButton import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.ext.textButton import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.ui.theme.StudeezTheme +import be.ugent.sel.studeez.R.string as AppText data class EditProfileActions( val onUserNameChange: (String) -> Unit, + val onBiographyChange: (String) -> Unit, val onSaveClick: () -> Unit, val onDeleteClick: () -> Unit ) @@ -25,6 +26,7 @@ fun getEditProfileActions( ): EditProfileActions { return EditProfileActions( onUserNameChange = { viewModel.onUsernameChange(it) }, + onBiographyChange = { viewModel.onBiographyChange(it) }, onSaveClick = { viewModel.onSaveClick() }, onDeleteClick = { viewModel.onDeleteClick(openAndPopUp) }, ) @@ -51,28 +53,41 @@ fun EditProfileScreen( editProfileActions: EditProfileActions, ) { SecondaryScreenTemplate( - title = resources().getString(R.string.editing_profile), + title = resources().getString(AppText.editing_profile), popUp = goBack ) { - Column { - LabelledInputField( - value = uiState.username, - onNewValue = editProfileActions.onUserNameChange, - label = R.string.username - ) - BasicTextButton( - text = R.string.save, - Modifier.textButton(), - action = { - editProfileActions.onSaveClick() - goBack() - } - ) - BasicTextButton( - text = R.string.delete_profile, - Modifier.textButton(), - action = editProfileActions.onDeleteClick - ) + LazyColumn { + item { + LabelledInputField( + value = uiState.username, + onNewValue = editProfileActions.onUserNameChange, + label = AppText.username + ) + } + item { + LabelledInputField( + value = uiState.biography, + onNewValue = editProfileActions.onBiographyChange, + label = AppText.biography + ) + } + item { + BasicTextButton( + text = AppText.save, + Modifier.textButton(), + action = { + editProfileActions.onSaveClick() + goBack() + } + ) + } + item { + BasicTextButton( + text = AppText.delete_profile, + Modifier.textButton(), + action = editProfileActions.onDeleteClick + ) + } } } } @@ -81,6 +96,6 @@ fun EditProfileScreen( @Composable fun EditProfileScreenComposable() { StudeezTheme { - EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {})) + EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {}, {})) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt index 9ecaba3..c686c92 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.profile data class ProfileEditUiState ( - val username: String = "" + val username: String = "", + val biography: String = "" ) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index cb270be..d715e10 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO @@ -23,7 +24,11 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - uiState.value = uiState.value.copy(username = userDAO.getUsername()!!) + val user: User = userDAO.getUser() + uiState.value = uiState.value.copy( + username = user.username, + biography = user.biography + ) } } @@ -31,9 +36,16 @@ class ProfileEditViewModel @Inject constructor( uiState.value = uiState.value.copy(username = newValue) } + fun onBiographyChange(newValue: String) { + uiState.value = uiState.value.copy(biography = newValue) + } + fun onSaveClick() { launchCatching { - userDAO.save(uiState.value.username) + userDAO.saveUser( + newUsername = uiState.value.username, + newBiography = uiState.value.biography + ) SnackbarManager.showMessage(R.string.success) } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 9c76337..3652c70 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,17 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit -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.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R +import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions @@ -22,16 +23,18 @@ import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit, + val getBiography: suspend CoroutineScope.() -> String?, + val onEditProfileClick: () -> Unit ) fun getProfileActions( viewModel: ProfileViewModel, - open: (String) -> Unit, + open: (String) -> Unit ): ProfileActions { return ProfileActions( getUsername = { viewModel.getUsername() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) }, + getBiography = { viewModel.getBiography() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) } ) } @@ -56,8 +59,10 @@ fun ProfileScreen( navigationBarActions: NavigationBarActions, ) { var username: String? by remember { mutableStateOf("") } + var biography: String? by remember { mutableStateOf("") } LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) + biography = profileActions.getBiography(this) } PrimaryScreenTemplate( title = resources().getString(AppText.profile), @@ -65,7 +70,20 @@ fun ProfileScreen( navigationBarActions = navigationBarActions, barAction = { EditAction(onClick = profileActions.onEditProfileClick) } ) { - Headline(text = (username ?: resources().getString(R.string.no_username))) + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = username ?: resources().getString(AppText.no_username)) + } + item { + Text( + text = biography ?: "", + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } } } @@ -86,7 +104,7 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, {}), + profileActions = ProfileActions({ null }, { null }, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e24defd..760b962 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -13,8 +13,12 @@ class ProfileViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - suspend fun getUsername(): String? { - return userDAO.getUsername() + suspend fun getUsername(): String { + return userDAO.getUser().username + } + + suspend fun getBiography(): String { + return userDAO.getUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index a08d063..2a448c8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.save(username) + userDAO.saveUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d51259c..0ed2693 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ Edit profile Editing profile Delete profile + Bio From 4419c86b050b5e403e984939edb69c4600e9e20d Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:38:58 +0200 Subject: [PATCH 07/68] Unify DAO names --- .../main/java/be/ugent/sel/studeez/di/DatabaseModule.kt | 6 +++--- .../{FireBaseCollections.kt => FirebaseCollections.kt} | 2 +- .../{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} | 6 +++--- .../{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} | 6 +++--- .../{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} | 8 ++++---- .../sel/studeez/domain/implementation/FirebaseTimerDAO.kt | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseCollections.kt => FirebaseCollections.kt} (90%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} (87%) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 7ee4992..93dea2d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -26,11 +26,11 @@ abstract class DatabaseModule { abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService @Binds - abstract fun provideSessionDAO(impl: FireBaseSessionDAO): SessionDAO + abstract fun provideSessionDAO(impl: FirebaseSessionDAO): SessionDAO @Binds - abstract fun provideSubjectDAO(impl: FireBaseSubjectDAO): SubjectDAO + abstract fun provideSubjectDAO(impl: FirebaseSubjectDAO): SubjectDAO @Binds - abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO + abstract fun provideTaskDAO(impl: FirebaseTaskDAO): TaskDAO } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt similarity index 90% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index 78867c9..fcdc0a4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.domain.implementation -object FireBaseCollections { +object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" const val TIMER_COLLECTION = "timers" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt index a818236..ca7c6aa 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSessionDAO @Inject constructor( +class FirebaseSessionDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO ) : SessionDAO { @@ -31,7 +31,7 @@ class FireBaseSessionDAO @Inject constructor( } private fun currentUserSessionsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SESSION_COLLECTION) + .collection(FirebaseCollections.SESSION_COLLECTION) } \ No newline at end of file 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 similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt index 7d90fbf..fc93da1 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 @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSubjectDAO @Inject constructor( +class FirebaseSubjectDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : SubjectDAO { @@ -33,7 +33,7 @@ class FireBaseSubjectDAO @Inject constructor( } private fun currentUserSubjectsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt index b8855e6..d0185ff 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseTaskDAO @Inject constructor( +class FirebaseTaskDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : TaskDAO { @@ -41,9 +41,9 @@ class FireBaseTaskDAO @Inject constructor( } private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) .document(subjectId) - .collection(FireBaseCollections.TASK_COLLECTION) + .collection(FirebaseCollections.TASK_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt index 1f37a18..dad7047 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt @@ -48,8 +48,8 @@ class FirebaseTimerDAO @Inject constructor( } private fun currentUserTimersCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.TIMER_COLLECTION) + .collection(FirebaseCollections.TIMER_COLLECTION) } \ No newline at end of file From d6496cb8ad33c611df326480f15ed1f31094a157 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 12:44:51 +0200 Subject: [PATCH 08/68] changed labeled error field to take some states --- .../common/composable/TextFieldComposable.kt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) 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 e3c54ef..9922985 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 @@ -119,7 +117,9 @@ fun LabeledErrorTextField( initialValue: String, @StringRes label: Int, singleLine: Boolean = false, - errorText: Int, + isValid: MutableState, + 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), From b4166386a56e4e4c736745d800c23032f2944f04 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 12:46:30 +0200 Subject: [PATCH 09/68] added error input field for name and discription --- .../form_screens/AbstractTimerFormScreen.kt | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) 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 168a2f8..e936898 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 @@ -13,8 +13,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment 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.LabeledErrorTextField import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo @@ -28,8 +30,14 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { extraButton: @Composable () -> Unit ) { + var name by remember { mutableStateOf(timerInfo.name) } + val isNameValid = remember { mutableStateOf(false) } + val hasEditedName = remember { mutableStateOf(true) } + var description by remember { mutableStateOf(timerInfo.description) } + val isDescriptionValid = remember { mutableStateOf(false) } + val hasEditedDescription = remember { mutableStateOf(true) } // This shall rerun whenever name and description change timerInfo.name = name @@ -47,18 +55,30 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { ) { // Fields that every timer shares (ommited id) - LabelledInputField( - value = name, - onNewValue = { name = it }, + LabeledErrorTextField( + initialValue = timerInfo.name, label = R.string.name, - ) + errorText = AppText.name_error, + isValid = isNameValid, + isFirst = hasEditedName, + keyboardType = KeyboardType.Text, + predicate = { it.isNotBlank() } + ) { correctName -> + name = correctName + } - LabelledInputField( - value = description, - onNewValue = { description = it }, - label = AppText.description, - singleLine = false - ) + LabeledErrorTextField( + initialValue = timerInfo.description, + label = R.string.description, + errorText = AppText.description_error, + isValid = isDescriptionValid, + isFirst = hasEditedDescription, + singleLine= false, + keyboardType = KeyboardType.Text, + predicate = { it.isNotBlank() } + ) { correctName -> + description = correctName + } ExtraFields() @@ -66,7 +86,12 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column { BasicButton(R.string.save, Modifier.basicButton()) { - onSaveClick(timerInfo) + if (isNameValid.value && isDescriptionValid.value) { + onSaveClick(timerInfo) + } else { + hasEditedName.value = false + hasEditedDescription.value = false + } } extraButton() } From a328d2194f2fd62d4da27d9933050fd6a8ba6e51 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 15:36:04 +0200 Subject: [PATCH 10/68] isValid is optional and by default true --- .../ugent/sel/studeez/common/composable/TextFieldComposable.kt | 2 +- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 9922985..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 @@ -117,7 +117,7 @@ fun LabeledErrorTextField( initialValue: String, @StringRes label: Int, singleLine: Boolean = false, - isValid: MutableState, + isValid: MutableState = remember { mutableStateOf(true) }, isFirst: MutableState = remember { mutableStateOf(false) }, @StringRes errorText: Int, keyboardType: KeyboardType, 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 21e862d..333fc24 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 @@ -30,6 +30,7 @@ class BreakTimerFormScreen( initialValue = breakTimerInfo.repeats.toString(), label = R.string.repeats, errorText = AppText.repeats_error, + isValid = mutableStateOf(false), keyboardType = KeyboardType.Decimal, predicate = { it.matches(Regex("[1-9]+\\d*")) } ) { correctlyTypedInt -> From e8f2d71df3fb3c119ee30989fa8a5ce79ea8e2f2 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:25:51 +0200 Subject: [PATCH 11/68] deleteTimer Apptext --- .../be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 7cef001..cf8f374 100644 --- 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 @@ -34,7 +34,7 @@ fun TimerEditRoute( @Composable fun deleteButton() { - DeleteButton(text = AppText.delete_subject) { + DeleteButton(text = AppText.delete_timer) { viewModel.deleteTimer(viewModel.getTimerInfo(), popUp) } } From eb28fa4ae40d9255347f5d2c409405d34ff67c26 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:26:19 +0200 Subject: [PATCH 12/68] valid and first booleans in map --- .../form_screens/AbstractTimerFormScreen.kt | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) 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 e936898..8ea31d2 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 @@ -6,43 +6,37 @@ 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.runtime.* import androidx.compose.ui.Alignment 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.LabeledErrorTextField -import be.ugent.sel.studeez.common.composable.LabelledInputField 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, extraButton: @Composable () -> Unit ) { - - var name by remember { mutableStateOf(timerInfo.name) } - val isNameValid = remember { mutableStateOf(false) } - val hasEditedName = remember { mutableStateOf(true) } - - var description by remember { mutableStateOf(timerInfo.description) } - val isDescriptionValid = remember { mutableStateOf(false) } - val hasEditedDescription = remember { mutableStateOf(true) } - - // This shall rerun whenever name and description change - timerInfo.name = name - timerInfo.description = description - Column( verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier @@ -59,25 +53,25 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { initialValue = timerInfo.name, label = R.string.name, errorText = AppText.name_error, - isValid = isNameValid, - isFirst = hasEditedName, + isValid = valids.getValue("name"), + isFirst = firsts.getValue("name"), keyboardType = KeyboardType.Text, predicate = { it.isNotBlank() } ) { correctName -> - name = correctName + timerInfo.name = correctName } LabeledErrorTextField( initialValue = timerInfo.description, label = R.string.description, errorText = AppText.description_error, - isValid = isDescriptionValid, - isFirst = hasEditedDescription, + isValid = valids.getValue("description"), + isFirst = firsts.getValue("description"), singleLine= false, keyboardType = KeyboardType.Text, - predicate = { it.isNotBlank() } + predicate = { textPredicate(it) } ) { correctName -> - description = correctName + timerInfo.description = correctName } ExtraFields() @@ -86,11 +80,11 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column { BasicButton(R.string.save, Modifier.basicButton()) { - if (isNameValid.value && isDescriptionValid.value) { + if (valids.all { it.component2().value }) { // All fields are valid onSaveClick(timerInfo) } else { - hasEditedName.value = false - hasEditedDescription.value = false + firsts.map { it.component2().value = false } // dont mask error because its not been filled out yet + SnackbarManager.showMessage(AppText.fill_out_error) } } extraButton() @@ -98,6 +92,10 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { } } + private fun textPredicate(text: String): Boolean { + return text.isNotBlank() + } + @Composable open fun ExtraFields() { // By default no extra fields, unless overwritten by subclass. From 2971ae85aa59b404cb313542c918c7693fc9203d Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:27:13 +0200 Subject: [PATCH 13/68] map implementation --- .../form_screens/BreakTimerFormScreen.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 333fc24..44ae76a 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,13 +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 = mutableStateOf(false), + 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() } @@ -40,6 +46,10 @@ class BreakTimerFormScreen( } } +fun isNumber(text: String): Boolean { + return text.matches(Regex("[1-9]+\\d*")) +} + @Preview @Composable fun BreakEditScreenPreview() { From 1330396ba8f7e9294157f34bdf16d0a0d33927c7 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:38:04 +0200 Subject: [PATCH 14/68] made buttons fill out screen --- .../timer_type_select/TimerTypeSelectScreen.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 fa8d650..4825c63 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,13 +1,13 @@ 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 import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +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.* @@ -37,7 +37,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) } } From eef4a8caf5dfe979754125d2c44efb7f72bda479 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:38:29 +0200 Subject: [PATCH 15/68] added fillout error text --- app/src/main/res/values/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70c4558..303fa29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,8 +69,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! From 75411627ac2b62699653b9597b2ca8079f191327 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:06:37 +0200 Subject: [PATCH 16/68] #71 Define Friendship DAO's --- .../studeez/data/local/models/Friendship.kt | 11 +++++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 48 +++++++++++++++++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 33 +++++++++++-- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt new file mode 100644 index 0000000..98aa9a5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt @@ -0,0 +1,11 @@ +package be.ugent.sel.studeez.data.local.models + +import com.google.firebase.Timestamp +import com.google.firebase.firestore.DocumentId + +data class Friendship( + @DocumentId val id: String = "", + val friendId: String = "", + val friendsSince: Timestamp = Timestamp.now(), + val accepted: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt new file mode 100644 index 0000000..eda933d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -0,0 +1,48 @@ +package be.ugent.sel.studeez.domain + +import be.ugent.sel.studeez.data.local.models.Friendship +import kotlinx.coroutines.flow.Flow + +/** + * Should be used for interactions between friends. + */ +interface FriendshipDAO { + + /** + * @return all friendships of the user that is currently logged in. + */ + fun getAllFriendships(): Flow> + + /** + * @return the amount of friends of the currently logged in user. + * This method should be faster than just counting the length of getAllFriends() + */ + fun getFriendshipCount(): Flow + + /** + * @param id the id of the friendship that you want details of + * @return the details of a Friendship + */ + fun getFriendshipDetails(id: String): Friendship + + /** + * Send a friend request to a user. + * @param id of the user that you want to add as a friend + * @return Success/faillure of transaction + */ + fun sendFriendshipRequest(id: String): Boolean + + /** + * Accept a friend request that has already been sent. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun acceptFriendship(id: String): Boolean + + /** + * Remove a friend or decline a friendrequest. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun removeFriendship(id: String): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 619c77b..6017342 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,19 +1,44 @@ package be.ugent.sel.studeez.domain import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow interface UserDAO { - suspend fun getUser(): User + /** + * @return all users + */ + fun getAllUsers(): Flow> - suspend fun saveUser( + /** + * @return all users based on a query, a trimmed down version of getAllUsers() + */ + fun getUsersWithQuery(): Flow> + // TODO Add query parameter + + /** + * Request information about a user + */ + fun getUserDetails( + userId: String + ): Flow + + /** + * @return information on the currently logged in user. + */ + suspend fun getLoggedInUser(): User + // TODO Should be refactored to fun getLoggedInUser(): Flow, without suspend. + + suspend fun saveLoggedInUser( newUsername: String, newBiography: String = "" ) + // TODO Should be refactored to fun saveLoggedInUser(...): Boolean, without suspend. /** - * Delete all references to this user in the database. Similar to the deleteCascade in + * Delete all references to the logged in user in the database. Similar to the deleteCascade in * relational databases. */ - suspend fun deleteUserReferences() + suspend fun deleteLoggedInUserReferences() + // TODO Should be refactored to fun deleteLoggedInUserReferences(): Boolean, without suspend. } \ No newline at end of file From 56427a69af2cdd07de10c1435b71c58bb87e7103 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:30:36 +0200 Subject: [PATCH 17/68] Allow build --- .../be/ugent/sel/studeez/di/DatabaseModule.kt | 3 ++ .../implementation/FirebaseCollections.kt | 1 + .../domain/implementation/FirebaseUserDAO.kt | 39 +++++++++++++------ .../screens/profile/ProfileEditViewModel.kt | 6 +-- .../screens/profile/ProfileViewModel.kt | 4 +- .../screens/sign_up/SignUpViewModel.kt | 2 +- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 93dea2d..a24adc2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -16,6 +16,9 @@ abstract class DatabaseModule { @Binds abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO + @Binds + abstract fun provideFriendshipDAO(impl: FirebaseFriendshipDAO): FriendshipDAO + @Binds abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index fcdc0a4..042c0f0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.domain.implementation object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" + const val FRIENDS_COLLECTION = "friends" const val TIMER_COLLECTION = "timers" const val SUBJECT_COLLECTION = "subjects" const val TASK_COLLECTION = "tasks" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index b9421de..293dd1f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,6 +7,7 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -15,7 +16,30 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUser(): User { + companion object { + private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" + } + + private fun currentUserDocument(): DocumentReference = + firestore + .collection(USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllUsers(): Flow> { + TODO("Not yet implemented") + } + + override fun getUsersWithQuery(): Flow> { + TODO("Not yet implemented") + } + + override fun getUserDetails(userId: String): Flow { + TODO("Not yet implemented") + } + + override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( username = userDocument.getString(USERNAME_FIELD) ?: "", @@ -23,7 +47,7 @@ class FirebaseUserDAO @Inject constructor( ) } - override suspend fun saveUser( + override suspend fun saveLoggedInUser( newUsername: String, newBiography: String ) { @@ -33,16 +57,7 @@ class FirebaseUserDAO @Inject constructor( )) } - private fun currentUserDocument(): DocumentReference = - firestore.collection(USER_COLLECTION).document(auth.currentUserId) - - companion object { - private const val USER_COLLECTION = "users" - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" - } - - override suspend fun deleteUserReferences() { + override suspend fun deleteLoggedInUserReferences() { currentUserDocument().delete() .addOnSuccessListener { SnackbarManager.showMessage(R.string.success) } .addOnFailureListener { SnackbarManager.showMessage(R.string.generic_error) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index d715e10..9370881 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -24,7 +24,7 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - val user: User = userDAO.getUser() + val user: User = userDAO.getLoggedInUser() uiState.value = uiState.value.copy( username = user.username, biography = user.biography @@ -42,7 +42,7 @@ class ProfileEditViewModel @Inject constructor( fun onSaveClick() { launchCatching { - userDAO.saveUser( + userDAO.saveLoggedInUser( newUsername = uiState.value.username, newBiography = uiState.value.biography ) @@ -52,7 +52,7 @@ class ProfileEditViewModel @Inject constructor( fun onDeleteClick(openAndPopUp: (String, String) -> Unit) { launchCatching { - userDAO.deleteUserReferences() // Delete references + userDAO.deleteLoggedInUserReferences() // Delete references accountDAO.deleteAccount() // Delete authentication } openAndPopUp(StudeezDestinations.SIGN_UP_SCREEN, StudeezDestinations.EDIT_PROFILE_SCREEN) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index 760b962..d1fba29 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -14,11 +14,11 @@ class ProfileViewModel @Inject constructor( ) : StudeezViewModel(logService) { suspend fun getUsername(): String { - return userDAO.getUser().username + return userDAO.getLoggedInUser().username } suspend fun getBiography(): String { - return userDAO.getUser().biography + return userDAO.getLoggedInUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index 2a448c8..4cfa6a9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.saveUser(username) + userDAO.saveLoggedInUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } From 4f08453a2dbe057f6672489e036770d84b9b5eb4 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:18 +0200 Subject: [PATCH 18/68] Add getAllFriendships() --- .../implementation/FirebaseFriendshipDAO.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt new file mode 100644 index 0000000..4f2203c --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -0,0 +1,49 @@ +package be.ugent.sel.studeez.domain.implementation + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.domain.AccountDAO +import be.ugent.sel.studeez.domain.FriendshipDAO +import com.google.firebase.firestore.DocumentReference +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.snapshots +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class FirebaseFriendshipDAO @Inject constructor( + private val firestore: FirebaseFirestore, + private val auth: AccountDAO +): FriendshipDAO { + + private fun currentUserDocument(): DocumentReference = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllFriendships(): Flow> { + return currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .snapshots() + .map { it.toObjects(Friendship::class.java) } + } + + override fun getFriendshipCount(): Flow { + TODO("Not yet implemented") + } + + override fun getFriendshipDetails(id: String): Friendship { + TODO("Not yet implemented") + } + + override fun sendFriendshipRequest(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun acceptFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun removeFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + +} \ No newline at end of file From a6d4cf1369a4878516d80128bd276c1ce8d9a09f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:56 +0200 Subject: [PATCH 19/68] add getAllUsers() --- .../sel/studeez/domain/implementation/FirebaseUserDAO.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 293dd1f..df592fe 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,7 +7,9 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -28,7 +30,10 @@ class FirebaseUserDAO @Inject constructor( .document(auth.currentUserId) override fun getAllUsers(): Flow> { - TODO("Not yet implemented") + return firestore + .collection(FirebaseCollections.USER_COLLECTION) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUsersWithQuery(): Flow> { From bb64875bad5512b1f3f9861885e6c07ad9c84bb8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:39:34 +0200 Subject: [PATCH 20/68] Add getUserDetails() --- .../studeez/domain/implementation/FirebaseUserDAO.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index df592fe..8c84914 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -9,6 +9,7 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -41,7 +42,15 @@ class FirebaseUserDAO @Inject constructor( } override fun getUserDetails(userId: String): Flow { - TODO("Not yet implemented") + return flow { + val snapshot = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(userId) + .get() + .await() + val user = snapshot.toObject(User::class.java)!! + emit(user) + } } override suspend fun getLoggedInUser(): User { From 3b50054ff5ef182a6b1e54574e34a781a0c27782 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 12:07:56 +0200 Subject: [PATCH 21/68] #63 Add getFriendshipCount and add to profile screen --- .../common/composable/ButtonComposable.kt | 6 +- .../implementation/FirebaseFriendshipDAO.kt | 24 ++++++- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 4 ++ .../studeez/screens/profile/ProfileScreen.kt | 68 +++++++++++++++++-- .../screens/profile/ProfileViewModel.kt | 11 +++ app/src/main/res/values/strings.xml | 6 +- 7 files changed, 111 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 73ae1b5..2670095 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -31,7 +31,11 @@ import be.ugent.sel.studeez.common.ext.defaultButtonShape import be.ugent.sel.studeez.R.string as AppText @Composable -fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) { +fun BasicTextButton( + @StringRes text: Int, + modifier: Modifier, + action: () -> Unit +) { TextButton( onClick = action, modifier = modifier diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4f2203c..4665e0f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.domain.implementation +import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,8 +8,14 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import be.ugent.sel.studeez.R.string as AppText class FirebaseFriendshipDAO @Inject constructor( private val firestore: FirebaseFirestore, @@ -27,7 +34,22 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun getFriendshipCount(): Flow { - TODO("Not yet implemented") + return flow { + val friendshipCount = suspendCoroutine { continuation -> + currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .get() + .addOnSuccessListener { querySnapshot -> + continuation.resume(querySnapshot.size()) + } + .addOnFailureListener { exception -> + continuation.resumeWithException(exception) + } + } + emit(friendshipCount) + }.catch { + SnackbarManager.showMessage(AppText.generic_error) + } } override fun getFriendshipDetails(id: String): Friendship { diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 49856c9..2df3899 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -30,6 +30,7 @@ object StudeezDestinations { const val EDIT_TASK_FORM = "edit_task" // Friends flow + const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" // Create & edit screens 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 a09846a..0c4b803 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 @@ -220,6 +220,10 @@ fun StudeezNavGraph( } // Friends flow + composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { + // TODO + } + composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { // TODO } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 3652c70..ca59fba 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,30 +1,38 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Button import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions +import be.ugent.sel.studeez.common.ext.defaultButtonShape import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, val getBiography: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit + val getAmountOfFriends: () -> Flow, + val onEditProfileClick: () -> Unit, + val onViewFriendsClick: () -> Unit ) fun getProfileActions( @@ -34,7 +42,9 @@ fun getProfileActions( return ProfileActions( getUsername = { viewModel.getUsername() }, getBiography = { viewModel.getBiography() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) } + getAmountOfFriends = { viewModel.getAmountOfFriends() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) } ) } @@ -60,6 +70,8 @@ fun ProfileScreen( ) { var username: String? by remember { mutableStateOf("") } var biography: String? by remember { mutableStateOf("") } + val amountOfFriends = profileActions.getAmountOfFriends().collectAsState(initial = 0) + LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) biography = profileActions.getBiography(this) @@ -76,6 +88,21 @@ fun ProfileScreen( item { Headline(text = username ?: resources().getString(AppText.no_username)) } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier.fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + profileActions.onViewFriendsClick() + } + } + } + item { Text( text = biography ?: "", @@ -96,7 +123,6 @@ fun EditAction( imageVector = Icons.Default.Edit, contentDescription = resources().getString(AppText.edit_profile) ) - } } @@ -104,8 +130,38 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, { null }, {}), + profileActions = ProfileActions({ null }, { null }, { emptyFlow() }, {}, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) +} + +@Composable +fun AmountOfFriendsButton( + amountOfFriends: Int, + onClick: () -> Unit +){ + Button( + onClick = onClick, + shape = defaultButtonShape() + ) { + Text( + text = resources().getQuantityString( + /* id = */ R.plurals.friends_amount, + /* quantity = */ amountOfFriends, + /* ...formatArgs = */ amountOfFriends + ) + ) + } +} + +@Preview +@Composable +fun AmountOfFriendsButtonPreview() { + StudeezTheme { + Column { + AmountOfFriendsButton(amountOfFriends = 1) { } + AmountOfFriendsButton(amountOfFriends = 100) { } + } + } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index d1fba29..e485de6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -1,15 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow import javax.inject.Inject @HiltViewModel class ProfileViewModel @Inject constructor( private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, logService: LogService ) : StudeezViewModel(logService) { @@ -21,8 +24,16 @@ class ProfileViewModel @Inject constructor( return userDAO.getLoggedInUser().biography } + fun getAmountOfFriends(): Flow { + return friendshipDAO.getFriendshipCount() + } + fun onEditProfileClick(open: (String) -> Unit) { open(StudeezDestinations.EDIT_PROFILE_SCREEN) } + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ed2693..f370b35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,7 +20,7 @@ Success! Try again - Something wrong happened. Please try again. + Something went wrong. Please try again. Please insert a valid email. @@ -107,6 +107,10 @@ Friends Friend + + %d Friend + %d Friends + Adding friends still needs to be implemented. Hang on tight! From 87fe4767245571119e97c98ec2020a23c481b831 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 13:10:32 +0200 Subject: [PATCH 22/68] Basic friends overview screen --- .../screens/friend/FriendsOverviewScreen.kt | 218 ++++++++++++++++++ .../friend/FriendsOverviewViewModel.kt | 41 ++++ .../main/res/drawable/ic_more_horizontal.xml | 5 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 266 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt create mode 100644 app/src/main/res/drawable/ic_more_horizontal.xml diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt new file mode 100644 index 0000000..c9e80f5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt @@ -0,0 +1,218 @@ +package be.ugent.sel.studeez.screens.friend + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +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.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.ext.basicButton +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import com.google.firebase.Timestamp +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import be.ugent.sel.studeez.R.string as AppText + +data class FriendsOverviewActions( + val getFriendsFlow: () -> Flow>>, + val searchFriends: () -> Unit +) + +fun getFriendsOverviewActions( + viewModel: FriendsOverviewViewModel, + open: (String) -> Unit +): FriendsOverviewActions { + return FriendsOverviewActions( + getFriendsFlow = viewModel::getAllFriends, + searchFriends = { viewModel.searchFriends(open) } + ) +} + +@Composable +fun FriendsOveriewRoute( + open: (String) -> Unit, + popUp: () -> Unit, + viewModel: FriendsOverviewViewModel +) { + FriendsOverviewScreen( + friendsOverviewActions = getFriendsOverviewActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun FriendsOverviewScreen( + friendsOverviewActions: FriendsOverviewActions, + popUp: () -> Unit +) { + val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) + + SecondaryScreenTemplate( + title = "TODO there needs to be a search field here", // TODO + popUp = popUp + ) { + LazyColumn { + if (friends.value.isEmpty()) { + // Show a quick button to search friends when the user does not have any friends yet. + item { + BasicButton( + text = AppText.no_friends, + modifier = Modifier.basicButton() + ) { + friendsOverviewActions.searchFriends() + } + } + } + + items(friends.value) { friend -> + FriendsEntry( + user = friend.first, + friendship = friend.second + ) + } + } + } +} + +@Preview +@Composable +fun FriendsOverviewPreview() { + StudeezTheme { + FriendsOverviewScreen( + friendsOverviewActions = FriendsOverviewActions( + getFriendsFlow = { emptyFlow() }, + searchFriends = {} + ), + popUp = {} + ) + } +} + +@Composable +fun FriendsEntry( + user: User, + friendship: Friendship +) { + // TODO Styling + Row ( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + ThreeDots(friendship = friendship) + } + } +} + +@Preview +@Composable +fun FriendsEntryPreview() { + StudeezTheme { + FriendsEntry( + user = User( + id = "", + username = "Tibo De Peuter", + biography = "short bio" + ), + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} + +@Composable +fun ThreeDots( + friendship: Friendship +) { + IconButton( + onClick = { /* TODO Open dropdown */ } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } +} + +@Preview +@Composable +fun ThreeDotsPreview() { + StudeezTheme { + ThreeDots( + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt new file mode 100644 index 0000000..0dec506 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt @@ -0,0 +1,41 @@ +package be.ugent.sel.studeez.screens.friend + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapConcat +import javax.inject.Inject + +@HiltViewModel +class FriendsOverviewViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + logService: LogService +) : StudeezViewModel(logService) { + + fun getAllFriends(): Flow>> { + return friendshipDAO.getAllFriendships() + .flatMapConcat { friendships -> + val userFlows = friendships.map { friendship -> + userDAO.getUserDetails(friendship.friendId) + } + combine(userFlows) { users -> + friendships.zip(users) { friendship, user -> + Pair(user, friendship) + } + } + } + } + + fun searchFriends(open: (String) -> Unit) { + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_more_horizontal.xml b/app/src/main/res/drawable/ic_more_horizontal.xml new file mode 100644 index 0000000..afbe22d --- /dev/null +++ b/app/src/main/res/drawable/ic_more_horizontal.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f370b35..1ae1bc8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Go back Next Start + View more Success! @@ -112,6 +113,7 @@ %d Friends Adding friends still needs to be implemented. Hang on tight! + You don\'t have any friends yet. Add one! From 9d13a3395955dd933ace60e43a9552190d5d7079 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:02:56 +0200 Subject: [PATCH 23/68] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/AbstractTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8ea31d2..8afe306 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 @@ -34,7 +34,7 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { @Composable operator fun invoke( onSaveClick: (TimerInfo) -> Unit, - extraButton: @Composable () -> Unit + extraButton: @Composable () -> Unit = {}, ) { Column( From 29b976cae426d2a1879ed5bd0621dd13e6adf475 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:21 +0200 Subject: [PATCH 24/68] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 44ae76a..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 @@ -61,6 +61,6 @@ fun BreakEditScreenPreview() { 5 ) StudeezTheme { - BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}, {}) + BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From f722218abe0f8e8de0113c62478ff271ab3a440c Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:29 +0200 Subject: [PATCH 25/68] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/CustomTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt index b0000a0..27c0657 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt @@ -29,6 +29,6 @@ class CustomTimerFormScreen( fun CustomEditScreenPreview() { val customTimerInfo = CustomTimerInfo("custom", "my description", 25) StudeezTheme { - CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}, extraButton = {}) + CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From bd1c4df3012a53f25a360dbb7681e4554225188f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:37 +0200 Subject: [PATCH 26/68] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/EndlessTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt index e096946..9009fff 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt @@ -18,6 +18,6 @@ fun EndlessEditScreenPreview() { "My endless timer description", ) StudeezTheme { - EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}, {}) + EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From 6938b3e868ca5ebd48ff45d576054098dd0811a3 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:52:57 +0200 Subject: [PATCH 27/68] #118 AbstractSessionScreen -> this file --- .../composables/BreakTimerScreenComposable.kt | 2 + .../CustomTimerSessionScreenComposable.kt | 2 + .../EndlessTimerSessionScreenComposable.kt | 4 ++ .../composables/SessionScreenComposable.kt | 67 +++++++++++++++++++ .../composables/TimerComposable.kt | 2 + 5 files changed, 77 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt new file mode 100644 index 0000000..da72fe0 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt new file mode 100644 index 0000000..da72fe0 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt new file mode 100644 index 0000000..683b284 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +class EndlessTimerSessionScreenComposable { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt new file mode 100644 index 0000000..d148e19 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -0,0 +1,67 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +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.data.local.models.timer_functional.FunctionalTimer +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun SessionScreen( + open: (String) -> Unit, + sessionActions: SessionActions, + motivationString: @Composable () -> String +) { + Column( + modifier = Modifier.padding(10.dp) + ) { + Timer(sessionActions = sessionActions, motivationString = motivationString) + Box( + contentAlignment = Alignment.Center, modifier = Modifier + .fillMaxWidth() + .padding(50.dp) + ) { + EndSessionButton(sessionActions = sessionActions) + } + } +} + +@Composable +fun EndSessionButton(sessionActions: SessionActions) { + TextButton( + onClick = { + sessionActions.releaseMediaPlayer + 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/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt new file mode 100644 index 0000000..ba3f71d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens + From 5073e5cb224c8a77d0d67a3309558403dd7aa9cf Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:54:07 +0200 Subject: [PATCH 28/68] #118 subclass of AbstractSessionScreen -> this file --- .../composables/BreakTimerScreenComposable.kt | 36 +++++++++++++++++++ .../CustomTimerSessionScreenComposable.kt | 27 ++++++++++++++ .../EndlessTimerSessionScreenComposable.kt | 22 +++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index da72fe0..11cce8d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -1,2 +1,38 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +import androidx.compose.runtime.Composable +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun BreakSessionScreenComposable( + open: (String) -> Unit, + sessionActions: SessionActions, + pomodoroTimer: FunctionalPomodoroTimer +) { + SessionScreen( + open = open, + sessionActions = sessionActions + ) { + motivationString(pomodoroTimer = pomodoroTimer) + } +} + +@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().getQuantityString( + R.plurals.state_focus_remaining, + pomodoroTimer.breaksRemaining, + pomodoroTimer.breaksRemaining + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index da72fe0..bafcb19 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -1,2 +1,29 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +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 + +@Composable +fun CustomTimerSessionScreenComposable( + open: (String) -> Unit, + sessionActions: SessionActions, + customTimer: FunctionalCustomTimer +) { + SessionScreen( + open = open, + 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/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt index 683b284..b223f52 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt @@ -1,4 +1,24 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -class EndlessTimerSessionScreenComposable { +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 From fdd0429e320b69d9e5ff9648cb4be898ce4e0ef4 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:54:37 +0200 Subject: [PATCH 29/68] TimerComposable als apparte file --- .../composables/TimerComposable.kt | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index ba3f71d..6123a8a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -1,2 +1,92 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens +package be.ugent.sel.studeez.screens.session.sessionScreens.composables +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, + motivationString: @Composable () -> String +) { + var tikker by remember { mutableStateOf(false) } + LaunchedEffect(tikker) { + delay(1.seconds) + sessionActions.getTimer().tick() + // callMediaPlayer() TODO + tikker = !tikker + } + + val hms = sessionActions.getTimer().getHoursMinutesSeconds() + Column { + + TimerClock(hms) + MotivationText(text = motivationString()) + + 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 From 492775565c014b131f8821ec5220198f35b0b80e Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 13:23:11 +0200 Subject: [PATCH 30/68] #118 integrated dots in composition --- .../composables/BreakTimerScreenComposable.kt | 39 ++++++++++++++++++- .../composables/SessionScreenComposable.kt | 10 ++++- .../composables/TimerComposable.kt | 4 +- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 11cce8d..b548591 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -1,6 +1,14 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +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 @@ -14,12 +22,39 @@ fun BreakSessionScreenComposable( ) { SessionScreen( open = open, - sessionActions = sessionActions + sessionActions = sessionActions, + midSection = { Dots(pomodoroTimer) }, + motivationString = { motivationString(pomodoroTimer = pomodoroTimer) } + ) +} + +@Composable +private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { - motivationString(pomodoroTimer = pomodoroTimer) + repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining) { + Dot(color = Color.DarkGray) + } + if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(pomodoroTimer.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 private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { if (pomodoroTimer.isInBreak) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index d148e19..5ed29f8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -23,12 +23,18 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun SessionScreen( open: (String) -> Unit, sessionActions: SessionActions, - motivationString: @Composable () -> String + midSection: @Composable () -> Unit = {}, + motivationString: @Composable () -> String, + ) { Column( modifier = Modifier.padding(10.dp) ) { - Timer(sessionActions = sessionActions, motivationString = motivationString) + Timer( + sessionActions = sessionActions, + motivationString = motivationString, + midSection = midSection + ) Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 6123a8a..3b65599 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -23,7 +23,8 @@ import kotlin.time.Duration.Companion.seconds @Composable fun Timer( sessionActions: SessionActions, - motivationString: @Composable () -> String + motivationString: @Composable () -> String, + midSection: @Composable () -> Unit ) { var tikker by remember { mutableStateOf(false) } LaunchedEffect(tikker) { @@ -39,6 +40,7 @@ fun Timer( TimerClock(hms) MotivationText(text = motivationString()) + Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() From 71b9550bd04546aa87726b89faaebae9da66a238 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:08:54 +0200 Subject: [PATCH 31/68] Move profile edit screen to new package --- .../screens/profile/{ => edit_profile}/ProfileEditScreen.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditUiState.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditViewModel.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditScreen.kt (98%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditUiState.kt (62%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditViewModel.kt (97%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt index 950595f..31dcb9d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt similarity index 62% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt index c686c92..911df68 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile data class ProfileEditUiState ( val username: String = "", diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt similarity index 97% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt index 9370881..57bbbc0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R From bc7753fce53475fbe2e214e4f68e4fc1d2fdf7a9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:28 +0200 Subject: [PATCH 32/68] Move friends overview to new package and new stuff --- .../FriendsOverviewScreen.kt | 143 ++++++++++++++---- .../FriendsOverviewUiState.kt | 6 + .../FriendsOverviewViewModel.kt | 41 ++++- 3 files changed, 155 insertions(+), 35 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewScreen.kt (57%) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewViewModel.kt (53%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt similarity index 57% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt index c9e80f5..7e2462d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt @@ -1,20 +1,21 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -22,7 +23,8 @@ 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.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User @@ -35,7 +37,11 @@ import be.ugent.sel.studeez.R.string as AppText data class FriendsOverviewActions( val getFriendsFlow: () -> Flow>>, - val searchFriends: () -> Unit + val searchFriends: () -> Unit, + val onQueryStringChange: (String) -> Unit, + val onSubmit: () -> Unit, + val viewProfile: (String) -> Unit, + val removeFriend: (Friendship) -> Unit ) fun getFriendsOverviewActions( @@ -44,7 +50,13 @@ fun getFriendsOverviewActions( ): FriendsOverviewActions { return FriendsOverviewActions( getFriendsFlow = viewModel::getAllFriends, - searchFriends = { viewModel.searchFriends(open) } + searchFriends = { viewModel.searchFriends(open) }, + onQueryStringChange = viewModel::onQueryStringChange, + onSubmit = { viewModel.onSubmit(open) }, + viewProfile = { userId -> + viewModel.viewProfile(userId, open) + }, + removeFriend = viewModel::removeFriend ) } @@ -54,27 +66,52 @@ fun FriendsOveriewRoute( popUp: () -> Unit, viewModel: FriendsOverviewViewModel ) { + val uiState by viewModel.uiState FriendsOverviewScreen( + popUp = popUp, + uiState = uiState, friendsOverviewActions = getFriendsOverviewActions( viewModel = viewModel, open = open - ), - popUp = popUp + ) ) } @Composable fun FriendsOverviewScreen( - friendsOverviewActions: FriendsOverviewActions, - popUp: () -> Unit + popUp: () -> Unit, + uiState: FriendsOverviewUiState, + friendsOverviewActions: FriendsOverviewActions ) { val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) - - SecondaryScreenTemplate( - title = "TODO there needs to be a search field here", // TODO - popUp = popUp - ) { - LazyColumn { + + Scaffold( + topBar = { + TopAppBar( + title = { + // TODO Link to each other + SearchField( + value = uiState.queryString, + onValueChange = friendsOverviewActions.onQueryStringChange, + onSubmit = friendsOverviewActions.onSubmit, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + // TODO Add inbox action + ) + } + ) { paddingValues -> + LazyColumn ( + modifier = Modifier.padding(paddingValues) + ) { if (friends.value.isEmpty()) { // Show a quick button to search friends when the user does not have any friends yet. item { @@ -90,7 +127,9 @@ fun FriendsOverviewScreen( items(friends.value) { friend -> FriendsEntry( user = friend.first, - friendship = friend.second + friendship = friend.second, + viewProfile = { userId -> friendsOverviewActions.viewProfile(userId) }, + removeFriend = friendsOverviewActions.removeFriend ) } } @@ -102,11 +141,16 @@ fun FriendsOverviewScreen( fun FriendsOverviewPreview() { StudeezTheme { FriendsOverviewScreen( + popUp = {}, + uiState = FriendsOverviewUiState(""), friendsOverviewActions = FriendsOverviewActions( getFriendsFlow = { emptyFlow() }, - searchFriends = {} - ), - popUp = {} + searchFriends = {}, + onQueryStringChange = {}, + onSubmit = {}, + viewProfile = {}, + removeFriend = {} + ) ) } } @@ -114,7 +158,9 @@ fun FriendsOverviewPreview() { @Composable fun FriendsEntry( user: User, - friendship: Friendship + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { // TODO Styling Row ( @@ -162,7 +208,11 @@ fun FriendsEntry( Box( modifier = Modifier.fillMaxWidth(0.15f) ) { - ThreeDots(friendship = friendship) + FriendsOverviewDropDown( + friendship = friendship, + viewProfile = viewProfile, + removeFriend = removeFriend + ) } } } @@ -182,17 +232,23 @@ fun FriendsEntryPreview() { friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = {} ) } } @Composable -fun ThreeDots( - friendship: Friendship +fun FriendsOverviewDropDown( + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { + var expanded by remember { mutableStateOf(false) } + IconButton( - onClick = { /* TODO Open dropdown */ } + onClick = { expanded = true } ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), @@ -200,19 +256,40 @@ fun ThreeDots( modifier = Modifier.fillMaxSize() ) } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + viewProfile(friendship.friendId) + } + DrawerEntry( + icon = Icons.Default.Delete, + text = stringResource(id = AppText.remove_friend) + ) { + removeFriend(friendship) + expanded = false + } + } } @Preview @Composable -fun ThreeDotsPreview() { +fun FriendsOverviewDropDownPreview() { StudeezTheme { - ThreeDots( + FriendsOverviewDropDown( friendship = Friendship( id = "", friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = { } ) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt new file mode 100644 index 0000000..8672814 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.screens.friends.friends_overview + +data class FriendsOverviewUiState( + val userId: String, + val queryString: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt similarity index 53% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt index 0dec506..ce0c5af 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt @@ -1,5 +1,6 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview +import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,6 +8,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -17,11 +19,19 @@ import javax.inject.Inject class FriendsOverviewViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, + private val selectedProfileState: SelectedProfileState, logService: LogService ) : StudeezViewModel(logService) { + var uiState = mutableStateOf(FriendsOverviewUiState( + userId = selectedProfileState.selectedUserId + )) + private set + fun getAllFriends(): Flow>> { - return friendshipDAO.getAllFriendships() + return friendshipDAO.getAllFriendships( + userId = uiState.value.userId + ) .flatMapConcat { friendships -> val userFlows = friendships.map { friendship -> userDAO.getUserDetails(friendship.friendId) @@ -38,4 +48,31 @@ class FriendsOverviewViewModel @Inject constructor( open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) } + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + } + + fun onSubmit(open: (String) -> Unit) { + val query = uiState.value.queryString // TODO Pass as argument + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + + fun viewProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } + + fun removeFriend( + friendship: Friendship + ) { + friendshipDAO.removeFriendship( + friendship = friendship + ) + } + } \ No newline at end of file From a6a5fb5e958548d318054063dc3d37405cdd7337 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:55 +0200 Subject: [PATCH 33/68] Add public profile --- .../public_profile/PublicProfileScreen.kt | 178 ++++++++++++++++++ .../public_profile/PublicProfileUiState.kt | 5 + .../public_profile/PublicProfileViewModel.kt | 60 ++++++ .../public_profile/SelectedProfileState.kt | 12 ++ 4 files changed, 255 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt new file mode 100644 index 0000000..41e33c5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt @@ -0,0 +1,178 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MailOutline +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.Headline +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.profile.AmountOfFriendsButton +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import be.ugent.sel.studeez.R.string as AppText + +data class PublicProfileActions( + val getUserDetails: () -> Flow, + val getAmountOfFriends: () -> Flow, + val onViewFriendsClick: () -> Unit, + val sendFriendRequest: () -> Boolean +) + +fun getPublicProfileActions( + viewModel: PublicProfileViewModel, + open: (String) -> Unit +): PublicProfileActions { + return PublicProfileActions( + getUserDetails = { viewModel.getUserDetails(viewModel.uiState.value.userId) }, + getAmountOfFriends = { viewModel.getAmountOfFriends( + userId = viewModel.uiState.value.userId + ) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, + sendFriendRequest = { viewModel.sendFriendRequest( + userId = viewModel.uiState.value.userId + ) } + ) +} + +@Composable +fun PublicProfileRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: PublicProfileViewModel +) { + PublicProfileScreen( + publicProfileActions = getPublicProfileActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun PublicProfileScreen( + publicProfileActions: PublicProfileActions, + popUp: () -> Unit +) { + val user = publicProfileActions.getUserDetails().collectAsState(initial = User()) + val amountOfFriends = publicProfileActions.getAmountOfFriends().collectAsState(initial = 0) + + SecondaryScreenTemplate( + title = stringResource(id = AppText.profile), + popUp = popUp, + barAction = { + PublicProfileEllipsis( + sendFriendRequest = publicProfileActions.sendFriendRequest + ) + } + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = user.value.username) + } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier + .fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + publicProfileActions.onViewFriendsClick() + } + } + } + + item { + Text( + text = user.value.biography, + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } + } +} + +@Preview +@Composable +fun PublicProfilePreview() { + StudeezTheme { + PublicProfileScreen( + publicProfileActions = PublicProfileActions( + getUserDetails = { + flowOf(User( + id = "someid", + username = "Maxime De Poorter", + biography = "I am a different student and this is my public profile" + )) + }, + getAmountOfFriends = { flowOf(113) }, + onViewFriendsClick = {}, + sendFriendRequest = { true } + ), + popUp = {} + ) + } +} + +@Composable +fun PublicProfileEllipsis( + sendFriendRequest: () -> Boolean +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.MailOutline, + text = stringResource(id = AppText.send_friend_request) + ) { + sendFriendRequest() + } + } + } +} + +@Preview +@Composable +fun PublicProfileEllipsisPreview() { + StudeezTheme { + PublicProfileEllipsis( + sendFriendRequest = { true } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt new file mode 100644 index 0000000..537fed9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt @@ -0,0 +1,5 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +data class PublicProfileUiState( + var userId: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt new file mode 100644 index 0000000..6cf22d9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt @@ -0,0 +1,60 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.selects.select +import javax.inject.Inject + +@HiltViewModel +class PublicProfileViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + val uiState = mutableStateOf( + PublicProfileUiState( + userId = selectedProfileState.selectedUserId + ) + ) + + fun getUserDetails( + userId: String + ): Flow { + uiState.value = uiState.value.copy( + userId = userId + ) + return userDAO.getUserDetails( + userId = uiState.value.userId + ) + } + + fun getAmountOfFriends( + userId: String + ): Flow { + return friendshipDAO.getFriendshipCount( + userId = userId + ) + } + + fun onViewFriendsClick( + open: (String) -> Unit + ) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + + fun sendFriendRequest( + userId: String + ): Boolean { + return friendshipDAO.sendFriendshipRequest(userId) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt new file mode 100644 index 0000000..4226d65 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt @@ -0,0 +1,12 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import be.ugent.sel.studeez.domain.UserDAO +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SelectedProfileState @Inject constructor( + userDAO: UserDAO +) { + var selectedUserId: String = userDAO.getCurrentUserId() +} \ No newline at end of file From 566102d5d4868e2a4331666661e0ea6e74bba43f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:13:13 +0200 Subject: [PATCH 34/68] More with friendship --- .../studeez/data/remote/FirebaseFriendship.kt | 7 ++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 18 ++-- .../implementation/FirebaseFriendshipDAO.kt | 83 ++++++++++++++++--- .../screens/profile/ProfileViewModel.kt | 3 +- 4 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt new file mode 100644 index 0000000..fb2af4b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt @@ -0,0 +1,7 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseFriendship { + const val FRIENDID: String = "friendId" + const val ACCEPTED: String = "accepted" + const val FRIENDSSINCE: String = "friendsSince" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt index eda933d..0beb01a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -9,15 +9,19 @@ import kotlinx.coroutines.flow.Flow interface FriendshipDAO { /** - * @return all friendships of the user that is currently logged in. + * @return all friendships of a chosen user. */ - fun getAllFriendships(): Flow> + fun getAllFriendships( + userId: String + ): Flow> /** - * @return the amount of friends of the currently logged in user. + * @return the amount of friends of a chosen user. * This method should be faster than just counting the length of getAllFriends() */ - fun getFriendshipCount(): Flow + fun getFriendshipCount( + userId: String + ): Flow /** * @param id the id of the friendship that you want details of @@ -41,8 +45,10 @@ interface FriendshipDAO { /** * Remove a friend or decline a friendrequest. - * @param id of the friendship that you want to update + * @param friendship the one you want to remove * @return: Success/faillure of transaction */ - fun removeFriendship(id: String): Boolean + fun removeFriendship( + friendship: Friendship + ): Boolean } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4665e0f..bd429e1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,9 +1,16 @@ package be.ugent.sel.studeez.domain.implementation +import androidx.compose.runtime.collectAsState import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.ACCEPTED +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDSSINCE +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDID import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.FRIENDS_COLLECTION +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION +import com.google.firebase.Timestamp import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -23,21 +30,29 @@ class FirebaseFriendshipDAO @Inject constructor( ): FriendshipDAO { private fun currentUserDocument(): DocumentReference = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(auth.currentUserId) - override fun getAllFriendships(): Flow> { - return currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + override fun getAllFriendships( + userId: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .snapshots() .map { it.toObjects(Friendship::class.java) } } - override fun getFriendshipCount(): Flow { + override fun getFriendshipCount( + userId: String + ): Flow { return flow { val friendshipCount = suspendCoroutine { continuation -> - currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .get() .addOnSuccessListener { querySnapshot -> continuation.resume(querySnapshot.size()) @@ -57,15 +72,63 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun sendFriendshipRequest(id: String): Boolean { - TODO("Not yet implemented") + val currentUserId: String = auth.currentUserId + val otherUserId: String = id + + // Add entry to current user + currentUserDocument() + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to otherUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + // Add entry to other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to currentUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + return true } override fun acceptFriendship(id: String): Boolean { TODO("Not yet implemented") } - override fun removeFriendship(id: String): Boolean { - TODO("Not yet implemented") + override fun removeFriendship( + friendship: Friendship + ): Boolean { + val currentUserId: String = auth.currentUserId + val otherUserId: String = friendship.friendId + + // Remove at logged in user + firestore.collection(USER_COLLECTION) + .document(currentUserId) + .collection(FRIENDS_COLLECTION) + .document(friendship.id) + .delete() + + // Remove at other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .whereEqualTo(FRIENDID, currentUserId) + .get() + .addOnSuccessListener { + for (document in it) { + document.reference.delete() + } + }.addOnFailureListener { + SnackbarManager.showMessage(AppText.generic_error) + } + + return true } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e485de6..93fa086 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -5,6 +5,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import com.google.firebase.auth.FirebaseAuth import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -25,7 +26,7 @@ class ProfileViewModel @Inject constructor( } fun getAmountOfFriends(): Flow { - return friendshipDAO.getFriendshipCount() + return friendshipDAO.getFriendshipCount(userDAO.getCurrentUserId()) } fun onEditProfileClick(open: (String) -> Unit) { From 7c95e78b2a8eb168ce25cbf9049ad1a243a201e9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:26 +0200 Subject: [PATCH 35/68] More with UserDAO --- .../sel/studeez/data/remote/FirebaseUser.kt | 6 ++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 8 +++-- .../domain/implementation/FirebaseUserDAO.kt | 34 ++++++++++++------- 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt new file mode 100644 index 0000000..9ee5aa2 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseUser { + const val USERNAME: String = "username" + const val BIOGRAPHY: String = "biography" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 6017342..09179f1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.Flow interface UserDAO { + fun getCurrentUserId(): String + /** * @return all users */ @@ -13,8 +15,10 @@ interface UserDAO { /** * @return all users based on a query, a trimmed down version of getAllUsers() */ - fun getUsersWithQuery(): Flow> - // TODO Add query parameter + fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> /** * Request information about a user diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 8c84914..f41fcd9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -3,8 +3,11 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser.BIOGRAPHY +import be.ugent.sel.studeez.data.remote.FirebaseUser.USERNAME import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -17,12 +20,10 @@ import javax.inject.Inject class FirebaseUserDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO - ) : UserDAO { +) : UserDAO { - companion object { - private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" + override fun getCurrentUserId(): String { + return auth.currentUserId } private fun currentUserDocument(): DocumentReference = @@ -32,19 +33,26 @@ class FirebaseUserDAO @Inject constructor( override fun getAllUsers(): Flow> { return firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .snapshots() .map { it.toObjects(User::class.java) } } - override fun getUsersWithQuery(): Flow> { - TODO("Not yet implemented") + override fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .whereEqualTo(fieldName, value) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUserDetails(userId: String): Flow { return flow { val snapshot = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(userId) .get() .await() @@ -56,8 +64,8 @@ class FirebaseUserDAO @Inject constructor( override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( - username = userDocument.getString(USERNAME_FIELD) ?: "", - biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + username = userDocument.getString(USERNAME) ?: "", + biography = userDocument.getString(BIOGRAPHY) ?: "" ) } @@ -66,8 +74,8 @@ class FirebaseUserDAO @Inject constructor( newBiography: String ) { currentUserDocument().set(mapOf( - USERNAME_FIELD to newUsername, - BIOGRAPHY_FIELD to newBiography + USERNAME to newUsername, + BIOGRAPHY to newBiography )) } From 74a6f77261661728761380ff21c54fae25fed211 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:49 +0200 Subject: [PATCH 36/68] Add friends search screen --- .../friends_search/SearchFriendUiState.kt | 10 + .../friends_search/SearchFriendsScreen.kt | 269 ++++++++++++++++++ .../friends_search/SearchFriendsViewModel.kt | 57 ++++ 3 files changed, 336 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt new file mode 100644 index 0000000..0a5a10f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt @@ -0,0 +1,10 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +data class SearchFriendUiState( + val queryString: String = "", + val searchResults: Flow> = emptyFlow() +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt new file mode 100644 index 0000000..a91d0f4 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt @@ -0,0 +1,269 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +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.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.* +import be.ugent.sel.studeez.R.string as AppText + +data class SearchFriendsActions( + val onQueryStringChange: (String) -> Unit, + val getUsersWithUsername: (String) -> Unit, + val getAllUsers: () -> Flow>, + val goToProfile: (String) -> Unit +) + +fun getSearchFriendsActions( + viewModel: SearchFriendsViewModel, + open: (String) -> Unit +): SearchFriendsActions { + return SearchFriendsActions( + onQueryStringChange = viewModel::onQueryStringChange, + getUsersWithUsername = viewModel::getUsersWithUsername, + getAllUsers = { viewModel.getAllUsers() }, + goToProfile = { userId -> viewModel.goToProfile(userId, open) } + ) +} + +@Composable +fun SearchFriendsRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: SearchFriendsViewModel +) { + val uiState by viewModel.uiState + + SearchFriendsScreen( + popUp = popUp, + uiState = uiState, + searchFriendsActions = getSearchFriendsActions( + viewModel = viewModel, + open = open + ) + ) +} + +@Composable +fun SearchFriendsScreen( + popUp: () -> Unit, + uiState: SearchFriendUiState, + searchFriendsActions: SearchFriendsActions +) { + var query by remember { mutableStateOf(uiState.queryString) } + val searchResults = searchFriendsActions.getAllUsers().collectAsState( + initial = emptyList() + ) + + Scaffold( + topBar = { + TopAppBar( + title = { + SearchField( + value = query, + onValueChange = { newValue -> + searchFriendsActions.onQueryStringChange(newValue) + query = newValue + }, + onSubmit = { }, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + items (searchResults.value) { user -> + UserEntry( + user = user, + goToProfile = searchFriendsActions.goToProfile + ) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsPreview() { + StudeezTheme { + SearchFriendsScreen( + popUp = {}, + uiState = SearchFriendUiState( + queryString = "dit is een test", + searchResults = flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + ), + searchFriendsActions = SearchFriendsActions( + onQueryStringChange = {}, + getUsersWithUsername = {}, + getAllUsers = { + flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + }, + goToProfile = { } + ) + ) + } +} + +@Composable +fun UserEntry( + user: User, + goToProfile: (String) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + SearchFriendsDropDown( + user = user, + goToProfile = goToProfile + ) + } + } +} + +@Preview +@Composable +fun UserEntryPreview() { + StudeezTheme { + UserEntry( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} + +/** + * Three dots that open a dropdown menu that allow to go the users profile. + */ +@Composable +fun SearchFriendsDropDown( + user: User, + goToProfile: (String) -> Unit +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = stringResource(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + goToProfile(user.id) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsDropDownPreview() { + StudeezTheme { + SearchFriendsDropDown( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt new file mode 100644 index 0000000..a25e1a7 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt @@ -0,0 +1,57 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +@HiltViewModel +class SearchFriendsViewModel @Inject constructor( + private val userDAO: UserDAO, + private val selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + var uiState = mutableStateOf(SearchFriendUiState()) + private set + + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + uiState.value = uiState.value.copy( + searchResults = userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = uiState.value.queryString + ) + ) + } + + fun getUsersWithUsername( + value: String + ): Flow> { + return userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = value + ) + } + + fun getAllUsers(): Flow> { + return userDAO.getAllUsers() + } + + fun goToProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } +} \ No newline at end of file From f03a06b24594a32b13811018128a1abfc11d401a Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:10 +0200 Subject: [PATCH 37/68] Update navigation --- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 2df3899..578c74a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -32,6 +32,7 @@ object StudeezDestinations { // Friends flow const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" + const val PUBLIC_PROFILE_SCREEN = "public_profile" // Create & edit screens const val CREATE_TASK_SCREEN = "create_task" 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 0c4b803..d6264fc 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 @@ -14,10 +14,13 @@ import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions +import be.ugent.sel.studeez.screens.friends.friends_overview.FriendsOveriewRoute +import be.ugent.sel.studeez.screens.friends.friends_search.SearchFriendsRoute import be.ugent.sel.studeez.screens.home.HomeRoute import be.ugent.sel.studeez.screens.log_in.LoginRoute -import be.ugent.sel.studeez.screens.profile.EditProfileRoute +import be.ugent.sel.studeez.screens.profile.edit_profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute +import be.ugent.sel.studeez.screens.profile.public_profile.PublicProfileRoute import be.ugent.sel.studeez.screens.session.SessionRoute import be.ugent.sel.studeez.screens.session_recap.SessionRecapRoute import be.ugent.sel.studeez.screens.sessions.SessionsRoute @@ -221,11 +224,27 @@ fun StudeezNavGraph( // Friends flow composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { - // TODO + FriendsOveriewRoute( + open = open, + popUp = goBack, + viewModel = hiltViewModel() + ) } composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { - // TODO + SearchFriendsRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) + } + + composable(StudeezDestinations.PUBLIC_PROFILE_SCREEN) { + PublicProfileRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) } // Create & edit screens From b7ffc7d802a4f3b08c0f415793d658982ac6c2db Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:36 +0200 Subject: [PATCH 38/68] Add searchfield composable --- .../common/composable/TextFieldComposable.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) 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..dfd989c 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 @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.common.composable import androidx.annotation.StringRes +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -10,6 +11,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Email import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -22,6 +24,7 @@ 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 com.google.android.material.color.MaterialColors import kotlin.math.sin import be.ugent.sel.studeez.R.drawable as AppIcon import be.ugent.sel.studeez.R.string as AppText @@ -218,4 +221,34 @@ private fun PasswordField( keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), visualTransformation = visualTransformation ) +} + +@Composable +fun SearchField( + value: String, + onValueChange: (String) -> Unit, + onSubmit: () -> Unit, + @StringRes label: Int, + modifier: Modifier = Modifier +) { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + label = { Text(text = stringResource(id = label)) }, + trailingIcon = { + IconButton(onClick = onSubmit) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(label), + tint = MaterialTheme.colors.primary + ) + } + }, + singleLine = true, + colors = TextFieldDefaults.outlinedTextFieldColors( + textColor = MaterialTheme.colors.onBackground, + backgroundColor = MaterialTheme.colors.background + ) + ) } \ No newline at end of file From 70f1be9b2223641a37a12504303237870b3bba01 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:11 +0200 Subject: [PATCH 39/68] Add friends button to home screen --- .../be/ugent/sel/studeez/screens/home/HomeScreen.kt | 11 ++++++++--- .../ugent/sel/studeez/screens/home/HomeViewModel.kt | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index f02852e..689e512 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -24,6 +24,7 @@ fun HomeRoute( ) { HomeScreen( onStartSessionClick = { viewModel.onStartSessionClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, drawerActions = drawerActions, navigationBarActions = navigationBarActions, ) @@ -32,6 +33,7 @@ fun HomeRoute( @Composable fun HomeScreen( onStartSessionClick: () -> Unit, + onViewFriendsClick: () -> Unit, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions ) { @@ -39,7 +41,7 @@ fun HomeScreen( title = resources().getString(R.string.home), drawerActions = drawerActions, navigationBarActions = navigationBarActions, - // TODO barAction = { FriendsAction() } + barAction = { FriendsAction(onViewFriendsClick) } ) { BasicButton(R.string.start_session, Modifier.basicButton()) { onStartSessionClick() @@ -48,8 +50,10 @@ fun HomeScreen( } @Composable -fun FriendsAction() { - IconButton(onClick = { /*TODO*/ }) { +fun FriendsAction( + onClick: () -> Unit +) { + IconButton(onClick = onClick) { Icon( imageVector = Icons.Default.Person, contentDescription = resources().getString(R.string.friends) @@ -62,6 +66,7 @@ fun FriendsAction() { fun HomeScreenPreview() { HomeScreen( onStartSessionClick = {}, + onViewFriendsClick = {}, drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt index b27f995..e757b15 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt @@ -16,4 +16,8 @@ class HomeViewModel @Inject constructor( fun onStartSessionClick(open: (String) -> Unit) { open(StudeezDestinations.TIMER_SELECTION_SCREEN) } + + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } } \ No newline at end of file From 10c86c9bf088216866ba3d7e2e00130342ed96f6 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:30 +0200 Subject: [PATCH 40/68] Add resources --- app/src/main/res/values/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ae1bc8..4a72fdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,10 @@ Adding friends still needs to be implemented. Hang on tight! You don\'t have any friends yet. Add one! + Search friends + Send friend request + Remove as friend + Show profile From 522ecae87a1ebcd21b5583affa43004674c23bce Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 15:11:50 +0200 Subject: [PATCH 41/68] fist test --- .idea/androidTestResultsUserPreferences.xml | 50 +++++++++++++ .idea/misc.xml | 3 +- app/build.gradle | 10 ++- .../be/ugent/sel/studeez/SessionScreenTest.kt | 70 +++++++++++++++++++ .../InvisibleSessionManagerTest.kt | 1 - build.gradle | 7 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 8 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 .idea/androidTestResultsUserPreferences.xml create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml new file mode 100644 index 0000000..a751b96 --- /dev/null +++ b/.idea/androidTestResultsUserPreferences.xml @@ -0,0 +1,50 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..773fe0f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,6 @@ - - + diff --git a/app/build.gradle b/app/build.gradle index a19cbd7..bc5cfaf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,8 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.compose.material:material:1.2.0' + implementation 'androidx.test.ext:junit-ktx:1.1.5' + implementation 'androidx.navigation:navigation-testing:2.5.3' debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" @@ -96,13 +98,17 @@ dependencies { // JUnit testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" // Coroutine testing testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' // Mocking testImplementation 'org.mockito.kotlin:mockito-kotlin:3.2.0' + androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1' + androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0" // Networking implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -111,8 +117,6 @@ dependencies { testImplementation 'androidx.arch.core:core-testing:2.1.0' // GUI testing - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" //Firebase diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt new file mode 100644 index 0000000..65fb5eb --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt @@ -0,0 +1,70 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.Modifier +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.SessionReport +import be.ugent.sel.studeez.screens.session_recap.SessionRecapActions +import be.ugent.sel.studeez.screens.session_recap.SessionRecapScreen +import com.google.firebase.Timestamp +import org.junit.Assert +import org.junit.Rule +import org.junit.Test + + +class SessionScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun SessionRecapTest() { + var saveCalled = false + var discardCalled = false + + composeTestRule.setContent { + SessionRecapScreen( + Modifier, + SessionRecapActions( + { + SessionReport( + "", + 0, + Timestamp(0, 0), + "") + }, + { saveCalled = true }, + { discardCalled = true } + ) + ) + } + composeTestRule + .onNodeWithText( + "You studied", + substring = true, + ignoreCase = true + ) + .assertExists() + + composeTestRule + .onNodeWithText( + "save", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "discard", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + Assert.assertTrue(saveCalled) + Assert.assertTrue(discardCalled) + } +} 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 54f673d..0c973a5 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 @@ -6,7 +6,6 @@ import be.ugent.sel.studeez.data.SelectedTimer 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.domain.LogService import be.ugent.sel.studeez.domain.implementation.LogServiceImpl import be.ugent.sel.studeez.screens.session.InvisibleSessionManager import be.ugent.sel.studeez.screens.session.SessionViewModel diff --git a/build.gradle b/build.gradle index 7f25617..7f70dd1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ buildscript { ext { - compose_ui_version = '1.2.0' compose_version = '1.1.1' coreTestingVersion = '2.1.0' espressoVersion = '3.4.0' @@ -9,13 +8,13 @@ buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.0.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.2' apply false - id 'com.android.library' version '7.4.2' apply false + id 'com.android.application' version '8.0.0' apply false + id 'com.android.library' version '8.0.0' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false // Hilt diff --git a/gradle.properties b/gradle.properties index edf11ef..8581bd2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 435a2d6..a38b6fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Mar 25 16:03:50 CET 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 74526bc98451a3c30177b4e92a60ba35b82f5f92 Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 15:58:59 +0200 Subject: [PATCH 42/68] proto error --- .idea/androidTestResultsUserPreferences.xml | 26 ++++++++++++ app/build.gradle | 12 ++---- .../be/ugent/sel/studeez/HomeScreenTest.kt | 42 +++++++++++++++++++ ...nstrumentedTest.kt => InstrumentedTest.kt} | 2 +- .../be/ugent/sel/studeez/SessionScreenTest.kt | 4 +- build.gradle | 8 ++-- 6 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt rename app/src/androidTest/java/be/ugent/sel/studeez/{ExampleInstrumentedTest.kt => InstrumentedTest.kt} (95%) diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index a751b96..59f6bc1 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -16,6 +16,19 @@ + + + + + + + @@ -44,6 +57,19 @@ + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index bc5cfaf..197b055 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,8 +65,6 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.compose.material:material:1.2.0' - implementation 'androidx.test.ext:junit-ktx:1.1.5' - implementation 'androidx.navigation:navigation-testing:2.5.3' debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" @@ -98,17 +96,13 @@ dependencies { // JUnit testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" + androidTestImplementation 'androidx.test.ext:junit:1.1.5' // Coroutine testing testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' // Mocking testImplementation 'org.mockito.kotlin:mockito-kotlin:3.2.0' - androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1' - androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0" // Networking implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -117,6 +111,8 @@ dependencies { testImplementation 'androidx.arch.core:core-testing:2.1.0' // GUI testing + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" //Firebase @@ -154,4 +150,4 @@ protobuf { } } } -} +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt new file mode 100644 index 0000000..fa60dee --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt @@ -0,0 +1,42 @@ +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.common.composable.drawer.DrawerActions +import be.ugent.sel.studeez.common.composable.feed.FeedUiState +import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions +import be.ugent.sel.studeez.data.local.models.FeedEntry +import be.ugent.sel.studeez.screens.home.HomeScreen +import org.junit.Rule +import org.junit.Test + +class HomeScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun sessionRecapTestt() { + var saveCalled = false + + composeTestRule.setContent { + HomeScreen( + open = {}, + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({false}, {}, {}, {}, {}, {}, {}, {}), + feedUiState = FeedUiState.Succes(mapOf()), + continueTask = {_, _ -> }, + onEmptyFeedHelp = {} + ) + } + + composeTestRule + .onNodeWithText( + "continue", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt similarity index 95% rename from app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt rename to app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt index 06f9435..d6a1522 100644 --- a/app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt @@ -14,7 +14,7 @@ import org.junit.Assert.* * See [testing documentation](http://d.android.com/tools/testing). */ @RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { +class InstrumentedTest { @Test fun useAppContext() { // Context of the app under test. diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt index 65fb5eb..995f0a2 100644 --- a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt @@ -18,7 +18,7 @@ class SessionScreenTest { val composeTestRule = createComposeRule() @Test - fun SessionRecapTest() { + fun sessionRecapTest() { var saveCalled = false var discardCalled = false @@ -67,4 +67,6 @@ class SessionScreenTest { Assert.assertTrue(saveCalled) Assert.assertTrue(discardCalled) } + + } diff --git a/build.gradle b/build.gradle index 7f70dd1..4535dd7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ buildscript { ext { + compose_ui_version = '1.2.0' compose_version = '1.1.1' coreTestingVersion = '2.1.0' espressoVersion = '3.4.0' @@ -8,16 +9,15 @@ buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.android.tools.build:gradle:8.0.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.0.0' apply false - id 'com.android.library' version '8.0.0' apply false + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false // Hilt id 'com.google.dagger.hilt.android' version '2.44' apply false } - From b614f7d530204521f910892466893f13fe87769c Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:30:13 +0200 Subject: [PATCH 43/68] Dots now returns an int --- .../composables/BreakTimerScreenComposable.kt | 21 +++++++++++-------- .../composables/GetSessionScreenComposable.kt | 4 ++++ .../composables/SessionScreenComposable.kt | 4 ++-- .../composables/TimerComposable.kt | 4 ++-- 4 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index b548591..89bcc24 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables 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 @@ -18,18 +19,18 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun BreakSessionScreenComposable( open: (String) -> Unit, sessionActions: SessionActions, - pomodoroTimer: FunctionalPomodoroTimer + pomodoroTimer: FunctionalPomodoroTimer, ) { SessionScreen( open = open, sessionActions = sessionActions, - midSection = { Dots(pomodoroTimer) }, - motivationString = { motivationString(pomodoroTimer = pomodoroTimer) } + midSection = { Dots(pomodoroTimer = pomodoroTimer) }, + motivationString = { motivationString (pomodoroTimer = pomodoroTimer) } ) } @Composable -private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { +private fun Dots(pomodoroTimer: FunctionalPomodoroTimer): Int { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -43,6 +44,7 @@ private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { Dot(color = Color.Gray) } } + return pomodoroTimer.breaksRemaining } @Composable @@ -65,9 +67,10 @@ private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { return resources().getString(R.string.state_done) } - return resources().getQuantityString( - R.plurals.state_focus_remaining, - pomodoroTimer.breaksRemaining, - pomodoroTimer.breaksRemaining - ) + return resources().getString(R.string.state_focus) +} + +@Composable +private fun test(pomodoroTimer: FunctionalPomodoroTimer): String { + return pomodoroTimer.breaksRemaining.toString() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt new file mode 100644 index 0000000..3f8762b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +class GetSessionScreenComposable { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index 5ed29f8..b31ee45 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -23,7 +23,7 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun SessionScreen( open: (String) -> Unit, sessionActions: SessionActions, - midSection: @Composable () -> Unit = {}, + midSection: @Composable () -> Int = {0}, motivationString: @Composable () -> String, ) { @@ -33,7 +33,7 @@ fun SessionScreen( Timer( sessionActions = sessionActions, motivationString = motivationString, - midSection = midSection + MidSection = midSection ) Box( contentAlignment = Alignment.Center, modifier = Modifier diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 3b65599..87a6839 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds fun Timer( sessionActions: SessionActions, motivationString: @Composable () -> String, - midSection: @Composable () -> Unit + MidSection: @Composable () -> Int ) { var tikker by remember { mutableStateOf(false) } LaunchedEffect(tikker) { @@ -39,7 +39,7 @@ fun Timer( TimerClock(hms) MotivationText(text = motivationString()) - + MidSection() Box( contentAlignment = Alignment.Center, modifier = Modifier From 052ebb8c32996f48ec376d17cab9e35891b3802d Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:31:36 +0200 Subject: [PATCH 44/68] #118 new visitor for session screen --- .../composables/GetSessionScreenComposable.kt | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt index 3f8762b..bae4d92 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -1,4 +1,49 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -class GetSessionScreenComposable { +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.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.sessionScreens.AbstractSessionScreen +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 + +class GetSessionScreenComposable( + private val mediaplayer: MediaPlayer?, + 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, + 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, + pomodoroTimer = functionalPomodoroTimer + ) + } + } } \ No newline at end of file From 4466f3646f9d730be0b1f8009be25174552a2cfc Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:34:19 +0200 Subject: [PATCH 45/68] #118 gebruik nieuwe visitor om sessionScreen te instantieren --- .../ugent/sel/studeez/screens/session/SessionRoute.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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..959bc74 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 @@ -5,9 +5,12 @@ 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.FunctionalPomodoroTimer 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.composables.BreakSessionScreenComposable +import be.ugent.sel.studeez.screens.session.sessionScreens.composables.GetSessionScreenComposable data class SessionActions( val getTimer: () -> FunctionalTimer, @@ -47,10 +50,8 @@ fun SessionRoute( mediaplayer = mediaplayer ) - val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen(mediaplayer)) + val sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) + val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(mediaplayer, open, sessionActions)) - sessionScreen( - open = open, - sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) - ) + sessionScreen() } From 4519bf2e3037766c979a8f32b485dbe7f8bfee5a Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 18:57:24 +0200 Subject: [PATCH 46/68] added soundplayer --- .../session/sessionScreens/GetSessionScreen.kt | 18 ------------------ .../session/sessionScreens/SoundPlayer.kt | 4 ++++ .../composables/BreakTimerScreenComposable.kt | 9 +++------ .../CustomTimerSessionScreenComposable.kt | 5 ++++- 4 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt 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/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt new file mode 100644 index 0000000..f9c0942 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens + +class SoundPlayer { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 89bcc24..57d73e2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -3,7 +3,6 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables 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 @@ -14,17 +13,20 @@ 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.sessionScreens.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) } ) } @@ -68,9 +70,4 @@ private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { } return resources().getString(R.string.state_focus) -} - -@Composable -private fun test(pomodoroTimer: FunctionalPomodoroTimer): String { - return pomodoroTimer.breaksRemaining.toString() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index bafcb19..5cdc62c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -5,15 +5,18 @@ 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.sessionScreens.SoundPlayer @Composable fun CustomTimerSessionScreenComposable( open: (String) -> Unit, sessionActions: SessionActions, - customTimer: FunctionalCustomTimer + customTimer: FunctionalCustomTimer, + soundPlayer: SoundPlayer ) { SessionScreen( open = open, + callMediaPlayer = { soundPlayer.playOn(customTimer.hasEnded()) }, sessionActions = sessionActions ) { motivationString(customTimer = customTimer) From 4a04a703db101141b396f5d52f130bd10951d37f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 18:58:46 +0200 Subject: [PATCH 47/68] soundplayer wrapper to make mediaplayer easier to work with --- .../studeez/screens/session/SoundPlayer.kt | 29 +++++++++++++++++++ .../session/sessionScreens/SoundPlayer.kt | 4 --- 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt 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/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt deleted file mode 100644 index f9c0942..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt +++ /dev/null @@ -1,4 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -class SoundPlayer { -} \ No newline at end of file From 1db5a4160ea5c84b996b50df79286f291142bd1a Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:00:36 +0200 Subject: [PATCH 48/68] refactor soundplayer location --- .../sessionScreens/composables/BreakTimerScreenComposable.kt | 2 +- .../composables/CustomTimerSessionScreenComposable.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 57d73e2..cac3509 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -13,7 +13,7 @@ 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.sessionScreens.SoundPlayer +import be.ugent.sel.studeez.screens.session.SoundPlayer @Composable fun BreakSessionScreenComposable( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index 5cdc62c..ec3c2d8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -5,7 +5,7 @@ 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.sessionScreens.SoundPlayer +import be.ugent.sel.studeez.screens.session.SoundPlayer @Composable fun CustomTimerSessionScreenComposable( From 47491089928c438ac9861d49da86da91802e0704 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:01:06 +0200 Subject: [PATCH 49/68] soundplayer to screens instead of mediaplayer --- .../composables/GetSessionScreenComposable.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt index bae4d92..3780f42 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -1,19 +1,15 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -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.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.sessionScreens.AbstractSessionScreen -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 be.ugent.sel.studeez.screens.session.SoundPlayer class GetSessionScreenComposable( - private val mediaplayer: MediaPlayer?, + private val soundPlayer: SoundPlayer, private val open: (String) -> Unit, private val sessionActions: SessionActions ) : @@ -23,7 +19,8 @@ class GetSessionScreenComposable( return { CustomTimerSessionScreenComposable( open = open, sessionActions = sessionActions, - customTimer = functionalCustomTimer + soundPlayer = soundPlayer, + customTimer = functionalCustomTimer, ) } } @@ -42,6 +39,7 @@ class GetSessionScreenComposable( BreakSessionScreenComposable( open = open, sessionActions = sessionActions, + soundPlayer = soundPlayer, pomodoroTimer = functionalPomodoroTimer ) } From 8294d63e92d9c7445f7361077cde75f24581cb42 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:02:23 +0200 Subject: [PATCH 50/68] added callmedia to session screen --- .../sessionScreens/composables/SessionScreenComposable.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index b31ee45..0d02add 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -16,13 +16,13 @@ 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.data.local.models.timer_functional.FunctionalTimer 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, @@ -32,6 +32,7 @@ fun SessionScreen( ) { Timer( sessionActions = sessionActions, + callMediaPlayer = callMediaPlayer, motivationString = motivationString, MidSection = midSection ) @@ -49,7 +50,6 @@ fun SessionScreen( fun EndSessionButton(sessionActions: SessionActions) { TextButton( onClick = { - sessionActions.releaseMediaPlayer sessionActions.endSession() }, modifier = Modifier From 53bdf7d21588a5f5199a3861d807a447343ab2f5 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:02:55 +0200 Subject: [PATCH 51/68] added callMediaPlayer call --- .../session/sessionScreens/composables/TimerComposable.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 87a6839..cade06f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -23,6 +23,7 @@ import kotlin.time.Duration.Companion.seconds @Composable fun Timer( sessionActions: SessionActions, + callMediaPlayer: () -> Unit, motivationString: @Composable () -> String, MidSection: @Composable () -> Int ) { @@ -30,7 +31,7 @@ fun Timer( LaunchedEffect(tikker) { delay(1.seconds) sessionActions.getTimer().tick() - // callMediaPlayer() TODO + callMediaPlayer() tikker = !tikker } From 346b24aabe952a0d46f4b5f537901a71771631d6 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:05:43 +0200 Subject: [PATCH 52/68] give soundplayer to visitor --- .../studeez/screens/session/SessionRoute.kt | 14 +- .../sessionScreens/AbstractSessionScreen.kt | 150 ------------------ .../sessionScreens/BreakSessionScreen.kt | 93 ----------- .../sessionScreens/CustomSessionScreen.kt | 35 ---- .../sessionScreens/EndlessSessionScreen.kt | 16 -- 5 files changed, 3 insertions(+), 305 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt 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 959bc74..3572b5e 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 @@ -5,32 +5,23 @@ 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.FunctionalPomodoroTimer 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.composables.BreakSessionScreenComposable import be.ugent.sel.studeez.screens.session.sessionScreens.composables.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, ) } @@ -50,8 +41,9 @@ fun SessionRoute( mediaplayer = mediaplayer ) - val sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) - val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(mediaplayer, open, sessionActions)) + val soundPlayer = SoundPlayer(LocalContext.current) + val sessionActions = getSessionActions(viewModel, openAndPopUp) + val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(soundPlayer, open, sessionActions)) sessionScreen() } 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/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/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 From 9573a2eb5c8b91220ba8784ec80e5275933fec22 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:09:14 +0200 Subject: [PATCH 53/68] changed location of sessionscreens --- .../java/be/ugent/sel/studeez/screens/session/SessionRoute.kt | 2 +- .../{composables => }/BreakTimerScreenComposable.kt | 2 +- .../{composables => }/CustomTimerSessionScreenComposable.kt | 2 +- .../{composables => }/EndlessTimerSessionScreenComposable.kt | 2 +- .../{composables => }/GetSessionScreenComposable.kt | 2 +- .../sessionScreens/{composables => }/SessionScreenComposable.kt | 2 +- .../session/sessionScreens/{composables => }/TimerComposable.kt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/BreakTimerScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/CustomTimerSessionScreenComposable.kt (93%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/EndlessTimerSessionScreenComposable.kt (87%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/GetSessionScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/SessionScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/TimerComposable.kt (97%) 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 3572b5e..0db1e73 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 @@ -6,7 +6,7 @@ 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.composables.GetSessionScreenComposable +import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreenComposable data class SessionActions( val getTimer: () -> FunctionalTimer, diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt index cac3509..093c16a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.layout.* diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt similarity index 93% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt index ec3c2d8..a0c385c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.R diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt index b223f52..4f1dbe3 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.R diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt index 3780f42..47ca52e 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt index 0d02add..c94d2a5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.border diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt similarity index 97% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt index cade06f..2a29403 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box From 0487347c8f4882c51afa8bf0b6b64552f943c5d0 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:18:47 +0200 Subject: [PATCH 54/68] cleanup sessionroute --- .../studeez/screens/session/InvisibleSessionManager.kt | 10 ++++++++-- .../ugent/sel/studeez/screens/session/SessionRoute.kt | 9 +-------- 2 files changed, 9 insertions(+), 10 deletions(-) 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..763fb1d 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,10 @@ package be.ugent.sel.studeez.screens.session +import android.annotation.SuppressLint +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 +14,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 0db1e73..9d1953b 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 @@ -31,15 +31,8 @@ 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 soundPlayer = SoundPlayer(LocalContext.current) val sessionActions = getSessionActions(viewModel, openAndPopUp) From 6554a92f837285b43e200e7face8cdbd3e348671 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:20:42 +0200 Subject: [PATCH 55/68] remove imports sessionroute --- .../java/be/ugent/sel/studeez/screens/session/SessionRoute.kt | 3 --- 1 file changed, 3 deletions(-) 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 9d1953b..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,8 +1,5 @@ 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 From 5ddd92a66fd6862b1755eb50fba70c25206ab721 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 20:58:37 +0200 Subject: [PATCH 56/68] 'merge' dots --- .../FunctionalPomodoroTimer.kt | 2 +- .../BreakTimerScreenComposable.kt | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) 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..e754963 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,7 +6,7 @@ class FunctionalPomodoroTimer( val repeats: Int ) : FunctionalTimer(studyTime) { - var breaksRemaining = repeats + var breaksRemaining = repeats - 1 var isInBreak = false override fun tick() { 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 index 093c16a..42ec4f7 100644 --- 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 @@ -38,12 +38,18 @@ private fun Dots(pomodoroTimer: FunctionalPomodoroTimer): Int { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { - repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining) { - Dot(color = Color.DarkGray) - } - if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(pomodoroTimer.breaksRemaining - 1) { - Dot(color = Color.Gray) + 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 From 860fb5fac190bb86e35c86ef0e7c30335542bf7f Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 21:53:04 +0200 Subject: [PATCH 57/68] ui tests --- .idea/androidTestResultsUserPreferences.xml | 338 ++++++++++++++++++ app/build.gradle | 2 +- .../java/be/ugent/sel/studeez/FabTest.kt | 74 ++++ .../be/ugent/sel/studeez/HomeScreenTest.kt | 173 ++++++++- .../be/ugent/sel/studeez/LoginScreenTest.kt | 68 ++++ .../sel/studeez/ProfileEditScreenTest.kt | 68 ++++ .../be/ugent/sel/studeez/ProfileScreenTest.kt | 42 +++ .../sel/studeez/SessionRecapScreenTest.kt | 75 ++++ .../be/ugent/sel/studeez/SessionScreenTest.kt | 123 +++++-- .../be/ugent/sel/studeez/SignUpScreenTest.kt | 52 +++ .../be/ugent/sel/studeez/SplashScreenTest.kt | 40 +++ .../be/ugent/sel/studeez/SubjectScreenTest.kt | 155 ++++++++ .../be/ugent/sel/studeez/TaskScreenTest.kt | 160 +++++++++ .../sel/studeez/TimerOverviewScreenTest.kt | 58 +++ .../be/ugent/sel/studeez/TimerScreenTest.kt | 37 ++ .../sel/studeez/TimerSelectionScreenTest.kt | 40 +++ .../screens/timer_form/TimerFormRoute.kt | 13 + .../TimerTypeSelectScreen.kt | 10 + .../InvisibleSessionManagerTest.kt | 7 +- build.gradle | 6 +- 20 files changed, 1492 insertions(+), 49 deletions(-) create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/FabTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/LoginScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/ProfileEditScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/ProfileScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SessionRecapScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SignUpScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SplashScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TaskScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerOverviewScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerSelectionScreenTest.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index 59f6bc1..c9ff4ce 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -3,6 +3,19 @@