diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..773fe0f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 68d4e47..fc2bd08 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -147,4 +147,4 @@ protobuf { } } } -} +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/FabTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/FabTest.kt new file mode 100644 index 0000000..fbd6968 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/FabTest.kt @@ -0,0 +1,74 @@ +package be.ugent.sel.studeez + +import androidx.compose.material.FloatingActionButton +import androidx.compose.ui.test.hasClickAction +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.common.composable.AddButtonActions +import be.ugent.sel.studeez.common.composable.ExpandedAddButton +import org.junit.Rule +import org.junit.Test + +class FabTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun expandFabTest() { + var expand = false + + composeTestRule.setContent { + FloatingActionButton( + onClick = {expand = true} + ) {} + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNode(hasClickAction()) + .assertExists() + .performClick() + + assert(expand) + } + + @Test + fun fabTest() { + var task = false + var session = false + var friend = false + + composeTestRule.setContent { + ExpandedAddButton( + addButtonActions = AddButtonActions( + {task = true}, + {friend = true}, + {session = true} + ) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithContentDescription("Session") + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription("Task") + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription("Friend") + .assertExists() + .performClick() + + assert(task) + assert(session) + assert(friend) + } +} \ 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..6906683 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt @@ -0,0 +1,207 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithContentDescription +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.Assert +import org.junit.Rule +import org.junit.Test + +class HomeScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun homeScreenTest() { + var continueTask = false + + composeTestRule.setContent { + HomeScreen( + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({false}, {}, {}, {}, {}, {}, {}, {}), + feedUiState = FeedUiState.Succes(mapOf( + "08 May 2023" to listOf( + FeedEntry( + argb_color = 0xFFABD200, + subJectName = "Test Subject", + taskName = "Test Task", + totalStudyTime = 600, + ) + ) + )), + continueTask = {_, _ -> continueTask = true }, + onEmptyFeedHelp = {}, + onViewFriendsClick = {}, + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + "continue", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + Assert.assertTrue(continueTask) + } + + @Test + fun drawerTest() { + var homebuttontest = false + var timersbuttontest = false + var settingsbuttontest = false + var logoutbuttontest = false + var aboutbuttontest = false + + composeTestRule.setContent { + HomeScreen( + drawerActions = DrawerActions( + {homebuttontest = true}, + {timersbuttontest = true}, + {settingsbuttontest = true}, + {logoutbuttontest = true}, + {aboutbuttontest = true} + ), + navigationBarActions = NavigationBarActions({false}, {}, {}, {}, {}, {}, {}, {}), + feedUiState = FeedUiState.Succes(mapOf()), + continueTask = {_, _ -> }, + onEmptyFeedHelp = {}, + onViewFriendsClick = {}, + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onAllNodesWithText( + "home", + substring = true, + ignoreCase = true + )[2] // Third node has the button + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "timer", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "settings", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "log out", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "about", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + Assert.assertTrue(homebuttontest) + Assert.assertTrue(timersbuttontest) + Assert.assertTrue(settingsbuttontest) + Assert.assertTrue(logoutbuttontest) + Assert.assertTrue(aboutbuttontest) + } + + @Test + fun navigationbarTest() { + var hometest = false + var tasktest = false + var sessiontest = false + var profiletest = false + + composeTestRule.setContent { + HomeScreen( + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions( + {false}, + {hometest = true}, + {tasktest = true}, + {sessiontest = true}, + {profiletest = true}, + {}, {}, {} + ), + feedUiState = FeedUiState.Succes(mapOf()), + continueTask = {_, _ -> }, + onEmptyFeedHelp = {}, + onViewFriendsClick = {}, + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onAllNodesWithContentDescription( + "Home", + substring = true, + ignoreCase = true + )[0] // Third node has the button + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + "tasks", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + "session", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + "profile", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + Assert.assertTrue(hometest) + Assert.assertTrue(tasktest) + Assert.assertTrue(sessiontest) + Assert.assertTrue(profiletest) + } +} \ 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/LoginScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/LoginScreenTest.kt new file mode 100644 index 0000000..9498241 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/LoginScreenTest.kt @@ -0,0 +1,68 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.screens.log_in.LoginScreen +import be.ugent.sel.studeez.screens.log_in.LoginScreenActions +import be.ugent.sel.studeez.screens.log_in.LoginUiState +import org.junit.Rule +import org.junit.Test + +class LoginScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun loginScreenTest() { + var login = false + var signup = false + var forgot_password = false + + composeTestRule.setContent { + LoginScreen( + uiState = LoginUiState(), + loginScreenActions = LoginScreenActions( + {}, {}, + {signup = true}, + {login = true}, + {forgot_password = true} + ) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onAllNodesWithText( + text = "Sign in", + substring = true, + ignoreCase = true + )[0] // The first object is the button + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "Forgot", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "Sign up", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(signup) + assert(login) + assert(forgot_password) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/ProfileEditScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/ProfileEditScreenTest.kt new file mode 100644 index 0000000..43c8240 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/ProfileEditScreenTest.kt @@ -0,0 +1,70 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.assert +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.screens.profile.edit_profile.EditProfileActions +import be.ugent.sel.studeez.screens.profile.edit_profile.EditProfileScreen +import be.ugent.sel.studeez.screens.profile.edit_profile.ProfileEditUiState +import org.junit.Rule +import org.junit.Test + +class ProfileEditScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun profileEditScreenTest() { + var edit_save = false + var goback = false + var delete_click = false + + composeTestRule.setContent { + EditProfileScreen( + goBack = {goback = true}, + uiState = ProfileEditUiState(), + editProfileActions = EditProfileActions( + onUserNameChange = {}, + onBiographyChange = {}, + onSaveClick = {edit_save = true}, + onDeleteClick = { delete_click = true }, + ), + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "save", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "delete", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + label = "go back", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(edit_save) + assert(goback) + assert(delete_click) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/ProfileScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/ProfileScreenTest.kt new file mode 100644 index 0000000..14e077d --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/ProfileScreenTest.kt @@ -0,0 +1,61 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +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.navbar.NavigationBarActions +import be.ugent.sel.studeez.screens.profile.ProfileActions +import be.ugent.sel.studeez.screens.profile.ProfileScreen +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test + +class ProfileScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun profileScreenTest() { + var edit = false + var view_friends = false + + composeTestRule.setContent { + ProfileScreen( + profileActions = ProfileActions( + getUsername = {null}, + onEditProfileClick = {edit = true}, + getBiography = {null}, + getAmountOfFriends = { flowOf(0) }, + onViewFriendsClick = {view_friends = true} + ), + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithContentDescription( + label = "edit profile", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "friends", + substring = true, + ignoreCase = true, + ) + .assertExists() + .performClick() + + assert(edit) + assert(view_friends) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionRecapScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionRecapScreenTest.kt new file mode 100644 index 0000000..829152b --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SessionRecapScreenTest.kt @@ -0,0 +1,75 @@ +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 SessionRecapScreenTest { + @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.waitForIdle() + + 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/androidTest/java/be/ugent/sel/studeez/SignUpScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SignUpScreenTest.kt new file mode 100644 index 0000000..c7b5f41 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SignUpScreenTest.kt @@ -0,0 +1,52 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.screens.sign_up.SignUpActions +import be.ugent.sel.studeez.screens.sign_up.SignUpScreen +import be.ugent.sel.studeez.screens.sign_up.SignUpUiState +import org.junit.Rule +import org.junit.Test + +class SignUpScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun signupScreenTest() { + var create = false + var login = false + + composeTestRule.setContent { + SignUpScreen( + uiState = SignUpUiState(), + signUpActions = SignUpActions({}, {}, {}, {}, {create = true}, {login = true}) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "log in", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onAllNodesWithText( + text = "Create account", + substring = true, + ignoreCase = true + )[0] // First node has the button + .assertExists() + .performClick() + + assert(login) + assert(create) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SplashScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SplashScreenTest.kt new file mode 100644 index 0000000..6a43119 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SplashScreenTest.kt @@ -0,0 +1,40 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.screens.splash.SplashScreen +import org.junit.Rule +import org.junit.Test + +class SplashScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun splashScreenTest() { + var tryAgain = false + + composeTestRule.setContent { + SplashScreen( + Modifier, + {tryAgain = true}, + true + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onAllNodesWithText( + text = "try again", + substring = true, + ignoreCase = true + )[1] // Second node is the button + .assertExists() + .performClick() + + assert(tryAgain) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt new file mode 100644 index 0000000..d4b5c68 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt @@ -0,0 +1,158 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.common.composable.DeleteButton +import be.ugent.sel.studeez.common.composable.drawer.DrawerActions +import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions +import be.ugent.sel.studeez.data.local.models.task.Subject +import be.ugent.sel.studeez.screens.subjects.SubjectScreen +import be.ugent.sel.studeez.screens.subjects.SubjectUiState +import be.ugent.sel.studeez.screens.subjects.form.SubjectForm +import be.ugent.sel.studeez.screens.subjects.form.SubjectFormUiState +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test + +class SubjectScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun addSubjectScreenTest() { + var confirm = false + var goback = false + + composeTestRule.setContent { + SubjectForm( + title = R.string.new_subject, + goBack = {goback = true}, + uiState = SubjectFormUiState(), + onConfirm = {confirm = true}, + onNameChange = {}, + onColorChange = {}, + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "confirm", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + label = "go back", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(confirm) + assert(goback) + } + + @Test + fun editSubjectScreenTest() { + var confirm = false + var delete = false + + composeTestRule.setContent { + SubjectForm( + title = R.string.edit_subject, + goBack = {}, + uiState = SubjectFormUiState( + name = "Test Subject", + ), + onConfirm = {confirm = true}, + onNameChange = {}, + onColorChange = {}, + ) + DeleteButton(text = R.string.delete_subject) { + delete = true + } + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "confirm", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "delete", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(confirm) + assert(delete) + } + + @Test + fun subjectScreenTest() { + var view = false + var add = false + + composeTestRule.setContent { + SubjectScreen( + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({false}, {}, {}, {}, {}, {}, {}, {}), + onAddSubject = { add = true }, + onViewSubject = { view = true }, + getStudyTime = { flowOf() }, + getCompletedTaskCount = { flowOf() }, + getTaskCount = { flowOf() }, + uiState = SubjectUiState.Succes( + listOf( + Subject( + id = "", + name = "Test Subject", + argb_color = 0xFFFFD200, + archived = false + ) + ) + ) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "view", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "new subject", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(add) + assert(view) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/TaskScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/TaskScreenTest.kt new file mode 100644 index 0000000..0f7a8b8 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/TaskScreenTest.kt @@ -0,0 +1,160 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.common.composable.DeleteButton +import be.ugent.sel.studeez.data.local.models.task.Subject +import be.ugent.sel.studeez.data.local.models.task.Task +import be.ugent.sel.studeez.screens.tasks.TaskActions +import be.ugent.sel.studeez.screens.tasks.TaskScreen +import be.ugent.sel.studeez.screens.tasks.form.TaskForm +import be.ugent.sel.studeez.screens.tasks.form.TaskFormUiState +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test + +class TaskScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun addTaskScreenTest() { + var confirm = false + var goback = false + + composeTestRule.setContent { + TaskForm( + title = R.string.new_task, + goBack = {goback = true}, + uiState = TaskFormUiState(), + onConfirm = {confirm = true}, + onNameChange = {}, + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "confirm", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithContentDescription( + label = "go back", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(confirm) + assert(goback) + } + + @Test + fun editTaskScreenTest() { + var confirm = false + var delete = false + + composeTestRule.setContent { + TaskForm( + title = R.string.edit_task, + goBack = {}, + uiState = TaskFormUiState( + name = "Test Task", + ), + onConfirm = {confirm = true}, + onNameChange = {}, + ) { + DeleteButton(text = R.string.delete_task) { + delete = true + } + } + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "confirm", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "delete", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(confirm) + assert(delete) + } + + @Test + fun taskScreenTest() { + var add = false + var edit = false + var start = false + + composeTestRule.setContent { + TaskScreen( + goBack = {}, + taskActions = TaskActions( + {add = true}, + { Subject(name = "Test Subject") }, + { flowOf(listOf(Task())) }, + { _, _ -> run {} }, + {edit = true}, + {start = true}, + {}, + ) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithContentDescription( + label = "edit", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "new", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "start", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(add) + assert(edit) + assert(start) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/TimerOverviewScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/TimerOverviewScreenTest.kt new file mode 100644 index 0000000..357bed4 --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/TimerOverviewScreenTest.kt @@ -0,0 +1,58 @@ +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.data.local.models.timer_info.EndlessTimerInfo +import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewActions +import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test + +class TimerOverviewScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun timerOverviewScreenTest() { + var add = false + var edit = false + + composeTestRule.setContent { + TimerOverviewScreen( + timerOverviewActions = TimerOverviewActions( + { flowOf(listOf(EndlessTimerInfo("", ""))) }, + { listOf() }, + {edit = true}, + {add = true} + ), + drawerActions = DrawerActions({}, {}, {}, {}, {}) + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "add", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + text = "edit", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(add) + assert(edit) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/TimerSelectionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/TimerSelectionScreenTest.kt new file mode 100644 index 0000000..f055daa --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/TimerSelectionScreenTest.kt @@ -0,0 +1,40 @@ +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.screens.timer_selection.TimerSelectionActions +import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionScreen +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test + +class TimerSelectionScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun timerOverviewScreenTest() { + var start = false + + composeTestRule.setContent { + TimerSelectionScreen( + timerSelectionActions = TimerSelectionActions({ flowOf()}, {start = true}, 0), + popUp = {} + ) + } + + composeTestRule.waitForIdle() + + composeTestRule + .onNodeWithText( + text = "start", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + assert(start) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index 66c7bc4..ba07898 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 @@ -227,12 +227,14 @@ fun SearchField( onValueChange: (String) -> Unit, onSubmit: () -> Unit, @StringRes label: Int, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + enabled: Boolean = true ) { OutlinedTextField( value = value, onValueChange = onValueChange, modifier = modifier, + enabled = enabled, label = { Text(text = stringResource(id = label)) }, trailingIcon = { IconButton(onClick = onSubmit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt index 54be2ea..fb18f02 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt @@ -81,7 +81,7 @@ fun FeedWithElements( Text( text = "${HoursMinutesSeconds(totalDayStudyTime)}", fontSize = 15.sp, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Medium ) } feedEntries.forEach { feedEntry -> diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt index ff950d6..016090d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt @@ -56,7 +56,7 @@ fun FeedEntry( ) { Text( text = feedEntry.subJectName, - fontWeight = FontWeight.Bold, + fontWeight = FontWeight.Medium, overflow = TextOverflow.Ellipsis, maxLines = 1, ) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt index 3b8c142..bea755d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt @@ -5,6 +5,9 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.navigation.StudeezDestinations.FRIENDS_FEED import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN +import be.ugent.sel.studeez.navigation.StudeezDestinations.SEARCH_FRIENDS_SCREEN +import be.ugent.sel.studeez.navigation.StudeezDestinations.SELECT_SUBJECT +import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_SCREEN import be.ugent.sel.studeez.navigation.StudeezDestinations.SUBJECT_SCREEN import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -33,13 +36,11 @@ class NavigationBarViewModel @Inject constructor( } fun onAddTaskClick(open: (String) -> Unit) { - // TODO open(CREATE_TASK_SCREEN) - SnackbarManager.showMessage(AppText.create_task_not_possible_yet) // TODO Remove + open(SELECT_SUBJECT) } fun onAddFriendClick(open: (String) -> Unit) { - // TODO open(SEARCH_FRIENDS_SCREEN) - SnackbarManager.showMessage(AppText.add_friend_not_possible_yet) // TODO Remove + open(SEARCH_FRIENDS_SCREEN) } fun onAddSessionClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt index c6631ce..1c98af2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Card import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.List @@ -30,10 +31,10 @@ import be.ugent.sel.studeez.R.string as AppText @Composable fun SubjectEntry( subject: Subject, - onViewSubject: () -> Unit, getTaskCount: () -> Flow, getCompletedTaskCount: () -> Flow, getStudyTime: () -> Flow, + selectButton: @Composable (RowScope) -> Unit, ) { val studytime by getStudyTime().collectAsState(initial = 0) val taskCount by getTaskCount().collectAsState(initial = 0) @@ -65,16 +66,17 @@ fun SubjectEntry( ) { Text( text = subject.name, - fontWeight = FontWeight.Bold, overflow = TextOverflow.Ellipsis, maxLines = 1, + fontWeight = FontWeight.Medium ) Row( horizontalArrangement = Arrangement.spacedBy(10.dp), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.CenterVertically ) { Text( text = HoursMinutesSeconds(studytime).toString(), + color = MaterialTheme.colors.onBackground.copy(alpha = 0.6f) ) Row( verticalAlignment = Alignment.CenterVertically, @@ -82,21 +84,18 @@ fun SubjectEntry( ) { Icon( imageVector = Icons.Default.List, - contentDescription = stringResource(id = AppText.tasks) + contentDescription = stringResource(id = AppText.tasks), + tint = MaterialTheme.colors.onBackground.copy(alpha = 0.6f) + ) + Text( + text = "${completedTaskCount}/${taskCount}", + color = MaterialTheme.colors.onBackground.copy(alpha = 0.6f) ) - Text(text = "${completedTaskCount}/${taskCount}") } } } } - StealthButton( - text = AppText.view_tasks, - modifier = Modifier - .padding(start = 10.dp, end = 5.dp) - .weight(1f) - ) { - onViewSubject() - } + selectButton(this) } } } @@ -109,11 +108,16 @@ fun SubjectEntryPreview() { name = "Test Subject", argb_color = 0xFFFFD200, ), - onViewSubject = {}, getTaskCount = { flowOf() }, getCompletedTaskCount = { flowOf() }, getStudyTime = { flowOf() }, - ) + ) { + StealthButton( + text = AppText.view_tasks, + modifier = Modifier + .padding(start = 10.dp, end = 5.dp) + ) {} + } } @Preview @@ -124,9 +128,8 @@ fun OverflowSubjectEntryPreview() { name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", argb_color = 0xFFFFD200, ), - onViewSubject = {}, getTaskCount = { flowOf() }, getCompletedTaskCount = { flowOf() }, getStudyTime = { flowOf() }, - ) + ) {} } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt index c52939f..ebe8589 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt @@ -5,6 +5,7 @@ import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.data.local.models.task.Task import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo +import be.ugent.sel.studeez.domain.UserDAO import javax.inject.Inject import javax.inject.Singleton @@ -42,4 +43,11 @@ class SelectedSubject @Inject constructor() : SelectedState() { @Singleton class SelectedTimerInfo @Inject constructor() : SelectedState() { override lateinit var value: TimerInfo +} + +@Singleton +class SelectedUserId @Inject constructor( + userDAO: UserDAO +): SelectedState() { + override var value: String = userDAO.getCurrentUserId() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt index 261f3e0..88c48c0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt @@ -1,7 +1,6 @@ package be.ugent.sel.studeez.data.local.models.task import com.google.firebase.firestore.DocumentId -import com.google.firebase.firestore.Exclude data class Subject( @DocumentId val id: String = "", 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 bd429e1..2583bed 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,11 +1,10 @@ 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.data.remote.FirebaseFriendship.FRIENDSSINCE import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.FRIENDS_COLLECTION @@ -18,6 +17,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.tasks.await import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException @@ -75,24 +75,40 @@ class FirebaseFriendshipDAO @Inject constructor( 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 + // Check if the friendship already exists for the logged in user + var allowed = false firestore.collection(USER_COLLECTION) - .document(otherUserId) + .document(currentUserId) .collection(FRIENDS_COLLECTION) - .add(mapOf( - FRIENDID to currentUserId, - ACCEPTED to true, // TODO Make it not automatically accepted. - FRIENDSSINCE to Timestamp.now() - )) + .whereEqualTo(FRIENDID, otherUserId) + .get() + .addOnSuccessListener { + allowed = it.documents.isEmpty() + + if (allowed) { + // 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() + )) + } + }.addOnSuccessListener { + val message = if (allowed) AppText.success else AppText.already_friend + SnackbarManager.showMessage(message) + } return true } diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt index 65c11a5..1b8d7da 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 @@ -94,4 +94,4 @@ class FirebaseSubjectDAO @Inject constructor( fun Query.subjectNotArchived(): Query = this.whereEqualTo(SubjectDocument.archived, false) -} \ No newline at end of file +} 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 32ad7ff..1ab69be 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 @@ -27,6 +27,7 @@ object StudeezDestinations { const val EDIT_SUBJECT_FORM = "edit_subject" const val TASKS_SCREEN = "tasks" const val ADD_TASK_FORM = "add_task" + const val SELECT_SUBJECT = "select_subject" const val EDIT_TASK_FORM = "edit_task" // Friends flow diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 067896e..a6f08fa 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 @@ -31,6 +31,7 @@ import be.ugent.sel.studeez.screens.splash.SplashRoute import be.ugent.sel.studeez.screens.subjects.SubjectRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectCreateRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute +import be.ugent.sel.studeez.screens.subjects.select.SubjectSelectionRoute import be.ugent.sel.studeez.screens.tasks.TaskRoute import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute @@ -69,7 +70,7 @@ fun StudeezNavGraph( // NavBar composable(StudeezDestinations.HOME_SCREEN) { HomeRoute( - open, + open = open, drawerActions = drawerActions, navigationBarActions = navigationBarActions, feedViewModel = hiltViewModel(), @@ -86,6 +87,14 @@ fun StudeezNavGraph( ) } + composable(StudeezDestinations.SELECT_SUBJECT) { + SubjectSelectionRoute( + open = { openAndPopUp(it, StudeezDestinations.SELECT_SUBJECT) }, + goBack = goBack, + viewModel = hiltViewModel(), + ) + } + composable(StudeezDestinations.ADD_SUBJECT_FORM) { SubjectCreateRoute( goBack = goBack, @@ -104,7 +113,7 @@ fun StudeezNavGraph( composable(StudeezDestinations.TASKS_SCREEN) { TaskRoute( - goBack = goBack, + goBack = { openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.TASKS_SCREEN) }, open = open, viewModel = hiltViewModel(), ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt index 8ea6d20..a1960d8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt @@ -1,15 +1,14 @@ 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.* 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.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -23,7 +22,6 @@ 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.ProfilePicture -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 @@ -89,13 +87,32 @@ fun FriendsOverviewScreen( topBar = { TopAppBar( title = { - // TODO Link to each other - SearchField( - value = uiState.queryString, - onValueChange = friendsOverviewActions.onQueryStringChange, - onSubmit = friendsOverviewActions.onSubmit, - label = AppText.search_friends - ) + // TODO Make search field +// SearchField( +// value = uiState.queryString, +// onValueChange = friendsOverviewActions.onQueryStringChange, +// onSubmit = friendsOverviewActions.onSubmit, +// label = AppText.search_friends, +// enabled = false +// ) + IconButton( + onClick = friendsOverviewActions.onSubmit, +// modifier = Modifier.background( +// color = MaterialTheme.colors.background +// ), + ) { + Row { + Text( + text = stringResource(id = AppText.click_search_friends), + color = MaterialTheme.colors.onPrimary + ) + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(AppText.search_friends), + tint = MaterialTheme.colors.onPrimary + ) + } + } }, navigationIcon = { IconButton(onClick = popUp) { @@ -162,49 +179,52 @@ fun FriendsEntry( viewProfile: (String) -> Unit, removeFriend: (Friendship) -> Unit ) { - Row ( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 15.dp, vertical = 7.dp), - ) { - Box( - modifier = Modifier - .padding(vertical = 4.dp) - ) { - ProfilePicture() - } - - Box ( + Card { + Row ( modifier = Modifier .fillMaxWidth() + .padding(horizontal = 15.dp, vertical = 7.dp), + horizontalArrangement = Arrangement.spacedBy(15.dp) ) { - Column ( + Box( 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 - ) + ProfilePicture() } - Box( - modifier = Modifier.fillMaxWidth(), - contentAlignment = Alignment.CenterEnd + Box ( + modifier = Modifier + .fillMaxWidth() ) { - FriendsOverviewDropDown( - friendship = friendship, - viewProfile = viewProfile, - removeFriend = removeFriend - ) + 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(), + contentAlignment = Alignment.CenterEnd + ) { + FriendsOverviewDropDown( + friendship = friendship, + viewProfile = viewProfile, + removeFriend = removeFriend + ) + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt index ce0c5af..556e435 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.screens.friends.friends_overview import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.SelectedUserId 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 @@ -8,7 +9,6 @@ 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 @@ -19,12 +19,12 @@ import javax.inject.Inject class FriendsOverviewViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, - private val selectedProfileState: SelectedProfileState, + private val selectedUserIdState: SelectedUserId, logService: LogService ) : StudeezViewModel(logService) { var uiState = mutableStateOf(FriendsOverviewUiState( - userId = selectedProfileState.selectedUserId + userId = selectedUserIdState.value )) private set @@ -63,7 +63,7 @@ class FriendsOverviewViewModel @Inject constructor( userId: String, open: (String) -> Unit ) { - selectedProfileState.selectedUserId = userId + selectedUserIdState.value = userId open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) } 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 index e84bb9f..7803066 100644 --- 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 @@ -9,6 +9,7 @@ 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.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -82,14 +83,16 @@ fun SearchFriendsScreen( topBar = { TopAppBar( title = { - SearchField( - value = query, - onValueChange = { newValue -> - searchFriendsActions.onQueryStringChange(newValue) - query = newValue - }, - onSubmit = { }, - label = AppText.search_friends + // TODO Make search field +// SearchField( +// value = uiState.queryString, +// onValueChange = friendsOverviewActions.onQueryStringChange, +// onSubmit = friendsOverviewActions.onSubmit, +// label = AppText.search_friends, +// enabled = false +// ) + Text( + text = stringResource(id = AppText.searching_friends) ) }, navigationIcon = { 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 index 11aecd7..05bf6d6 100644 --- 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 @@ -1,22 +1,22 @@ package be.ugent.sel.studeez.screens.friends.friends_search import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.SelectedUserId 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 kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map import javax.inject.Inject @HiltViewModel class SearchFriendsViewModel @Inject constructor( private val userDAO: UserDAO, - private val selectedProfileState: SelectedProfileState, + private val selectedProfileState: SelectedUserId, logService: LogService ): StudeezViewModel(logService) { @@ -49,8 +49,8 @@ class SearchFriendsViewModel @Inject constructor( */ fun getAllUsers(): Flow> { return userDAO.getAllUsers() - .filter { users -> - users.any { user -> + .map { users -> + users.filter { user -> user.id != userDAO.getCurrentUserId() } } @@ -60,7 +60,7 @@ class SearchFriendsViewModel @Inject constructor( userId: String, open: (String) -> Unit ) { - selectedProfileState.selectedUserId = userId + selectedProfileState.value = userId open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) } } \ No newline at end of file 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 index 41e33c5..fdc2e97 100644 --- 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 @@ -18,6 +18,7 @@ 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.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.screens.profile.AmountOfFriendsButton @@ -30,7 +31,7 @@ data class PublicProfileActions( val getUserDetails: () -> Flow, val getAmountOfFriends: () -> Flow, val onViewFriendsClick: () -> Unit, - val sendFriendRequest: () -> Boolean + val sendFriendRequest: () -> Unit ) fun getPublicProfileActions( @@ -43,9 +44,11 @@ fun getPublicProfileActions( userId = viewModel.uiState.value.userId ) }, onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, - sendFriendRequest = { viewModel.sendFriendRequest( - userId = viewModel.uiState.value.userId - ) } + sendFriendRequest = { + viewModel.sendFriendRequest( + userId = viewModel.uiState.value.userId + ) + } ) } @@ -129,7 +132,7 @@ fun PublicProfilePreview() { }, getAmountOfFriends = { flowOf(113) }, onViewFriendsClick = {}, - sendFriendRequest = { true } + sendFriendRequest = {} ), popUp = {} ) @@ -138,7 +141,7 @@ fun PublicProfilePreview() { @Composable fun PublicProfileEllipsis( - sendFriendRequest: () -> Boolean + sendFriendRequest: () -> Unit ) { var expanded by remember { mutableStateOf(false) } @@ -147,8 +150,7 @@ fun PublicProfileEllipsis( ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), - contentDescription = resources().getString(AppText.view_more), - modifier = Modifier.fillMaxSize() + contentDescription = resources().getString(AppText.view_more) ) } @@ -172,7 +174,7 @@ fun PublicProfileEllipsis( fun PublicProfileEllipsisPreview() { StudeezTheme { PublicProfileEllipsis( - sendFriendRequest = { true } + sendFriendRequest = {} ) } } \ 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 index 6cf22d9..700084f 100644 --- 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 @@ -1,6 +1,8 @@ package be.ugent.sel.studeez.screens.profile.public_profile import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.SelectedUserId import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.LogService @@ -9,20 +11,20 @@ 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 +import be.ugent.sel.studeez.R.string as AppText @HiltViewModel class PublicProfileViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, - selectedProfileState: SelectedProfileState, + selectedUserIdState: SelectedUserId, logService: LogService ): StudeezViewModel(logService) { val uiState = mutableStateOf( PublicProfileUiState( - userId = selectedProfileState.selectedUserId + userId = selectedUserIdState.value ) ) @@ -53,8 +55,8 @@ class PublicProfileViewModel @Inject constructor( fun sendFriendRequest( userId: String - ): Boolean { - return friendshipDAO.sendFriendshipRequest(userId) + ) { + 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 deleted file mode 100644 index 4226d65..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt +++ /dev/null @@ -1,12 +0,0 @@ -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 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 763fb1d..8a7c405 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,10 +1,6 @@ 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 @@ -14,10 +10,8 @@ object InvisibleSessionManager { private var viewModel: SessionViewModel? = null private lateinit var 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 + fun setParameters(viewModel: SessionViewModel, mediaPlayer: MediaPlayer) { + this.mediaPlayer = mediaPlayer this.viewModel = viewModel } 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 aeaf544..6ca8a96 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,5 +1,8 @@ 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 @@ -28,8 +31,11 @@ fun SessionRoute( openAndPopUp: (String, String) -> Unit, viewModel: SessionViewModel, ) { + val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) + val mediaPlayer = MediaPlayer.create(LocalContext.current, uri) + mediaPlayer.isLooping = false - InvisibleSessionManager.setParameters(viewModel = viewModel, context = LocalContext.current) + InvisibleSessionManager.setParameters(viewModel = viewModel, mediaPlayer = mediaPlayer) val soundPlayer = SoundPlayer(LocalContext.current) val sessionActions = getSessionActions(viewModel, openAndPopUp) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt index 852c0e5..accb7de 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt @@ -15,10 +15,12 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.NewTaskSubjectButton import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate +import be.ugent.sel.studeez.common.composable.StealthButton import be.ugent.sel.studeez.common.composable.drawer.DrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.tasks.SubjectEntry import be.ugent.sel.studeez.data.local.models.task.Subject +import be.ugent.sel.studeez.navigation.StudeezDestinations import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import be.ugent.sel.studeez.R.string as AppText @@ -35,11 +37,11 @@ fun SubjectRoute( drawerActions = drawerActions, navigationBarActions = navigationBarActions, onAddSubject = { viewModel.onAddSubject(open) }, - onViewSubject = { viewModel.onViewSubject(it, open) }, + onViewSubject = { viewModel.onSelectSubject(it) { open(StudeezDestinations.TASKS_SCREEN) } }, getTaskCount = viewModel::getTaskCount, getCompletedTaskCount = viewModel::getCompletedTaskCount, getStudyTime = viewModel::getStudyTime, - uiState, + uiState = uiState, ) } @@ -76,14 +78,22 @@ fun SubjectScreen( ) { NewTaskSubjectButton(onClick = onAddSubject, AppText.new_subject) LazyColumn { - items(uiState.subjects) { + items(uiState.subjects) { subject -> SubjectEntry( - subject = it, - onViewSubject = { onViewSubject(it) }, - getTaskCount = { getTaskCount(it) }, - getCompletedTaskCount = { getCompletedTaskCount(it) }, - getStudyTime = { getStudyTime(it) }, - ) + subject = subject, + getTaskCount = { getTaskCount(subject) }, + getCompletedTaskCount = { getCompletedTaskCount(subject) }, + getStudyTime = { getStudyTime(subject) }, + ) { + StealthButton( + text = AppText.view_tasks, + modifier = Modifier + .padding(start = 10.dp, end = 5.dp) + .weight(1f) + ) { + onViewSubject(subject) + } + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt index 19ba396..0c5b354 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt @@ -42,8 +42,8 @@ class SubjectViewModel @Inject constructor( return subjectDAO.getStudyTime(subject) } - fun onViewSubject(subject: Subject, open: (String) -> Unit) { + fun onSelectSubject(subject: Subject, open: () -> Unit) { selectedSubject.set(subject) - open(StudeezDestinations.TASKS_SCREEN) + open() } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index 9e787dd..196ad3f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -18,7 +18,6 @@ import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.DeleteButton import be.ugent.sel.studeez.common.composable.FormComposable import be.ugent.sel.studeez.common.composable.LabelledInputField -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.generateRandomArgb diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 7a1554b..84162d0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -2,8 +2,6 @@ package be.ugent.sel.studeez.screens.subjects.form import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.graphics.Color -import be.ugent.sel.studeez.common.ext.generateRandomArgb import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.domain.LogService diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/select/SubjectSelectionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/select/SubjectSelectionScreen.kt new file mode 100644 index 0000000..099786e --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/select/SubjectSelectionScreen.kt @@ -0,0 +1,128 @@ +package be.ugent.sel.studeez.screens.subjects.select + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.StealthButton +import be.ugent.sel.studeez.common.composable.tasks.SubjectEntry +import be.ugent.sel.studeez.data.local.models.task.Subject +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.subjects.SubjectUiState +import be.ugent.sel.studeez.screens.subjects.SubjectViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +@Composable +fun SubjectSelectionRoute( + open: (String) -> Unit, + goBack: () -> Unit, + viewModel: SubjectViewModel, +) { + val uiState by viewModel.uiState.collectAsState() + SubjectSelectionScreen( + onViewSubject = { viewModel.onSelectSubject(it) { open(StudeezDestinations.ADD_TASK_FORM) } }, + getTaskCount = viewModel::getTaskCount, + getCompletedTaskCount = viewModel::getCompletedTaskCount, + getStudyTime = viewModel::getStudyTime, + goBack = goBack, + uiState = uiState, + ) +} + +@Composable +fun SubjectSelectionScreen( + goBack: () -> Unit, + onViewSubject: (Subject) -> Unit, + getTaskCount: (Subject) -> Flow, + getCompletedTaskCount: (Subject) -> Flow, + getStudyTime: (Subject) -> Flow, + uiState: SubjectUiState, +) { + SecondaryScreenTemplate( + title = stringResource(R.string.select_subject_title), + barAction = {}, + popUp = goBack, + ) { + when (uiState) { + SubjectUiState.Loading -> Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator(color = MaterialTheme.colors.onBackground) + } + is SubjectUiState.Succes -> { + Column( + modifier = Modifier.padding(top = 5.dp) + ) { + LazyColumn { + items(uiState.subjects) { subject -> + SubjectEntry( + subject = subject, + getTaskCount = { getTaskCount(subject) }, + getCompletedTaskCount = { getCompletedTaskCount(subject) }, + getStudyTime = { getStudyTime(subject) }, + ) { + StealthButton( + text = R.string.select_subject, + modifier = Modifier + .padding(start = 4.dp, end = 4.dp) + .weight(1f) + ) { + onViewSubject(subject) + } + } + } + } + } + } + } + } +} + +@Preview +@Composable +fun SubjectScreenPreview() { + SubjectSelectionScreen( + goBack = {}, + onViewSubject = {}, + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, + getStudyTime = { flowOf() }, + uiState = SubjectUiState.Succes( + listOf( + Subject( + name = "Test Subject", + argb_color = 0xFFFFD200, + ) + ) + ) + ) +} + +@Preview +@Composable +fun SubjectScreenLoadingPreview() { + SubjectSelectionScreen( + goBack = {}, + onViewSubject = {}, + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, + getStudyTime = { flowOf() }, + uiState = SubjectUiState.Loading, + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt index fea01e6..c69e929 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt @@ -4,7 +4,6 @@ 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.common.composable.FormComposable import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.R.string as AppText 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 4825c63..3663e4a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt @@ -7,6 +7,7 @@ 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.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate @@ -46,4 +47,13 @@ fun TimerTypeSelectScreen( } } } +} + +@Preview +@Composable +fun TimerTypeSelectScreenPreview() { + TimerTypeSelectScreen( + open = {}, + popUp = {} + ) } \ 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 2f8896b..691b9f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ Tasks Task My Subjects + Select Subject New Subject New Task Edit Subject @@ -50,6 +51,7 @@ Delete Subject Delete Task View + Select Regenerate Color @@ -135,6 +137,9 @@ Send friend request Remove as friend Show profile + Click to search friends + Searching friends + You are already befriended with that person. diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/FunctionalPomodoroTimerUnitTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/FunctionalPomodoroTimerUnitTest.kt index 4b259c8..89a9b17 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/FunctionalPomodoroTimerUnitTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/FunctionalPomodoroTimerUnitTest.kt @@ -7,13 +7,14 @@ import org.junit.Test class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() { private val breakTime = 10 private val breaks = 2 + private val repeats = 3 // = breaks + 1 override val hours = 0 override val minutes = 0 override val seconds = 10 private lateinit var pomodoroTimer: FunctionalPomodoroTimer override fun setTimer() { - pomodoroTimer = FunctionalPomodoroTimer(time, breakTime, breaks) + pomodoroTimer = FunctionalPomodoroTimer(time, breakTime, repeats) } @Test 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..891c379 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt @@ -1,12 +1,11 @@ package be.ugent.sel.studeez.timer_functional -import android.media.MediaPlayer import be.ugent.sel.studeez.data.SelectedSessionReport +import be.ugent.sel.studeez.data.SelectedTask import be.ugent.sel.studeez.data.SelectedTimer 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 @@ -22,13 +21,13 @@ import org.mockito.kotlin.mock class InvisibleSessionManagerTest { private var selectedTimer: SelectedTimer = SelectedTimer() private lateinit var viewModel: SessionViewModel - private var mediaPlayer: MediaPlayer = mock() + @Test fun InvisibleEndlessTimerTest() = runTest { selectedTimer.set(FunctionalEndlessTimer()) - viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) - InvisibleSessionManager.setParameters(viewModel, mediaPlayer) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl()) + InvisibleSessionManager.setParameters(viewModel, mock()) val test = launch { InvisibleSessionManager.updateTimer() @@ -47,10 +46,10 @@ class InvisibleSessionManagerTest { fun InvisiblePomodoroTimerTest() = runTest { val studyTime = 10 val breakTime = 5 - val repeats = 1 + val repeats = 2 selectedTimer.set(FunctionalPomodoroTimer(studyTime, breakTime, repeats)) - viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) - InvisibleSessionManager.setParameters(viewModel, mediaPlayer) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl()) + InvisibleSessionManager.setParameters(viewModel, mock()) val test = launch { InvisibleSessionManager.updateTimer() @@ -82,8 +81,8 @@ class InvisibleSessionManagerTest { @Test fun InvisibleCustomTimerTest() = runTest { selectedTimer.set(FunctionalCustomTimer(5)) - viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) - InvisibleSessionManager.setParameters(viewModel, mediaPlayer) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), SelectedTask(), LogServiceImpl()) + InvisibleSessionManager.setParameters(viewModel, mock()) val test = launch { InvisibleSessionManager.updateTimer() diff --git a/build.gradle b/build.gradle index 7f25617..4535dd7 100644 --- a/build.gradle +++ b/build.gradle @@ -21,4 +21,3 @@ plugins { // Hilt id 'com.google.dagger.hilt.android' version '2.44' apply false } - 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