Merge pull request #84 from SELab1/screenrefactor

Screenrefactor
This commit is contained in:
brreynie 2023-04-24 16:31:12 +02:00 committed by GitHub Enterprise
commit c73c06b11c
21 changed files with 854 additions and 372 deletions

2
.idea/compiler.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

2
.idea/kotlinc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.0-release" /> <option name="version" value="1.7.0" />
</component> </component>
</project> </project>

3
.idea/misc.xml generated
View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

@ -2,7 +2,13 @@ package be.ugent.sel.studeez
import android.content.res.Resources import android.content.res.Resources
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.* import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Snackbar
import androidx.compose.material.SnackbarHost
import androidx.compose.material.Surface
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -11,22 +17,24 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraphBuilder import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel
import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.common.snackbar.SnackbarManager
import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.home.HomeScreen import be.ugent.sel.studeez.screens.home.HomeRoute
import be.ugent.sel.studeez.screens.log_in.LoginScreen import be.ugent.sel.studeez.screens.log_in.LoginRoute
import be.ugent.sel.studeez.screens.session.SessionScreen import be.ugent.sel.studeez.screens.profile.EditProfileRoute
import be.ugent.sel.studeez.screens.profile.EditProfileScreen import be.ugent.sel.studeez.screens.profile.ProfileRoute
import be.ugent.sel.studeez.screens.profile.ProfileScreen import be.ugent.sel.studeez.screens.session.SessionRoute
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen import be.ugent.sel.studeez.screens.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashScreen import be.ugent.sel.studeez.screens.splash.SplashRoute
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionScreen import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionRoute
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -48,13 +56,7 @@ fun StudeezApp() {
}, },
scaffoldState = appState.scaffoldState scaffoldState = appState.scaffoldState
) { innerPaddingModifier -> ) { innerPaddingModifier ->
NavHost( StudeezNavGraph(appState, Modifier.padding(innerPaddingModifier))
navController = appState.navController,
startDestination = StudeezDestinations.SPLASH_SCREEN,
modifier = Modifier.padding(innerPaddingModifier)
) {
studeezGraph(appState)
}
} }
} }
} }
@ -79,49 +81,73 @@ fun resources(): Resources {
return LocalContext.current.resources return LocalContext.current.resources
} }
fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) { @Composable
fun StudeezNavGraph(
appState: StudeezAppstate,
modifier: Modifier,
) {
val drawerViewModel: DrawerViewModel = hiltViewModel()
val navBarViewModel: NavigationBarViewModel = hiltViewModel()
NavHost(
navController = appState.navController,
startDestination = StudeezDestinations.SPLASH_SCREEN,
modifier = modifier,
) {
val goBack: () -> Unit = { val goBack: () -> Unit = {
appState.popUp() appState.popUp()
} }
val open: (String) -> Unit = { val open: (String) -> Unit = { route ->
route -> appState.navigate(route) appState.navigate(route)
} }
val openAndPopUp: (String, String) -> Unit = { val openAndPopUp: (String, String) -> Unit = { route, popUp ->
route, popUp -> appState.navigateAndPopUp(route, popUp) appState.navigateAndPopUp(route, popUp)
} }
composable(StudeezDestinations.SPLASH_SCREEN) { composable(StudeezDestinations.SPLASH_SCREEN) {
SplashScreen(openAndPopUp) SplashRoute(openAndPopUp, viewModel = hiltViewModel())
} }
composable(StudeezDestinations.LOGIN_SCREEN) { composable(StudeezDestinations.LOGIN_SCREEN) {
LoginScreen(openAndPopUp) LoginRoute(openAndPopUp, viewModel = hiltViewModel())
} }
composable(StudeezDestinations.SIGN_UP_SCREEN) { composable(StudeezDestinations.SIGN_UP_SCREEN) {
SignUpScreen(openAndPopUp) SignUpRoute(openAndPopUp, viewModel = hiltViewModel())
} }
composable(StudeezDestinations.HOME_SCREEN) { composable(StudeezDestinations.HOME_SCREEN) {
HomeScreen(open, openAndPopUp) HomeRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
} }
// TODO Tasks screen // TODO Tasks screen
// TODO Sessions screen // TODO Sessions screen
composable(StudeezDestinations.PROFILE_SCREEN) { composable(StudeezDestinations.PROFILE_SCREEN) {
ProfileScreen(open, openAndPopUp) ProfileRoute(open, openAndPopUp, viewModel = hiltViewModel())
} }
composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) { composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) {
TimerOverviewScreen(open, openAndPopUp) TimerOverviewRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
} }
composable(StudeezDestinations.SESSION_SCREEN) { composable(StudeezDestinations.SESSION_SCREEN) {
SessionScreen(open, openAndPopUp) SessionRoute(open, viewModel = hiltViewModel())
} }
// TODO Timers screen // TODO Timers screen
@ -129,10 +155,17 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
// Edit screens // Edit screens
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) { composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
EditProfileScreen(goBack, openAndPopUp) EditProfileRoute(goBack, openAndPopUp, viewModel = hiltViewModel())
} }
composable(StudeezDestinations.TIMER_SELECTION_SCREEN) { composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
TimerSelectionScreen(open, openAndPopUp) TimerSelectionRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
}
} }
} }

View file

@ -2,17 +2,26 @@ package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.* import androidx.compose.material.FabPosition
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.drawer.Drawer import be.ugent.sel.studeez.common.composable.drawer.Drawer
import be.ugent.sel.studeez.screens.navbar.NavigationBar import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBar
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -20,8 +29,8 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun PrimaryScreenTemplate( fun PrimaryScreenTemplate(
title: String, title: String,
open: (String) -> Unit, drawerActions: DrawerActions,
openAndPopUp: (String, String) -> Unit, navigationBarActions: NavigationBarActions,
action: @Composable RowScope.() -> Unit = {}, action: @Composable RowScope.() -> Unit = {},
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
@ -31,7 +40,8 @@ fun PrimaryScreenTemplate(
Scaffold( Scaffold(
scaffoldState = scaffoldState, scaffoldState = scaffoldState,
topBar = { TopAppBar( topBar = {
TopAppBar(
title = { Text(text = title) }, title = { Text(text = title) },
navigationIcon = { navigationIcon = {
IconButton(onClick = { IconButton(onClick = {
@ -44,13 +54,14 @@ fun PrimaryScreenTemplate(
} }
}, },
actions = action actions = action
) }, )
drawerContent = {
Drawer(open, openAndPopUp)
}, },
bottomBar = { NavigationBar(open) }, drawerContent = {
Drawer(drawerActions)
},
bottomBar = { NavigationBar(navigationBarActions) },
floatingActionButtonPosition = FabPosition.Center, floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true, isFloatingActionButtonDocked = true,
floatingActionButton = { CollapsedAddButton() } floatingActionButton = { CollapsedAddButton() }
@ -65,14 +76,16 @@ fun PrimaryScreenPreview() {
StudeezTheme { StudeezTheme {
PrimaryScreenTemplate( PrimaryScreenTemplate(
"Preview screen", "Preview screen",
{ _ -> {}}, DrawerActions({}, {}, {}, {}, {}),
{ _, _ -> {}}, NavigationBarActions({}, {}, {}, {}),
{ IconButton(onClick = { /*TODO*/ }) { {
IconButton(onClick = { /*TODO*/ }) {
Icon( Icon(
imageVector = Icons.Default.Edit, imageVector = Icons.Default.Edit,
contentDescription = "Edit" contentDescription = "Edit"
) )
}} }
},
) {} ) {}
} }
} }

View file

@ -0,0 +1,62 @@
package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
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_info.CustomTimerInfo
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
@Composable
fun TimerEntry(
timerInfo: TimerInfo,
button: @Composable () -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
Modifier.padding(horizontal = 10.dp)
) {
Text(
text = timerInfo.name, fontWeight = FontWeight.Bold, fontSize = 20.sp
)
Text(
text = timerInfo.description, fontWeight = FontWeight.Light, fontSize = 15.sp
)
}
button()
}
}
@Preview
@Composable
fun TimerEntryPreview() {
val timerInfo = CustomTimerInfo(
"my preview timer", "This is the description of the timer", 60
)
TimerEntry(timerInfo = timerInfo) {
StealthButton(text = R.string.edit) {}
}
}
@Preview
@Composable
fun TimerDefaultEntryPreview() {
val timerInfo = CustomTimerInfo(
"Default preview timer", "This is the description of the timer", 60
)
TimerEntry(timerInfo = timerInfo) {}
}

View file

@ -1,7 +1,12 @@
package be.ugent.sel.studeez.screens.drawer package be.ugent.sel.studeez.common.composable.drawer
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -14,57 +19,71 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
data class DrawerActions(
val onHomeButtonClick: () -> Unit,
val onTimersClick: () -> Unit,
val onSettingsClick: () -> Unit,
val onLogoutClick: () -> Unit,
val onAboutClick: () -> Unit,
)
fun getDrawerActions(
drawerViewModel: DrawerViewModel,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
): DrawerActions {
return DrawerActions(
onHomeButtonClick = { drawerViewModel.onHomeButtonClick(open) },
onTimersClick = { drawerViewModel.onTimersClick(open) },
onSettingsClick = { drawerViewModel.onSettingsClick(open) },
onLogoutClick = { drawerViewModel.onLogoutClick(openAndPopUp) },
onAboutClick = { drawerViewModel.onAboutClick(open) },
)
}
@Composable @Composable
fun Drawer( fun Drawer(
open: (String) -> Unit, drawerActions: DrawerActions,
openAndPopUp: (String, String) -> Unit,
viewModel: DrawerViewModel = hiltViewModel()
) { ) {
Column ( Column(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Column ( Column(
modifier = Modifier.fillMaxWidth().weight(1f) modifier = Modifier
.fillMaxWidth()
.weight(1f)
) { ) {
DrawerEntry( DrawerEntry(
icon = Icons.Default.Home, icon = Icons.Default.Home,
text = resources().getString(R.string.home) text = resources().getString(R.string.home),
) { onClick = drawerActions.onHomeButtonClick,
viewModel.onHomeButtonClick(open) )
}
DrawerEntry( DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_timer), icon = ImageVector.vectorResource(id = R.drawable.ic_timer),
text = resources().getString(R.string.timers) text = resources().getString(R.string.timers),
) { onClick = drawerActions.onTimersClick,
viewModel.onTimersClick(open) )
}
DrawerEntry( DrawerEntry(
icon = Icons.Default.Settings, icon = Icons.Default.Settings,
text = resources().getString(R.string.settings) text = resources().getString(R.string.settings),
) { onClick = drawerActions.onSettingsClick,
viewModel.onSettingsClick(open) )
}
DrawerEntry( DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_logout), icon = ImageVector.vectorResource(id = R.drawable.ic_logout),
text = resources().getString(R.string.log_out) text = resources().getString(R.string.log_out),
) { onClick = drawerActions.onLogoutClick,
viewModel.onLogoutClick(openAndPopUp) )
}
} }
DrawerEntry( DrawerEntry(
icon = Icons.Outlined.Info, icon = Icons.Outlined.Info,
text = resources().getString(R.string.about) text = resources().getString(R.string.about),
) { onClick = drawerActions.onAboutClick,
viewModel.onAboutClick(open) )
}
} }
} }
@ -100,11 +119,8 @@ fun DrawerEntry(
@Preview @Preview
@Composable @Composable
fun DrawerPreview() { fun DrawerPreview() {
val drawerActions = DrawerActions({}, {}, {}, {}, {})
StudeezTheme { StudeezTheme {
Drawer( Drawer(drawerActions)
{ _, -> {} },
{ _, _ -> {} },
hiltViewModel()
)
} }
} }

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.drawer package be.ugent.sel.studeez.common.composable.drawer
import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.LogService

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.navbar package be.ugent.sel.studeez.common.composable.navbar
import androidx.compose.material.BottomNavigation import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem import androidx.compose.material.BottomNavigationItem
@ -12,16 +12,32 @@ import androidx.compose.material.icons.outlined.DateRange
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
data class NavigationBarActions(
val onHomeClick: () -> Unit,
val onTasksClick: () -> Unit,
val onSessionsClick: () -> Unit,
val onProfileClick: () -> Unit,
)
fun getNavigationBarActions(
navigationBarViewModel: NavigationBarViewModel,
open: (String) -> Unit,
): NavigationBarActions {
return NavigationBarActions(
onHomeClick = { navigationBarViewModel.onHomeClick(open) },
onTasksClick = { navigationBarViewModel.onTasksClick(open) },
onSessionsClick = { navigationBarViewModel.onSessionsClick(open) },
onProfileClick = { navigationBarViewModel.onProfileClick(open) },
)
}
@Composable @Composable
fun NavigationBar( fun NavigationBar(
open: (String) -> Unit, navigationBarActions: NavigationBarActions,
viewModel: NavigationBarViewModel = hiltViewModel()
) { ) {
// TODO Pass functions and new screens. // TODO Pass functions and new screens.
// TODO Pass which screen is selected. // TODO Pass which screen is selected.
@ -33,31 +49,43 @@ fun NavigationBar(
icon = { Icon(imageVector = Icons.Default.List, resources().getString(AppText.home)) }, icon = { Icon(imageVector = Icons.Default.List, resources().getString(AppText.home)) },
label = { Text(text = resources().getString(AppText.home)) }, label = { Text(text = resources().getString(AppText.home)) },
selected = false, // TODO selected = false, // TODO
onClick = { viewModel.onHomeClick(open) } onClick = navigationBarActions.onHomeClick
) )
BottomNavigationItem( BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Check, resources().getString(AppText.tasks)) }, icon = {
Icon(
imageVector = Icons.Default.Check, resources().getString(AppText.tasks)
)
},
label = { Text(text = resources().getString(AppText.tasks)) }, label = { Text(text = resources().getString(AppText.tasks)) },
selected = false, // TODO selected = false, // TODO
onClick = { viewModel.onTasksClick(open) } onClick = navigationBarActions.onTasksClick
) )
// Hack to space the entries in the navigation bar, make space for fab // Hack to space the entries in the navigation bar, make space for fab
BottomNavigationItem(icon = {}, onClick = {}, selected = false) BottomNavigationItem(icon = {}, onClick = {}, selected = false)
BottomNavigationItem( BottomNavigationItem(
icon = { Icon(imageVector = Icons.Outlined.DateRange, resources().getString(AppText.sessions)) }, icon = {
Icon(
imageVector = Icons.Outlined.DateRange, resources().getString(AppText.sessions)
)
},
label = { Text(text = resources().getString(AppText.sessions)) }, label = { Text(text = resources().getString(AppText.sessions)) },
selected = false, // TODO selected = false, // TODO
onClick = { viewModel.onSessionsClick(open) } onClick = navigationBarActions.onSessionsClick
) )
BottomNavigationItem( BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Person, resources().getString(AppText.profile)) }, icon = {
Icon(
imageVector = Icons.Default.Person, resources().getString(AppText.profile)
)
},
label = { Text(text = resources().getString(AppText.profile)) }, label = { Text(text = resources().getString(AppText.profile)) },
selected = false, // TODO selected = false, // TODO
onClick = { viewModel.onProfileClick(open) } onClick = navigationBarActions.onProfileClick
) )
} }
@ -67,9 +95,6 @@ fun NavigationBar(
@Composable @Composable
fun NavigationBarPreview() { fun NavigationBarPreview() {
StudeezTheme { StudeezTheme {
NavigationBar( NavigationBar(NavigationBarActions({}, {}, {}, {}))
{ _ -> {} },
hiltViewModel()
)
} }
} }

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.navbar package be.ugent.sel.studeez.common.composable.navbar
import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.LogService

View file

@ -6,33 +6,55 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
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.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
@Composable @Composable
fun HomeScreen( fun HomeRoute(
open: (String) -> Unit, open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit, openAndPopUp: (String, String) -> Unit,
viewModel: HomeViewModel = hiltViewModel() viewModel: HomeViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) { ) {
HomeScreen(
onStartSessionClick = { viewModel.onStartSessionClick(open) },
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable
fun HomeScreen(
onStartSessionClick: () -> Unit,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
PrimaryScreenTemplate( PrimaryScreenTemplate(
title = resources().getString(R.string.home), title = resources().getString(R.string.home),
open = open, drawerActions = drawerActions,
openAndPopUp = openAndPopUp, navigationBarActions = navigationBarActions,
action = { FriendsAction() } action = { FriendsAction() }
) { ) {
BasicButton(R.string.start_session, Modifier.basicButton()) { BasicButton(R.string.start_session, Modifier.basicButton()) {
viewModel.onStartSessionClick(open) onStartSessionClick()
} }
} }
} }
@Composable @Composable
fun FriendsAction () { fun FriendsAction() {
IconButton(onClick = { /*TODO*/ }) { IconButton(onClick = { /*TODO*/ }) {
Icon( Icon(
imageVector = Icons.Default.Person, imageVector = Icons.Default.Person,
@ -40,3 +62,13 @@ fun FriendsAction () {
) )
} }
} }
@Preview
@Composable
fun HomeScreenPreview() {
HomeScreen(
onStartSessionClick = {},
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
)
}

View file

@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.* import be.ugent.sel.studeez.common.composable.*
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.fieldModifier
@ -18,14 +18,48 @@ import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
data class LoginScreenActions(
val onEmailChange: (String) -> Unit,
val onPasswordChange: (String) -> Unit,
val onSignUpClick: () -> Unit,
val onSignInClick: () -> Unit,
val onForgotPasswordClick: () -> Unit,
)
fun getLoginScreenActions(
viewModel: LoginViewModel,
openAndPopUp: (String, String) -> Unit,
): LoginScreenActions {
return LoginScreenActions(
onEmailChange = { viewModel.onEmailChange(it) },
onPasswordChange = { viewModel.onPasswordChange(it) },
onSignUpClick = { viewModel.onSignUpClick(openAndPopUp) },
onSignInClick = { viewModel.onSignInClick(openAndPopUp) },
onForgotPasswordClick = { viewModel.onForgotPasswordClick() }
)
}
@Composable @Composable
fun LoginScreen( fun LoginRoute(
openAndPopUp: (String, String) -> Unit, openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: LoginViewModel = hiltViewModel() viewModel: LoginViewModel,
) { ) {
val uiState by viewModel.uiState val uiState by viewModel.uiState
LoginScreen(
modifier = modifier,
uiState = uiState,
loginScreenActions = getLoginScreenActions(viewModel = viewModel, openAndPopUp)
)
}
@Composable
fun LoginScreen(
modifier: Modifier = Modifier,
uiState: LoginUiState,
loginScreenActions: LoginScreenActions,
) {
SimpleScreenTemplate(title = resources().getString(AppText.sign_in)) { SimpleScreenTemplate(title = resources().getString(AppText.sign_in)) {
Column( Column(
modifier = modifier modifier = modifier
@ -35,18 +69,42 @@ fun LoginScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
EmailField(uiState.email, viewModel::onEmailChange, Modifier.fieldModifier()) EmailField(
PasswordField(uiState.password, viewModel::onPasswordChange, Modifier.fieldModifier()) uiState.email,
loginScreenActions.onEmailChange,
Modifier.fieldModifier()
)
PasswordField(
uiState.password,
loginScreenActions.onPasswordChange,
Modifier.fieldModifier()
)
BasicButton(
AppText.sign_in,
Modifier.basicButton(),
onClick = loginScreenActions.onSignInClick,
)
BasicButton(AppText.sign_in, Modifier.basicButton()) { viewModel.onSignInClick(openAndPopUp) } BasicTextButton(
AppText.not_already_user,
Modifier.textButton(),
action = loginScreenActions.onSignUpClick,
)
BasicTextButton(AppText.not_already_user, Modifier.textButton()) { BasicTextButton(
viewModel.onNotAlreadyUser(openAndPopUp) AppText.forgot_password,
} Modifier.textButton(),
action = loginScreenActions.onForgotPasswordClick,
BasicTextButton(AppText.forgot_password, Modifier.textButton()) { )
viewModel.onForgotPasswordClick()
}
} }
} }
} }
@Preview
@Composable
fun LoginScreenPreview() {
LoginScreen(
uiState = LoginUiState(),
loginScreenActions = LoginScreenActions({}, {}, {}, {}, {})
)
}

View file

@ -63,7 +63,7 @@ class LoginViewModel @Inject constructor(
} }
} }
fun onNotAlreadyUser(openAndPopUp: (String, String) -> Unit) { fun onSignUpClick(openAndPopUp: (String, String) -> Unit) {
openAndPopUp(SIGN_UP_SCREEN, LOGIN_SCREEN) openAndPopUp(SIGN_UP_SCREEN, LOGIN_SCREEN)
} }
} }

View file

@ -14,14 +14,43 @@ import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
data class EditProfileActions(
val onUserNameChange: (String) -> Unit,
val onSaveClick: () -> Unit,
val onDeleteClick: () -> Unit
)
fun getEditProfileActions(
viewModel: ProfileEditViewModel,
openAndPopUp: (String, String) -> Unit,
): EditProfileActions {
return EditProfileActions(
onUserNameChange = { viewModel.onUsernameChange(it) },
onSaveClick = { viewModel.onSaveClick() },
onDeleteClick = { viewModel.onDeleteClick(openAndPopUp) },
)
}
@Composable
fun EditProfileRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileEditViewModel,
) {
val uiState by viewModel.uiState
EditProfileScreen(
goBack = goBack,
uiState = uiState,
editProfileActions = getEditProfileActions(viewModel, openAndPopUp)
)
}
@Composable @Composable
fun EditProfileScreen( fun EditProfileScreen(
goBack: () -> Unit, goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit, uiState: ProfileEditUiState,
viewModel: ProfileEditViewModel = hiltViewModel() editProfileActions: EditProfileActions,
) { ) {
val uiState by viewModel.uiState
SecondaryScreenTemplate( SecondaryScreenTemplate(
title = resources().getString(R.string.editing_profile), title = resources().getString(R.string.editing_profile),
popUp = goBack popUp = goBack
@ -29,16 +58,19 @@ fun EditProfileScreen(
Column { Column {
LabelledInputField( LabelledInputField(
value = uiState.username, value = uiState.username,
onNewValue = viewModel::onUsernameChange, onNewValue = editProfileActions.onUserNameChange,
label = R.string.username label = R.string.username
) )
BasicTextButton(
BasicTextButton(text = R.string.save, Modifier.textButton()) { text = R.string.save,
viewModel.onSaveClick() Modifier.textButton(),
} action = editProfileActions.onSaveClick
BasicTextButton(text = R.string.delete_profile, Modifier.textButton()) { )
viewModel.onDeleteClick(openAndPopUp) BasicTextButton(
} text = R.string.delete_profile,
Modifier.textButton(),
action = editProfileActions.onDeleteClick
)
} }
} }
} }
@ -47,9 +79,6 @@ fun EditProfileScreen(
@Composable @Composable
fun EditProfileScreenComposable() { fun EditProfileScreenComposable() {
StudeezTheme { StudeezTheme {
EditProfileScreen ( EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {}))
{},
{_, _ -> {}}
)
} }
} }

View file

@ -4,30 +4,68 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.* 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.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.Headline
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
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.getNavigationBarActions
import kotlinx.coroutines.CoroutineScope
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
data class ProfileActions(
val getUsername: suspend CoroutineScope.() -> String?,
val onEditProfileClick: () -> Unit,
)
fun getProfileActions(
viewModel: ProfileViewModel,
open: (String) -> Unit,
): ProfileActions {
return ProfileActions(
getUsername = { viewModel.getUsername() },
onEditProfileClick = { viewModel.onEditProfileClick(open) },
)
}
@Composable
fun ProfileRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileViewModel,
) {
ProfileScreen(
profileActions = getProfileActions(viewModel, open),
drawerActions = getDrawerActions(hiltViewModel(), open, openAndPopUp),
navigationBarActions = getNavigationBarActions(hiltViewModel(), open),
)
}
@Composable @Composable
fun ProfileScreen( fun ProfileScreen(
open: (String) -> Unit, profileActions: ProfileActions,
openAndPopUp: (String, String) -> Unit, drawerActions: DrawerActions,
viewModel: ProfileViewModel = hiltViewModel() navigationBarActions: NavigationBarActions,
) { ) {
var username: String? by remember { mutableStateOf("") } var username: String? by remember { mutableStateOf("") }
LaunchedEffect(key1 = Unit) { LaunchedEffect(key1 = Unit) {
username = viewModel.getUsername() username = profileActions.getUsername(this)
} }
PrimaryScreenTemplate( PrimaryScreenTemplate(
title = resources().getString(AppText.profile), title = resources().getString(AppText.profile),
open = open, drawerActions = drawerActions,
openAndPopUp = openAndPopUp, navigationBarActions = navigationBarActions,
action = { EditAction { viewModel.onEditProfileClick(open) } } action = { EditAction(onClick = profileActions.onEditProfileClick) }
) { ) {
Headline(text = (username ?: resources().getString(R.string.no_username))) Headline(text = (username ?: resources().getString(R.string.no_username)))
} }
@ -45,3 +83,13 @@ fun EditAction(
} }
} }
@Preview
@Composable
fun ProfileScreenPreview() {
ProfileScreen(
profileActions = ProfileActions({ null }, {}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
)
}

View file

@ -12,66 +12,95 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.ui.Alignment
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign 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.dp
import be.ugent.sel.studeez.navigation.StudeezDestinations
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer.StudyState import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer.StudyState
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
var timerEnd = false var timerEnd = false
data class SessionActions(
val getTimer: () -> FunctionalTimer,
val getTask: () -> String,
val prepareMediaPlayer: () -> Unit,
val releaseMediaPlayer: () -> Unit,
)
fun getSessionActions(
viewModel: SessionViewModel,
mediaplayer: MediaPlayer,
): SessionActions {
return SessionActions(
getTimer = viewModel::getTimer,
getTask = viewModel::getTask,
prepareMediaPlayer = mediaplayer::prepareAsync,
releaseMediaPlayer = mediaplayer::release,
)
}
@Composable @Composable
fun SessionScreen( fun SessionRoute(
open: (String) -> Unit, open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit, viewModel: SessionViewModel,
viewModel: SessionViewModel = hiltViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val mediaplayer = MediaPlayer.create(context, uri) val mediaplayer = MediaPlayer()
mediaplayer.setDataSource(context, uri)
mediaplayer.setOnCompletionListener { mediaplayer.setOnCompletionListener {
if (!mediaplayer.isPlaying) {
mediaplayer.stop() mediaplayer.stop()
}
if (timerEnd) { if (timerEnd) {
mediaplayer.release() // mediaplayer.release()
} }
} }
mediaplayer.setOnPreparedListener { mediaplayer.setOnPreparedListener {
mediaplayer.start() // mediaplayer.start()
} }
SessionScreen(
open = open,
sessionActions = getSessionActions(viewModel, mediaplayer),
)
}
@Composable
fun SessionScreen(
open: (String) -> Unit,
sessionActions: SessionActions,
) {
Column( Column(
modifier = Modifier.padding(10.dp) modifier = Modifier.padding(10.dp)
) { ) {
Timer(viewModel, mediaplayer) Timer(
sessionActions = sessionActions,
)
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center, modifier = Modifier
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(50.dp) .padding(50.dp)
) { ) {
TextButton( TextButton(
onClick = { onClick = {
mediaplayer.release() sessionActions.releaseMediaPlayer
open(StudeezDestinations.HOME_SCREEN) open(StudeezDestinations.HOME_SCREEN)
// Vanaf hier ook naar report gaan als "end session" knop word ingedrukt // Vanaf hier ook naar report gaan als "end session" knop word ingedrukt
}, },
@ -93,24 +122,30 @@ fun SessionScreen(
} }
@Composable @Composable
private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) { private fun Timer(
sessionActions: SessionActions,
) {
var tikker by remember { mutableStateOf(false) } var tikker by remember { mutableStateOf(false) }
LaunchedEffect(tikker) { LaunchedEffect(tikker) {
delay(1.seconds) delay(1.seconds)
viewModel.getTimer().tick() sessionActions.getTimer().tick()
tikker = !tikker tikker = !tikker
} }
if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) { if (
mediaplayer.prepare() sessionActions.getTimer().hasCurrentCountdownEnded() && !sessionActions.getTimer()
.hasEnded()
) {
// sessionActions.prepareMediaPlayer()
} }
if (!timerEnd && viewModel.getTimer().hasEnded()) { if (!timerEnd && sessionActions.getTimer().hasEnded()) {
mediaplayer.prepare() // sessionActions.prepareMediaPlayer()
timerEnd = true // Placeholder, vanaf hier moet het report opgestart worden en de sessie afgesloten timerEnd =
true // Placeholder, vanaf hier moet het report opgestart worden en de sessie afgesloten
} }
val hms = viewModel.getTimer().getHoursMinutesSeconds() val hms = sessionActions.getTimer().getHoursMinutesSeconds()
Column { Column {
Text( Text(
text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}", text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}",
@ -121,12 +156,11 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 40.sp, fontSize = 40.sp,
) )
val stateString: String = when (viewModel.getTimer().view) { val stateString: String = when (sessionActions.getTimer().view) {
StudyState.DONE -> resources().getString(R.string.state_done) StudyState.DONE -> resources().getString(R.string.state_done)
StudyState.FOCUS -> resources().getString(R.string.state_focus) StudyState.FOCUS -> resources().getString(R.string.state_focus)
StudyState.BREAK -> resources().getString(R.string.state_take_a_break) StudyState.BREAK -> resources().getString(R.string.state_take_a_break)
StudyState.FOCUS_REMAINING -> StudyState.FOCUS_REMAINING -> (sessionActions.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
(viewModel.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
resources().getQuantityString(R.plurals.state_focus_remaining, it, it) resources().getQuantityString(R.plurals.state_focus_remaining, it, it)
}.toString() }.toString()
} }
@ -140,8 +174,7 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
) )
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center, modifier = Modifier
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(50.dp) .padding(50.dp)
) { ) {
@ -152,16 +185,28 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
.background(Color.Blue, RoundedCornerShape(32.dp)) .background(Color.Blue, RoundedCornerShape(32.dp))
) { ) {
Text( Text(
text = viewModel.getTask(), text = sessionActions.getTask(),
color = Color.White, color = Color.White,
fontSize = 18.sp, fontSize = 18.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
modifier = Modifier modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp)
.padding(vertical = 4.dp, horizontal = 20.dp)
) )
} }
} }
} }
}
@Preview
@Composable
fun TimerPreview() {
Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}))
}
@Preview
@Composable
fun SessionPreview() {
SessionScreen(
open = {},
sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {})
)
} }

View file

@ -10,23 +10,64 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.* import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.BasicTextButton
import be.ugent.sel.studeez.common.composable.EmailField
import be.ugent.sel.studeez.common.composable.PasswordField
import be.ugent.sel.studeez.common.composable.RepeatPasswordField
import be.ugent.sel.studeez.common.composable.SimpleScreenTemplate
import be.ugent.sel.studeez.common.composable.UsernameField
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.common.ext.textButton import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
data class SignUpActions(
val onUserNameChange: (String) -> Unit,
val onEmailChange: (String) -> Unit,
val onPasswordChange: (String) -> Unit,
val onRepeatPasswordChange: (String) -> Unit,
val onSignUpClick: () -> Unit,
val onLoginClick: () -> Unit,
)
fun getSignUpActions(
viewModel: SignUpViewModel,
openAndPopUp: (String, String) -> Unit,
): SignUpActions {
return SignUpActions(
onUserNameChange = { viewModel.onUsernameChange(it) },
onEmailChange = { viewModel.onEmailChange(it) },
onPasswordChange = { viewModel.onPasswordChange(it) },
onRepeatPasswordChange = { viewModel.onRepeatPasswordChange(it) },
onSignUpClick = { viewModel.onSignUpClick(openAndPopUp) },
onLoginClick = { viewModel.onLoginClick(openAndPopUp) },
)
}
@Composable @Composable
fun SignUpScreen( fun SignUpRoute(
openAndPopUp: (String, String) -> Unit, openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: SignUpViewModel = hiltViewModel() viewModel: SignUpViewModel,
) { ) {
val uiState by viewModel.uiState val uiState by viewModel.uiState
val fieldModifier = Modifier.fieldModifier() SignUpScreen(
modifier = modifier,
uiState,
getSignUpActions(viewModel, openAndPopUp)
)
}
@Composable
fun SignUpScreen(
modifier: Modifier = Modifier,
uiState: SignUpUiState,
signUpActions: SignUpActions,
) {
val fieldModifier = Modifier.fieldModifier()
SimpleScreenTemplate(title = resources().getString(AppText.create_account)) { SimpleScreenTemplate(title = resources().getString(AppText.create_account)) {
Column( Column(
modifier = modifier modifier = modifier
@ -36,40 +77,45 @@ fun SignUpScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
UsernameField( UsernameField(
uiState.username, uiState.username,
viewModel::onUsernameChange, signUpActions.onUserNameChange,
fieldModifier fieldModifier
) )
EmailField( EmailField(
uiState.email, uiState.email,
viewModel::onEmailChange, signUpActions.onEmailChange,
fieldModifier fieldModifier
) )
PasswordField( PasswordField(
uiState.password, uiState.password,
viewModel::onPasswordChange, signUpActions.onPasswordChange,
fieldModifier fieldModifier
) )
RepeatPasswordField( RepeatPasswordField(
uiState.repeatPassword, uiState.repeatPassword,
viewModel::onRepeatPasswordChange, signUpActions.onRepeatPasswordChange,
fieldModifier fieldModifier
) )
BasicButton(
BasicButton(AppText.create_account, Modifier.basicButton()) { AppText.create_account,
viewModel.onSignUpClick(openAndPopUp) Modifier.basicButton(),
} onClick = signUpActions.onSignUpClick
)
BasicTextButton(AppText.already_user, Modifier.textButton()) { BasicTextButton(
viewModel.onLoginScreenClick(openAndPopUp) AppText.already_user,
Modifier.textButton(),
action = signUpActions.onLoginClick
)
} }
} }
} }
@Preview
@Composable
fun SignUpPreview() {
SignUpScreen(
uiState = SignUpUiState(),
signUpActions = SignUpActions({}, {}, {}, {}, {}, {})
)
} }

View file

@ -71,7 +71,7 @@ class SignUpViewModel @Inject constructor(
} }
} }
fun onLoginScreenClick(openAndPopUp: (String, String) -> Unit) { fun onLoginClick(openAndPopUp: (String, String) -> Unit) {
openAndPopUp(LOGIN_SCREEN, SIGN_UP_SCREEN) openAndPopUp(LOGIN_SCREEN, SIGN_UP_SCREEN)
} }
} }

View file

@ -1,5 +1,6 @@
package be.ugent.sel.studeez.screens.splash package be.ugent.sel.studeez.screens.splash
import android.window.SplashScreen
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -15,7 +16,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -24,14 +25,26 @@ import be.ugent.sel.studeez.R.string as AppText
private const val SPLASH_TIMEOUT = 500L private const val SPLASH_TIMEOUT = 500L
@Composable @Composable
fun SplashScreen( fun SplashRoute(
openAndPopUp: (String, String) -> Unit, openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: SplashViewModel = hiltViewModel() viewModel: SplashViewModel,
) {
SplashScreen(
modifier = modifier,
onAppStart = { viewModel.onAppStart(openAndPopUp) },
showError = viewModel.showError.value
)
}
@Composable
fun SplashScreen(
modifier: Modifier = Modifier,
onAppStart: () -> Unit,
showError: Boolean,
) { ) {
Column( Column(
modifier = modifier = modifier
modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight() .fillMaxHeight()
.background(color = MaterialTheme.colors.background) .background(color = MaterialTheme.colors.background)
@ -39,17 +52,37 @@ fun SplashScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
if (viewModel.showError.value) { if (showError) {
Text(text = stringResource(AppText.generic_error)) Text(text = stringResource(AppText.generic_error))
BasicButton(
BasicButton(AppText.try_again, Modifier.basicButton()) { viewModel.onAppStart(openAndPopUp) } AppText.try_again,
Modifier.basicButton(),
onClick = onAppStart,
)
} else { } else {
CircularProgressIndicator(color = MaterialTheme.colors.onBackground) CircularProgressIndicator(color = MaterialTheme.colors.onBackground)
} }
} }
LaunchedEffect(true) { LaunchedEffect(true) {
delay(SPLASH_TIMEOUT) delay(SPLASH_TIMEOUT)
viewModel.onAppStart(openAndPopUp) onAppStart()
} }
} }
@Preview
@Composable
fun SplashPreview() {
SplashScreen(
onAppStart = {},
showError = false,
)
}
@Preview
@Composable
fun SplashErrorPreview() {
SplashScreen(
onAppStart = {},
showError = true,
)
}

View file

@ -1,122 +1,118 @@
package be.ugent.sel.studeez.screens.timer_overview package be.ugent.sel.studeez.screens.timer_overview
import android.annotation.SuppressLint
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.StealthButton import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.common.composable.TimerEntry
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
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.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
data class TimerOverviewActions(
val getUserTimers: () -> Flow<List<TimerInfo>>,
val getDefaultTimers: () -> List<TimerInfo>,
val onEditClick: (TimerInfo) -> Unit,
)
fun getTimerOverviewActions(
viewModel: TimerOverviewViewModel,
): TimerOverviewActions {
return TimerOverviewActions(
getUserTimers = viewModel::getUserTimers,
getDefaultTimers = viewModel::getDefaultTimers,
onEditClick = { viewModel.update(it) },
)
}
@Composable
fun TimerOverviewRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerOverviewViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) {
TimerOverviewScreen(
timerOverviewActions = getTimerOverviewActions(viewModel),
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable @Composable
fun TimerOverviewScreen( fun TimerOverviewScreen(
open: (String) -> Unit, timerOverviewActions: TimerOverviewActions,
openAndPopUp: (String, String) -> Unit, drawerActions: DrawerActions,
viewModel: TimerOverviewViewModel = hiltViewModel() navigationBarActions: NavigationBarActions,
) { ) {
val timers = viewModel.getUserTimers().collectAsState(initial = emptyList()) val timers = timerOverviewActions.getUserTimers().collectAsState(initial = emptyList())
// TODO moet geen primary screen zijn: geen navbar nodig
PrimaryScreenTemplate( PrimaryScreenTemplate(
title = resources().getString(R.string.timers), title = resources().getString(R.string.timers),
open = open, drawerActions = drawerActions,
openAndPopUp = openAndPopUp navigationBarActions = navigationBarActions,
) { ) {
Column { Column {
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(7.dp) verticalArrangement = Arrangement.spacedBy(7.dp)
) { ) {
// Default Timers, cannot be edited // Default Timers, cannot be edited
items(viewModel.getDefaultTimers()) { items(timerOverviewActions.getDefaultTimers()) {
TimerEntry(timerInfo = it, canDisplay = false) TimerEntry(timerInfo = it) {}
}
// User timers, can be edited
items(timers.value) { timerInfo ->
TimerEntry(
timerInfo = timerInfo,
) {
StealthButton(
text = R.string.edit,
onClick = { timerOverviewActions.onEditClick(timerInfo) }
)
} }
// User timers, can be edited
items(timers.value) {
TimerEntry(timerInfo = it, true, R.string.edit) { timerInfo ->
viewModel.update(timerInfo)
}
} }
} }
BasicButton(R.string.add_timer, Modifier.basicButton()) { BasicButton(R.string.add_timer, Modifier.basicButton()) {
// TODO // TODO
} }
} }
} }
} }
@Composable
fun TimerEntry(
timerInfo: TimerInfo,
canDisplay: Boolean,
@StringRes buttonName: Int = -1,
buttonFunction: (TimerInfo) -> Unit = {}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
Modifier.padding(horizontal = 10.dp)
) {
Text(
text = timerInfo.name,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
Text(
text = timerInfo.description,
fontWeight = FontWeight.Light,
fontSize = 15.sp
)
}
if (canDisplay) {
StealthButton(buttonName) {
buttonFunction(timerInfo)
}
}
}
}
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Preview @Preview
@Composable @Composable
fun TimerEntryPreview() { fun TimerOverviewPreview() {
val timerInfo = CustomTimerInfo( val customTimer = CustomTimerInfo(
"my preview timer", "my preview timer", "This is the description of the timer", 60
"This is the description of the timer", )
60 TimerOverviewScreen(
timerOverviewActions = TimerOverviewActions(
{ flowOf() },
{ listOf(customTimer, customTimer) },
{}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
) )
Scaffold() {
Column() {
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
}
}
} }

View file

@ -3,42 +3,89 @@ package be.ugent.sel.studeez.screens.timer_selection
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.common.composable.TimerEntry
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
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.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.timer_overview.TimerEntry import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
data class TimerSelectionActions(
val getAllTimers: () -> Flow<List<TimerInfo>>,
val startSession: (TimerInfo) -> Unit,
)
fun getTimerSelectionActions(
viewModel: TimerSelectionViewModel,
open: (String) -> Unit,
): TimerSelectionActions {
return TimerSelectionActions(
getAllTimers = viewModel::getAllTimers,
startSession = { viewModel.startSession(open, it) },
)
}
@Composable
fun TimerSelectionRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerSelectionViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) {
TimerSelectionScreen(
timerSelectionActions = getTimerSelectionActions(viewModel, open),
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable @Composable
fun TimerSelectionScreen( fun TimerSelectionScreen(
open: (String) -> Unit, timerSelectionActions: TimerSelectionActions,
openAndPopUp: (String, String) -> Unit, drawerActions: DrawerActions,
viewModel: TimerSelectionViewModel = hiltViewModel() navigationBarActions: NavigationBarActions,
) { ) {
val timers = timerSelectionActions.getAllTimers().collectAsState(initial = emptyList())
val timers = viewModel.getAllTimers().collectAsState(initial = emptyList())
PrimaryScreenTemplate( PrimaryScreenTemplate(
title = resources().getString(R.string.timers), title = resources().getString(R.string.timers),
open = open, drawerActions = drawerActions,
openAndPopUp = openAndPopUp, navigationBarActions = navigationBarActions,
) { ) {
LazyColumn(verticalArrangement = Arrangement.spacedBy(7.dp)) { LazyColumn(verticalArrangement = Arrangement.spacedBy(7.dp)) {
// All timers // All timers
items(timers.value) { items(timers.value) { timerInfo ->
TimerEntry( TimerEntry(
timerInfo = it, timerInfo = timerInfo,
canDisplay = true, ) {
buttonName = R.string.start StealthButton(
) { timerInfo -> text = R.string.start,
viewModel.startSession(open, timerInfo) onClick = { timerSelectionActions.startSession(timerInfo) }
)
} }
} }
} }
} }
} }
@Preview
@Composable
fun TimerSelectionPreview() {
TimerSelectionScreen(
timerSelectionActions = TimerSelectionActions({ flowOf() }, {}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {}),
)
}