Skip to content

Commit e3f6773

Browse files
committed
refactor: implements nav drawer with compose
1 parent d7b4775 commit e3f6773

6 files changed

Lines changed: 268 additions & 3 deletions

File tree

app/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ android {
9090

9191
buildFeatures {
9292
viewBinding = true
93+
compose = true
9394
}
9495

9596
defaultConfig {
@@ -129,6 +130,10 @@ android {
129130
)
130131
}
131132

133+
composeOptions {
134+
kotlinCompilerExtensionVersion = "1.0.0-rc02"
135+
}
136+
132137
signingConfigs {
133138
create("release") {
134139
storeFile = file(KeyLoader.getValue(KeyLoader.KEYSTORE_PATH))
@@ -240,23 +245,27 @@ dependencies {
240245
testImplementation(libs.threetenbp)
241246

242247
implementation(libs.arrowKt)
248+
implementation(libs.androidx.activity.activityCompose)
243249
implementation(libs.androidx.annotation)
244250
implementation(libs.androidx.appcompat)
245251
implementation(libs.androidx.core.ktx)
246252
implementation(libs.androidx.constraintlayout)
247253
implementation(libs.androidx.datastore)
248254
implementation(libs.androidx.fragment.ktx)
255+
implementation(libs.androidx.navigation.navigationCompose)
249256
implementation(libs.androidx.media)
250257
implementation(libs.androidx.paging.runtime.ktx)
251258
implementation(libs.androidx.preference.ktx)
252259
implementation(libs.androidx.recyclerview)
253260
implementation(libs.androidx.swiperefresh)
254261
implementation(libs.androidx.viewpager2)
255262
implementation(libs.androidx.work.ktx)
263+
implementation(libs.bundles.androidx.compose)
256264
implementation(libs.bundles.androidx.lifecycle)
257265
implementation(libs.bundles.androidx.navigation)
258266
implementation(libs.bundles.androidx.room)
259267
implementation(libs.bundles.coroutines)
268+
implementation(libs.bundles.google.accompanist)
260269
implementation(libs.bundles.koin)
261270
implementation(libs.google.material)
262271
implementation(libs.google.protobuf.javalite)
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.kelsos.mbrc.app
2+
3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.fillMaxWidth
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.material.Divider
13+
import androidx.compose.material.MaterialTheme
14+
import androidx.compose.material.Surface
15+
import androidx.compose.material.Text
16+
import androidx.compose.material.TextButton
17+
import androidx.compose.material.icons.Icons
18+
import androidx.compose.material.icons.filled.Close
19+
import androidx.compose.material.icons.filled.Help
20+
import androidx.compose.material.icons.filled.Home
21+
import androidx.compose.material.icons.filled.LibraryMusic
22+
import androidx.compose.material.icons.filled.QueueMusic
23+
import androidx.compose.material.icons.filled.Radio
24+
import androidx.compose.material.icons.filled.Settings
25+
import androidx.compose.material.icons.filled.Speaker
26+
import androidx.compose.material.icons.filled.ViewHeadline
27+
import androidx.compose.material.icons.filled.ViewList
28+
import androidx.compose.runtime.Composable
29+
import androidx.compose.ui.Alignment
30+
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.graphics.Color
32+
import androidx.compose.ui.graphics.ColorFilter
33+
import androidx.compose.ui.graphics.vector.ImageVector
34+
import androidx.compose.ui.res.stringResource
35+
import androidx.compose.ui.tooling.preview.Preview
36+
import androidx.compose.ui.unit.dp
37+
import com.kelsos.mbrc.R
38+
import com.kelsos.mbrc.theme.RemoteTheme
39+
40+
@Composable
41+
fun AppDrawer(
42+
currentRoute: String,
43+
navigateTo: (destination: Destination) -> Unit,
44+
closeDrawer: () -> Unit
45+
) {
46+
Column(modifier = Modifier.fillMaxSize()) {
47+
DrawerButton(
48+
icon = Icons.Filled.Home,
49+
label = stringResource(id = R.string.nav_home),
50+
isSelected = Destination.Home.matches(currentRoute),
51+
action = { navigateTo(Destination.Home) }
52+
)
53+
DrawerButton(
54+
icon = Icons.Filled.LibraryMusic,
55+
label = stringResource(id = R.string.nav_library),
56+
isSelected = Destination.Library.matches(currentRoute),
57+
action = { navigateTo(Destination.Library) }
58+
)
59+
DrawerButton(
60+
icon = Icons.Filled.ViewList,
61+
label = stringResource(id = R.string.nav_now_playing),
62+
isSelected = Destination.NowPlaying.matches(currentRoute),
63+
action = { navigateTo(Destination.NowPlaying) }
64+
)
65+
DrawerButton(
66+
icon = Icons.Filled.QueueMusic,
67+
label = stringResource(id = R.string.nav_playlists),
68+
isSelected = Destination.Playlists.matches(currentRoute),
69+
action = { navigateTo(Destination.Playlists) }
70+
)
71+
DrawerButton(
72+
icon = Icons.Filled.Radio,
73+
label = stringResource(id = R.string.nav_radio),
74+
isSelected = Destination.Radio.matches(currentRoute),
75+
action = { navigateTo(Destination.Radio) }
76+
)
77+
DrawerButton(
78+
icon = Icons.Filled.ViewHeadline,
79+
label = stringResource(id = R.string.nav_lyrics),
80+
isSelected = Destination.Lyrics.matches(currentRoute),
81+
action = { navigateTo(Destination.Lyrics) }
82+
)
83+
84+
Divider(modifier = Modifier.padding(vertical = 8.dp))
85+
86+
Text(
87+
text = stringResource(id = R.string.nav_option_title),
88+
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
89+
style = MaterialTheme.typography.subtitle1,
90+
modifier = Modifier.padding(horizontal = 16.dp)
91+
)
92+
93+
DrawerButton(
94+
icon = Icons.Filled.Speaker,
95+
label = stringResource(id = R.string.nav_output),
96+
isSelected = Destination.OutputSelection.matches(currentRoute),
97+
action = { navigateTo(Destination.OutputSelection) }
98+
)
99+
100+
DrawerButton(
101+
icon = Icons.Filled.Settings,
102+
label = stringResource(id = R.string.nav_settings),
103+
isSelected = Destination.Settings.matches(currentRoute),
104+
action = { navigateTo(Destination.Settings) }
105+
)
106+
107+
DrawerButton(
108+
icon = Icons.Filled.Help,
109+
label = stringResource(id = R.string.nav_help),
110+
isSelected = Destination.Help.matches(currentRoute),
111+
action = { navigateTo(Destination.Help) }
112+
)
113+
114+
DrawerButton(
115+
icon = Icons.Filled.Close,
116+
label = stringResource(id = R.string.nav_exit),
117+
isSelected = false,
118+
action = { TODO("implement exit") }
119+
)
120+
}
121+
}
122+
123+
@Composable
124+
private fun DrawerButton(
125+
icon: ImageVector,
126+
label: String,
127+
isSelected: Boolean,
128+
action: () -> Unit,
129+
modifier: Modifier = Modifier
130+
) {
131+
val colors = MaterialTheme.colors
132+
val imageAlpha = if (isSelected) {
133+
1f
134+
} else {
135+
0.6f
136+
}
137+
val textIconColor = if (isSelected) {
138+
colors.primary
139+
} else {
140+
colors.onSurface.copy(alpha = 0.6f)
141+
}
142+
val backgroundColor = if (isSelected) {
143+
colors.primary.copy(alpha = 0.12f)
144+
} else {
145+
Color.Transparent
146+
}
147+
148+
val surfaceModifier = modifier
149+
.padding(start = 8.dp, top = 8.dp, end = 8.dp)
150+
.fillMaxWidth()
151+
Surface(
152+
modifier = surfaceModifier,
153+
color = backgroundColor,
154+
shape = MaterialTheme.shapes.small
155+
) {
156+
TextButton(
157+
onClick = action,
158+
modifier = Modifier.fillMaxWidth()
159+
) {
160+
Row(
161+
horizontalArrangement = Arrangement.Start,
162+
verticalAlignment = Alignment.CenterVertically,
163+
modifier = Modifier.fillMaxWidth()
164+
) {
165+
Image(
166+
imageVector = icon,
167+
contentDescription = null, // decorative
168+
colorFilter = ColorFilter.tint(textIconColor),
169+
alpha = imageAlpha
170+
)
171+
Spacer(Modifier.width(16.dp))
172+
Text(
173+
text = label,
174+
style = MaterialTheme.typography.body2,
175+
color = textIconColor
176+
)
177+
}
178+
}
179+
}
180+
}
181+
182+
@Preview
183+
@Composable
184+
fun RemoteDrawerPreview() {
185+
RemoteTheme {
186+
AppDrawer(Destination.Home.route, {}, {})
187+
}
188+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.kelsos.mbrc.app
2+
3+
sealed class Destination(val route: String) {
4+
object Home : Destination("home")
5+
object Library : Destination("library")
6+
object NowPlaying : Destination("now_playing")
7+
object Playlists : Destination("playlists")
8+
object Radio : Destination("radio")
9+
object Lyrics : Destination("lyrics")
10+
object OutputSelection : Destination("output_selection")
11+
object Settings : Destination("settings")
12+
object Help : Destination("help")
13+
14+
fun matches(route: String): Boolean = route == this.route
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.kelsos.mbrc.theme
2+
3+
import androidx.compose.ui.graphics.Color
4+
5+
val Primary = Color(0xffff9800)
6+
val PrimaryDark = Color(0xffe65100)
7+
val Accent = Color(0xffff5722)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.kelsos.mbrc.theme
2+
3+
import androidx.compose.material.MaterialTheme
4+
import androidx.compose.material.darkColors
5+
import androidx.compose.runtime.Composable
6+
7+
private val DarkColors = darkColors(
8+
primary = Primary,
9+
primaryVariant = PrimaryDark,
10+
secondary = Accent,
11+
)
12+
13+
@Composable
14+
fun RemoteTheme(content: @Composable () -> Unit) {
15+
MaterialTheme(
16+
colors = DarkColors,
17+
content = content
18+
)
19+
}

gradle/libs.versions.toml

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
[bundles]
2-
androidx-lifecycle = ["androidx-lifecycle-runtime-ktx", "androidx-lifecycle-viewmodel-ktx", "androidx-lifecycle-common"]
3-
androidx-navigation = ["androidx-navigation-ui-ktx", "androidx-navigation-fragment-ktx"]
2+
androidx-compose = ["androidx-compose-compiler", "androidx-compose-foundation", "androidx-compose-material", "androidx-compose-materialIconsExtended", "androidx-compose-runtimeLivedata", "androidx-compose-ui", "androidx-compose-uiTooling"]
3+
androidx-lifecycle = ["androidx-lifecycle-runtime-ktx", "androidx-lifecycle-viewmodel-ktx", "androidx-lifecycle-common","androidx-lifecycle-lifecycleViewmodelCompose"]
4+
androidx-navigation = ["androidx-navigation-ui-ktx", "androidx-navigation-fragment-ktx", "androidx-navigation-navigationCompose"]
45
androidx-room = ["androidx-room-runtime", "androidx-room-ktx"]
56
androidx-test-espresso = ["androidx-test-espresso-intents", "androidx-test-espresso-core", "androidx-test-espresso-contrib"]
67
coroutines = ["kotlin-coroutines-core", "kotlin-coroutines-android"]
8+
google-accompanist = ["google-accompanistCoil", "google-accompanistFlowLayout", "google-accompanistPager", "google-accompanistPagerIndicators", "google-accompanistSystemUIController"]
79
google-firebase = ["google-firebase-analytics-ktx", "google-firebase-crashlytics-ktx", "google-firebase-perf-ktx"]
810
koin = ["koin-android", "koin-viewmodel", "koin-ext", "koin-scope", "koin-fragment", "koin-workmanager"]
911

1012
[libraries]
1113
android-threetenbp = { module = "com.jakewharton.threetenabp:threetenabp", version.ref = "android-threetenbp" }
14+
androidx-activity-activityCompose = { module="androidx.activity:activity-compose", version.ref="activityCompose" }
1215
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
1316
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
1417
androidx-arch-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "androidx-arch-core-testing" }
18+
androidx-compose-compiler = { module="androidx.compose.compiler:compiler", version.ref="compose" }
19+
androidx-compose-foundation = { module="androidx.compose.foundation:foundation", version.ref="compose" }
20+
androidx-compose-material = { module="androidx.compose.material:material", version.ref="compose" }
21+
androidx-compose-materialIconsExtended = { module="androidx.compose.material:material-icons-extended", version.ref="compose" }
22+
androidx-compose-runtimeLivedata = { module="androidx.compose.runtime:runtime-livedata", version.ref="compose" }
23+
androidx-compose-ui = { module="androidx.compose.ui:ui", version.ref="compose" }
24+
androidx-compose-uiTest-junit4 = { module="androidx.compose.ui:ui-test-junit4", version.ref="compose" }
25+
androidx-compose-uiTest-manifest = { module="androidx.compose.ui:ui-test-manifest", version.ref="compose" }
26+
androidx-compose-uiTooling = { module="androidx.compose.ui:ui-tooling", version.ref="composeUiTooling" }
1527
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" }
1628
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" }
1729
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
1830
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
1931
androidx-fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "androidx-fragment" }
2032
androidx-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidx-lifecycle" }
33+
androidx-lifecycle-lifecycleViewmodelCompose = { module="androidx.lifecycle:lifecycle-viewmodel-compose", version.ref="lifecycleViewmodelCompose" }
2134
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
2235
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
2336
androidx-media = { module = "androidx.media:media", version.ref = "androidx-media" }
2437
androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" }
38+
androidx-navigation-navigationCompose = { module="androidx.navigation:navigation-compose", version.ref="navigationCompose" }
2539
androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" }
2640
androidx-paging-runtime-ktx = { module = "androidx.paging:paging-runtime-ktx", version.ref = "androidx-paging-runtime-ktx" }
2741
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "androidx-preference-ktx" }
@@ -42,7 +56,13 @@ androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref =
4256
androidx-work-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
4357
arrowKt = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrowKt" }
4458
cash-app-turbine = { module = "app.cash.turbine:turbine", version.ref = "cash-app-turbine" }
59+
coilKt-coil = { module="io.coil-kt:coil", version.ref="coil" }
4560
com-android-tools-desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar" }
61+
google-accompanistCoil = { module="com.google.accompanist:accompanist-coil", version.ref="accompanist" }
62+
google-accompanistFlowLayout = { module="com.google.accompanist:accompanist-flowlayout", version.ref="accompanist" }
63+
google-accompanistPager = { module="com.google.accompanist:accompanist-pager", version.ref="accompanist" }
64+
google-accompanistPagerIndicators = { module="com.google.accompanist:accompanist-pager-indicators", version.ref="accompanist" }
65+
google-accompanistSystemUIController = { module="com.google.accompanist:accompanist-systemuicontroller", version.ref="accompanist" }
4666
google-firebase-analytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" }
4767
google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
4868
google-firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" }
@@ -81,6 +101,8 @@ threetenbp = { module = "org.threeten:threetenbp", version.ref = "threetenbp" }
81101
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
82102

83103
[versions]
104+
accompanist = "0.13.0"
105+
activityCompose = "1.3.0-beta02"
84106
android-threetenbp = "1.3.1"
85107
androidx-annotation = "1.2.0"
86108
androidx-appcompat = "1.3.0"
@@ -106,6 +128,9 @@ androidx-viewpager2 = "1.0.0"
106128
androidx-work = "2.5.0"
107129
arrowKt = "0.13.2"
108130
cash-app-turbine = "0.5.2"
131+
coil = "1.2.2"
132+
compose = "1.0.0-rc01"
133+
composeUiTooling = "1.0.0-beta09"
109134
desugar = "1.1.5"
110135
firebaseBom = "28.2.0"
111136
google-material = "1.4.0"
@@ -116,12 +141,14 @@ gradlePlugin-gms = "4.3.8"
116141
gradlePlugin-kotlinter = "3.4.5"
117142
gradlePlugin-performance = "1.4.0"
118143
gradlePlugin-protobuf = "0.8.16"
119-
gradlePlugin-safeArgs = "2.3.5"
144+
gradlePlugin-safeArgs = "2.4.0-alpha05"
120145
gradlePlugin-versionsBenManes = "0.39.0"
121146
koin = "2.2.2"
122147
kotlin = "1.5.10"
123148
kotlinCoroutines = "1.5.0"
149+
lifecycleViewmodelCompose = "1.0.0-alpha07"
124150
mockk = "1.12.0"
151+
navigationCompose = "2.4.0-alpha04"
125152
protobuf = "3.10.0"
126153
robolectric = "4.6"
127154
squareup-leakcanary = "2.7"

0 commit comments

Comments
 (0)