Skip to content

Commit d84bc52

Browse files
committed
Test logic and Android UI
Change-Id: Icd2bdd002b1d8c37a78c2ef5e203265e8634f11e
1 parent 6003c29 commit d84bc52

17 files changed

Lines changed: 1445 additions & 4 deletions

File tree

composeApp/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ kotlin {
1717
sourceSets {
1818
androidMain.dependencies {
1919
implementation(compose.preview)
20+
implementation(libs.androidx.navigation3.runtime)
21+
implementation(libs.androidx.navigation3.ui)
2022
implementation(libs.koin.androidx.compose)
2123
}
2224
commonMain.dependencies {
2325
implementation(compose.components.resources)
2426
implementation(compose.foundation)
2527
implementation(compose.material3)
28+
implementation(compose.material3AdaptiveNavigationSuite)
29+
implementation(compose.materialIconsExtended)
2630
implementation(compose.runtime)
2731
implementation(compose.ui)
2832
implementation(libs.kotlinx.serialization.core)

composeApp/src/androidMain/kotlin/app/icampuspass/App.kt

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,95 @@
11
package app.icampuspass
22

3+
import androidx.activity.compose.BackHandler
34
import androidx.compose.foundation.layout.safeDrawingPadding
5+
import androidx.compose.material3.DrawerValue
6+
import androidx.compose.material3.ModalNavigationDrawer
47
import androidx.compose.material3.Surface
8+
import androidx.compose.material3.rememberDrawerState
59
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.mutableStateListOf
11+
import androidx.compose.runtime.remember
12+
import androidx.compose.runtime.rememberCoroutineScope
613
import androidx.compose.ui.Modifier
714
import androidx.compose.ui.graphics.Color
8-
import app.icampuspass.views.screens.GreetingScreen
15+
import androidx.lifecycle.compose.dropUnlessResumed
16+
import androidx.navigation3.runtime.NavKey
17+
import androidx.navigation3.runtime.rememberNavBackStack
18+
import app.icampuspass.views.composables.ModalDrawerSheet
19+
import app.icampuspass.views.navigation.NavDisplay
20+
import app.icampuspass.views.navigation.navkeys.AboutScreenNavKey
21+
import app.icampuspass.views.navigation.navkeys.MainScreenNavKey
22+
import app.icampuspass.views.navigation.navkeys.SettingsScreenNavKey
923
import app.icampuspass.views.theme.Theme
24+
import kotlinx.coroutines.launch
1025

1126
@Composable
1227
fun App() {
1328
Theme {
1429
Surface(color = Color.Black) {
1530
Surface(modifier = Modifier.safeDrawingPadding()) {
16-
GreetingScreen()
31+
val scope = rememberCoroutineScope()
32+
33+
val backStack = remember { mutableStateListOf<NavKey>(MainScreenNavKey) }
34+
35+
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
36+
37+
ModalNavigationDrawer(
38+
drawerContent = {
39+
ModalDrawerSheet(
40+
backStack = backStack,
41+
onMenuClose = {
42+
scope.launch {
43+
drawerState.close()
44+
}
45+
},
46+
onNavigateMain = dropUnlessResumed {
47+
scope.launch {
48+
drawerState.close()
49+
}
50+
51+
scope.launch {
52+
backStack.removeLastOrNull()
53+
backStack.add(MainScreenNavKey)
54+
}
55+
},
56+
onNavigateSettings = dropUnlessResumed {
57+
scope.launch {
58+
drawerState.close()
59+
}
60+
61+
scope.launch {
62+
backStack.removeLastOrNull()
63+
backStack.add(SettingsScreenNavKey)
64+
}
65+
},
66+
onNavigateAbout = dropUnlessResumed {
67+
scope.launch {
68+
drawerState.close()
69+
}
70+
71+
scope.launch {
72+
backStack.removeLastOrNull()
73+
backStack.add(AboutScreenNavKey)
74+
}
75+
}
76+
)
77+
},
78+
drawerState = drawerState,
79+
content = {
80+
NavDisplay(
81+
backStack = backStack,
82+
scope = scope,
83+
drawerState = drawerState
84+
)
85+
}
86+
)
87+
88+
BackHandler(enabled = drawerState.isOpen) {
89+
scope.launch {
90+
drawerState.close()
91+
}
92+
}
1793
}
1894
}
1995
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package app.icampuspass.views.composables
2+
3+
import android.content.res.Configuration.UI_MODE_NIGHT_YES
4+
import android.content.res.Configuration.UI_MODE_TYPE_NORMAL
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.height
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.layout.width
10+
import androidx.compose.material.icons.Icons
11+
import androidx.compose.material.icons.automirrored.filled.MenuOpen
12+
import androidx.compose.material.icons.filled.Home
13+
import androidx.compose.material.icons.filled.Info
14+
import androidx.compose.material.icons.filled.Settings
15+
import androidx.compose.material3.ExperimentalMaterial3Api
16+
import androidx.compose.material3.HorizontalDivider
17+
import androidx.compose.material3.Icon
18+
import androidx.compose.material3.IconButton
19+
import androidx.compose.material3.MaterialTheme
20+
import androidx.compose.material3.ModalDrawerSheet
21+
import androidx.compose.material3.NavigationDrawerItem
22+
import androidx.compose.material3.PlainTooltip
23+
import androidx.compose.material3.Text
24+
import androidx.compose.material3.TooltipAnchorPosition
25+
import androidx.compose.material3.TooltipBox
26+
import androidx.compose.material3.TooltipDefaults
27+
import androidx.compose.material3.TopAppBar
28+
import androidx.compose.material3.TopAppBarDefaults
29+
import androidx.compose.material3.rememberTooltipState
30+
import androidx.compose.runtime.Composable
31+
import androidx.compose.runtime.mutableStateListOf
32+
import androidx.compose.runtime.remember
33+
import androidx.compose.runtime.snapshots.SnapshotStateList
34+
import androidx.compose.ui.Modifier
35+
import androidx.compose.ui.graphics.Color
36+
import androidx.compose.ui.res.stringResource
37+
import androidx.compose.ui.tooling.preview.Preview
38+
import androidx.compose.ui.tooling.preview.PreviewDynamicColors
39+
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
40+
import androidx.compose.ui.tooling.preview.Wallpapers.BLUE_DOMINATED_EXAMPLE
41+
import androidx.compose.ui.tooling.preview.Wallpapers.GREEN_DOMINATED_EXAMPLE
42+
import androidx.compose.ui.tooling.preview.Wallpapers.RED_DOMINATED_EXAMPLE
43+
import androidx.compose.ui.tooling.preview.Wallpapers.YELLOW_DOMINATED_EXAMPLE
44+
import androidx.compose.ui.unit.dp
45+
import androidx.navigation3.runtime.NavKey
46+
import app.icampuspass.R
47+
import app.icampuspass.views.navigation.navkeys.MainScreenNavKey
48+
import app.icampuspass.views.theme.Theme
49+
50+
@OptIn(ExperimentalMaterial3Api::class)
51+
@Composable
52+
fun ModalDrawerSheet(
53+
backStack: SnapshotStateList<NavKey> = remember { mutableStateListOf<NavKey>(MainScreenNavKey) },
54+
onMenuClose: () -> Unit = {},
55+
onNavigateMain: () -> Unit = {},
56+
onNavigateSettings: () -> Unit = {},
57+
onNavigateAbout: () -> Unit = {}
58+
) {
59+
ModalDrawerSheet(modifier = Modifier.width(width = 320.dp)) {
60+
TopAppBar(
61+
title = {
62+
Text(text = stringResource(id = R.string.app_name))
63+
},
64+
navigationIcon = {
65+
TooltipBox(
66+
positionProvider = TooltipDefaults
67+
.rememberTooltipPositionProvider(
68+
positioning = TooltipAnchorPosition.Below,
69+
spacingBetweenTooltipAndAnchor = 16.dp
70+
),
71+
tooltip = {
72+
PlainTooltip(
73+
modifier = Modifier.padding(all = 8.dp)
74+
) {
75+
Text(
76+
text = stringResource(id = R.string.menu_action_close),
77+
modifier = Modifier
78+
.padding(horizontal = 8.dp, vertical = 2.dp),
79+
style = MaterialTheme.typography.labelLarge
80+
)
81+
}
82+
},
83+
state = rememberTooltipState()
84+
) {
85+
IconButton(onClick = onMenuClose) {
86+
Icon(
87+
imageVector = Icons.AutoMirrored.Filled.MenuOpen,
88+
contentDescription = stringResource(id = R.string.menu_action_close)
89+
)
90+
}
91+
}
92+
},
93+
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
94+
)
95+
96+
HorizontalDivider(
97+
modifier = Modifier.padding(bottom = 8.dp)
98+
)
99+
100+
Column(
101+
modifier = Modifier
102+
.padding(horizontal = 8.dp, vertical = 0.dp)
103+
.fillMaxWidth(),
104+
) {
105+
NavigationDrawerItem(
106+
label = {
107+
Text(text = stringResource(id = R.string.menu_item_main))
108+
},
109+
selected = true,
110+
onClick = onNavigateMain,
111+
modifier = Modifier.height(height = 48.dp),
112+
icon = {
113+
Icon(
114+
imageVector = Icons.Filled.Home, // Icons.Outlined.Home
115+
contentDescription = null
116+
)
117+
}
118+
)
119+
120+
NavigationDrawerItem(
121+
label = {
122+
Text(text = stringResource(id = R.string.menu_item_settings))
123+
},
124+
selected = true,
125+
onClick = onNavigateSettings,
126+
modifier = Modifier.height(height = 48.dp),
127+
icon = {
128+
Icon(
129+
imageVector = Icons.Filled.Settings, // Icons.Outlined.Settings
130+
contentDescription = null
131+
)
132+
}
133+
)
134+
135+
NavigationDrawerItem(
136+
label = {
137+
Text(text = stringResource(id = R.string.menu_item_about))
138+
},
139+
selected = true,
140+
onClick = onNavigateAbout,
141+
modifier = Modifier.height(height = 48.dp),
142+
icon = {
143+
Icon(
144+
imageVector = Icons.Filled.Info, // Icons.Outlined.Info
145+
contentDescription = null
146+
)
147+
}
148+
)
149+
}
150+
}
151+
}
152+
153+
@Preview(name = "Light")
154+
@Preview(name = "Dark", uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL)
155+
@PreviewDynamicColors
156+
@Preview(
157+
name = "Dark red",
158+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
159+
wallpaper = RED_DOMINATED_EXAMPLE
160+
)
161+
@Preview(
162+
name = "Dark blue",
163+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
164+
wallpaper = BLUE_DOMINATED_EXAMPLE
165+
)
166+
@Preview(
167+
name = "Dark green",
168+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
169+
wallpaper = GREEN_DOMINATED_EXAMPLE
170+
)
171+
@Preview(
172+
name = "Dark yellow",
173+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
174+
wallpaper = YELLOW_DOMINATED_EXAMPLE
175+
)
176+
@PreviewScreenSizes
177+
@Preview
178+
@Composable
179+
private fun ModalDrawerSheetPreview() {
180+
Theme {
181+
ModalDrawerSheet()
182+
}
183+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package app.icampuspass.views.composables
2+
3+
import android.content.res.Configuration
4+
import android.content.res.Configuration.UI_MODE_NIGHT_YES
5+
import android.content.res.Configuration.UI_MODE_TYPE_NORMAL
6+
import androidx.compose.foundation.isSystemInDarkTheme
7+
import androidx.compose.material.icons.Icons
8+
import androidx.compose.material.icons.filled.Menu
9+
import androidx.compose.material.icons.outlined.AccountCircle
10+
import androidx.compose.material3.ExperimentalMaterial3Api
11+
import androidx.compose.material3.Icon
12+
import androidx.compose.material3.IconButton
13+
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Text
15+
import androidx.compose.material3.TopAppBar
16+
import androidx.compose.material3.darkColorScheme
17+
import androidx.compose.material3.lightColorScheme
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.ui.res.stringResource
20+
import androidx.compose.ui.tooling.preview.Preview
21+
import androidx.compose.ui.tooling.preview.PreviewDynamicColors
22+
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
23+
import androidx.compose.ui.tooling.preview.Wallpapers.BLUE_DOMINATED_EXAMPLE
24+
import androidx.compose.ui.tooling.preview.Wallpapers.GREEN_DOMINATED_EXAMPLE
25+
import androidx.compose.ui.tooling.preview.Wallpapers.RED_DOMINATED_EXAMPLE
26+
import androidx.compose.ui.tooling.preview.Wallpapers.YELLOW_DOMINATED_EXAMPLE
27+
import app.icampuspass.R
28+
29+
@OptIn(ExperimentalMaterial3Api::class)
30+
@Composable
31+
fun TopAppBar(
32+
onNavigateUserPicker: () -> Unit = {}
33+
) {
34+
TopAppBar(
35+
title = {
36+
Text(text = stringResource(id = R.string.app_name))
37+
},
38+
navigationIcon = {
39+
IconButton(onClick = {}) {
40+
Icon(
41+
imageVector = Icons.Filled.Menu,
42+
contentDescription = null
43+
)
44+
}
45+
},
46+
actions = {
47+
IconButton(onClick = onNavigateUserPicker) {
48+
Icon(
49+
imageVector = Icons.Outlined.AccountCircle,
50+
contentDescription = null
51+
)
52+
}
53+
}
54+
)
55+
}
56+
57+
@Preview(name = "Light")
58+
@Preview(name = "Dark", uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL)
59+
@PreviewDynamicColors
60+
@Preview(
61+
name = "Dark red",
62+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
63+
wallpaper = RED_DOMINATED_EXAMPLE
64+
)
65+
@Preview(
66+
name = "Dark blue",
67+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
68+
wallpaper = BLUE_DOMINATED_EXAMPLE
69+
)
70+
@Preview(
71+
name = "Dark green",
72+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
73+
wallpaper = GREEN_DOMINATED_EXAMPLE
74+
)
75+
@Preview(
76+
name = "Dark yellow",
77+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
78+
wallpaper = YELLOW_DOMINATED_EXAMPLE
79+
)
80+
@PreviewScreenSizes
81+
@Preview
82+
@Composable
83+
private fun TopAppBarPreview() {
84+
MaterialTheme(
85+
colorScheme = if (isSystemInDarkTheme()) {
86+
darkColorScheme()
87+
} else {
88+
lightColorScheme()
89+
}
90+
) {
91+
TopAppBar()
92+
}
93+
}

0 commit comments

Comments
 (0)