Skip to content

Commit c0f6928

Browse files
committed
For touchlab#305 - Use Compose Multiplatform to show the UI from :shared on Android
Added moko-resources as the community preferred approach to using strings in similar scenarios. Added the materialIconsExtended dependency for :shared instead of reusing the vector images shown previously from :app since those vectors are not in a format moko-resources supports at this moment (though it support SVGs). While this is a big package R8 will ensure unused resources are stripped from the generated builds at compile time.
1 parent 87166b0 commit c0f6928

File tree

11 files changed

+92
-44
lines changed

11 files changed

+92
-44
lines changed

app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
66
import androidx.compose.foundation.isSystemInDarkTheme
7-
import co.touchlab.kampkit.android.ui.MainScreen
7+
import co.touchlab.kampkit.ui.MainScreen
88
import co.touchlab.kampkit.ui.theme.KaMPKitTheme
99
import co.touchlab.kampkit.injectLogger
1010
import co.touchlab.kampkit.models.BreedViewModel

app/src/main/res/drawable/ic_favorite_24px.xml

-9
This file was deleted.

app/src/main/res/drawable/ic_favorite_border_24px.xml

-9
This file was deleted.

build.gradle.kts

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
plugins {
55
alias(libs.plugins.gradleVersions)
66
alias(libs.plugins.ktlint) apply false
7+
alias(libs.plugins.moko.resources) apply false
78

89
kotlin("multiplatform") version libs.versions.kotlin.get() apply false
910
kotlin("plugin.serialization") version libs.versions.kotlin.get() apply false
@@ -18,13 +19,15 @@ allprojects {
1819
mavenCentral()
1920
maven("https://androidx.dev/storage/compose-compiler/repository/")
2021
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
22+
gradlePluginPortal() // for moko
2123
}
2224
}
2325

2426
subprojects {
2527
// TODO libs doesn't resolve if we do this
2628
// apply(plugin = libs.plugins.ktlint.get().pluginId)
2729
apply(plugin = "org.jlleitschuh.gradle.ktlint")
30+
apply(plugin = "dev.icerock.mobile.multiplatform-resources")
2831

2932
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
3033
enableExperimentalRules.set(true)

gradle/libs.versions.toml

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ multiplatformSettings = "1.0.0"
3636
turbine = "1.0.0"
3737
sqlDelight = "2.0.0-rc01"
3838

39+
moko-resources = "0.23.0"
40+
moko-graphics = "0.9.0"
41+
3942
[libraries]
4043
android-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "android-desugaring" }
4144
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
@@ -87,9 +90,14 @@ touchlab-kermit-simple = { module = "co.touchlab:kermit-simple", version.ref = "
8790
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
8891
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
8992

93+
moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko-resources" }
94+
moko-resources-compose = { module = "dev.icerock.moko:resources-compose", version.ref = "moko-resources" }
95+
moko-graphics = { module = "dev.icerock.moko:graphics", version.ref = "moko-graphics" }
96+
9097
[plugins]
9198
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
9299
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" }
100+
moko-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" }
93101

94102
[bundles]
95103
app-ui = [

shared/build.gradle.kts

+16
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,18 @@ kotlin {
4949
dependencies {
5050
implementation(compose.runtime)
5151
implementation(compose.foundation)
52+
implementation(compose.material) // for PullRefreshIndicator
5253
implementation(compose.material3)
54+
implementation(compose.materialIconsExtended)
5355
implementation(libs.koin.core)
5456
implementation(libs.coroutines.core)
5557
implementation(libs.sqlDelight.coroutinesExt)
5658
implementation(libs.bundles.ktor.common)
5759
implementation(libs.multiplatformSettings.common)
5860
implementation(libs.kotlinx.dateTime)
5961
api(libs.touchlab.kermit)
62+
api(libs.moko.resources)
63+
api(libs.moko.resources.compose)
6064
}
6165
}
6266
val commonTest by getting {
@@ -65,6 +69,8 @@ kotlin {
6569
}
6670
}
6771
val androidMain by getting {
72+
// Below line adds a temporary workaround for https://github.com/icerockdev/moko-resources/issues/531
73+
kotlin.srcDirs("build/generated/moko/androidMain/src")
6874
dependencies {
6975
implementation(libs.compose.activity)
7076
implementation(libs.androidx.lifecycle.viewmodel)
@@ -78,6 +84,7 @@ kotlin {
7884
}
7985
}
8086
val iosMain by getting {
87+
resources.srcDirs("build/generated/moko/iosMain/src")
8188
dependencies {
8289
implementation(libs.sqlDelight.native)
8390
implementation(libs.ktor.client.ios)
@@ -86,6 +93,7 @@ kotlin {
8693
}
8794
val iosTest by getting
8895
val iosSimulatorArm64Main by getting {
96+
resources.srcDirs("build/generated/moko/iosSimulatorArm64Main/src")
8997
dependsOn(iosMain)
9098
}
9199
val iosSimulatorArm64Test by getting {
@@ -105,6 +113,8 @@ kotlin {
105113
isStatic = false // SwiftUI preview requires dynamic framework
106114
linkerOpts("-lsqlite3")
107115
export(libs.touchlab.kermit.simple)
116+
export(libs.moko.resources)
117+
export(libs.moko.graphics)
108118
}
109119
ios.deploymentTarget = "14.0"
110120
podfile = project.file("../ios/Podfile")
@@ -123,3 +133,9 @@ sqldelight {
123133
packageName.set("co.touchlab.kampkit.db")
124134
}
125135
}
136+
137+
multiplatformResources {
138+
multiplatformResourcesPackage = "co.touchlab.kampkit" // required
139+
// multiplatformResourcesSourceSet = "iosSimulatorArm64Main"
140+
multiplatformResourcesClassName = "MR" // optional, default MR
141+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package co.touchlab.kampkit.utils
2+
3+
import android.content.Context
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.platform.LocalContext
6+
import dev.icerock.moko.resources.StringResource
7+
import dev.icerock.moko.resources.desc.Resource
8+
import dev.icerock.moko.resources.desc.StringDesc
9+
import dev.icerock.moko.resources.format
10+
11+
actual class Strings(private val context: Context) {
12+
actual fun get(id: StringResource, args: List<Any>): String {
13+
return when (args.isEmpty()) {
14+
true -> StringDesc.Resource(id).toString(context)
15+
false -> id.format(*args.toTypedArray()).toString(context)
16+
}
17+
}
18+
}
19+
20+
/**
21+
* Get a string existing in the shared module.
22+
*/
23+
@Composable
24+
fun getString(id: StringResource, vararg args: Any) = Strings(LocalContext.current).get(id, args.toList())

app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt

+13-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package co.touchlab.kampkit.android.ui
1+
package co.touchlab.kampkit.ui
22

33
import androidx.compose.animation.Crossfade
44
import androidx.compose.animation.core.FastOutSlowInEasing
@@ -20,33 +20,34 @@ import androidx.compose.material.ExperimentalMaterialApi
2020
import androidx.compose.material.MaterialTheme
2121
import androidx.compose.material.Surface
2222
import androidx.compose.material.Text
23+
import androidx.compose.material.icons.Icons
24+
import androidx.compose.material.icons.filled.Favorite
25+
import androidx.compose.material.icons.filled.FavoriteBorder
2326
import androidx.compose.material.pullrefresh.PullRefreshIndicator
2427
import androidx.compose.material.pullrefresh.pullRefresh
2528
import androidx.compose.material.pullrefresh.rememberPullRefreshState
2629
import androidx.compose.runtime.Composable
2730
import androidx.compose.runtime.LaunchedEffect
31+
import androidx.compose.runtime.collectAsState
2832
import androidx.compose.runtime.getValue
2933
import androidx.compose.runtime.rememberCoroutineScope
3034
import androidx.compose.ui.Alignment
3135
import androidx.compose.ui.Modifier
32-
import androidx.compose.ui.res.painterResource
33-
import androidx.compose.ui.res.stringResource
34-
import androidx.compose.ui.tooling.preview.Preview
3536
import androidx.compose.ui.unit.dp
36-
import androidx.lifecycle.compose.collectAsStateWithLifecycle
37-
import co.touchlab.kampkit.android.R
37+
import co.touchlab.kampkit.MR
3838
import co.touchlab.kampkit.db.Breed
3939
import co.touchlab.kampkit.models.BreedViewModel
4040
import co.touchlab.kampkit.models.BreedViewState
4141
import co.touchlab.kermit.Logger
42+
import dev.icerock.moko.resources.compose.stringResource
4243
import kotlinx.coroutines.launch
4344

4445
@Composable
4546
fun MainScreen(
4647
viewModel: BreedViewModel,
4748
log: Logger
4849
) {
49-
val dogsState by viewModel.breedState.collectAsStateWithLifecycle()
50+
val dogsState by viewModel.breedState.collectAsState()
5051
val scope = rememberCoroutineScope()
5152

5253
MainScreenContent(
@@ -106,7 +107,7 @@ fun Empty() {
106107
verticalArrangement = Arrangement.Center,
107108
horizontalAlignment = Alignment.CenterHorizontally
108109
) {
109-
Text(stringResource(R.string.empty_breeds))
110+
Text(text = stringResource(MR.strings.empty_breeds))
110111
}
111112
}
112113

@@ -166,27 +167,14 @@ fun FavoriteIcon(breed: Breed) {
166167
) { fav ->
167168
if (fav) {
168169
Image(
169-
painter = painterResource(id = R.drawable.ic_favorite_border_24px),
170-
contentDescription = stringResource(R.string.favorite_breed, breed.name)
170+
imageVector = Icons.Default.FavoriteBorder,
171+
contentDescription = stringResource(MR.strings.favorite_breed, breed.name)
171172
)
172173
} else {
173174
Image(
174-
painter = painterResource(id = R.drawable.ic_favorite_24px),
175-
contentDescription = stringResource(R.string.unfavorite_breed, breed.name)
175+
imageVector = Icons.Default.Favorite,
176+
contentDescription = stringResource(MR.strings.unfavorite_breed, breed.name)
176177
)
177178
}
178179
}
179180
}
180-
181-
@Preview
182-
@Composable
183-
fun MainScreenContentPreview_Success() {
184-
MainScreenContent(
185-
dogsState = BreedViewState(
186-
breeds = listOf(
187-
Breed(0, "appenzeller", false),
188-
Breed(1, "australian", true)
189-
)
190-
)
191-
)
192-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package co.touchlab.kampkit.utils
2+
3+
import dev.icerock.moko.resources.StringResource
4+
5+
expect class Strings {
6+
fun get(id: StringResource, args: List<Any>): String
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<resources>
3+
<string name="app_name">KaMP Kit</string>
4+
<string name="favorite_breed">Favorite %1$s</string>
5+
<string name="unfavorite_breed">Unfavorite %1$s</string>
6+
<string name="empty_breeds">Sorry, no doggos found</string>
7+
</resources>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package co.touchlab.kampkit.utils
2+
3+
import dev.icerock.moko.resources.StringResource
4+
import dev.icerock.moko.resources.desc.Resource
5+
import dev.icerock.moko.resources.desc.StringDesc
6+
import dev.icerock.moko.resources.format
7+
8+
actual class Strings {
9+
actual fun get(id: StringResource, args: List<Any>) = when (args.isEmpty()) {
10+
true -> StringDesc.Resource(id).localized()
11+
false -> id.format(*args.toTypedArray()).localized()
12+
}
13+
}

0 commit comments

Comments
 (0)