diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt b/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
index b9f8402d..91a9d0fd 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
+++ b/app/src/main/kotlin/co/touchlab/kampkit/android/MainActivity.kt
@@ -4,7 +4,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.isSystemInDarkTheme
-import co.touchlab.kampkit.android.ui.MainScreen
+import co.touchlab.kampkit.ui.MainScreen
import co.touchlab.kampkit.ui.theme.KaMPKitTheme
import co.touchlab.kampkit.injectLogger
import co.touchlab.kampkit.models.BreedViewModel
diff --git a/app/src/main/res/drawable/ic_favorite_24px.xml b/app/src/main/res/drawable/ic_favorite_24px.xml
deleted file mode 100644
index ce351f43..00000000
--- a/app/src/main/res/drawable/ic_favorite_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_favorite_border_24px.xml b/app/src/main/res/drawable/ic_favorite_border_24px.xml
deleted file mode 100644
index e6646709..00000000
--- a/app/src/main/res/drawable/ic_favorite_border_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/build.gradle.kts b/build.gradle.kts
index 673ba272..b71bca0a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,6 +4,7 @@
plugins {
alias(libs.plugins.gradleVersions)
alias(libs.plugins.ktlint) apply false
+ alias(libs.plugins.moko.resources) apply false
kotlin("multiplatform") version libs.versions.kotlin.get() apply false
kotlin("plugin.serialization") version libs.versions.kotlin.get() apply false
@@ -18,6 +19,7 @@ allprojects {
mavenCentral()
maven("https://androidx.dev/storage/compose-compiler/repository/")
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
+ gradlePluginPortal() // for moko
}
}
@@ -25,6 +27,7 @@ subprojects {
// TODO libs doesn't resolve if we do this
// apply(plugin = libs.plugins.ktlint.get().pluginId)
apply(plugin = "org.jlleitschuh.gradle.ktlint")
+ apply(plugin = "dev.icerock.mobile.multiplatform-resources")
configure {
enableExperimentalRules.set(true)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d50d86d6..695c9c94 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -36,6 +36,9 @@ multiplatformSettings = "1.0.0"
turbine = "1.0.0"
sqlDelight = "2.0.0-rc01"
+moko-resources = "0.23.0"
+moko-graphics = "0.9.0"
+
[libraries]
android-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "android-desugaring" }
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 = "
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko-resources" }
+moko-resources-compose = { module = "dev.icerock.moko:resources-compose", version.ref = "moko-resources" }
+moko-graphics = { module = "dev.icerock.moko:graphics", version.ref = "moko-graphics" }
+
[plugins]
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" }
+moko-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" }
[bundles]
app-ui = [
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index d6ee7bf7..6447a7c1 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -49,7 +49,9 @@ kotlin {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
+ implementation(compose.material) // for PullRefreshIndicator
implementation(compose.material3)
+ implementation(compose.materialIconsExtended)
implementation(libs.koin.core)
implementation(libs.coroutines.core)
implementation(libs.sqlDelight.coroutinesExt)
@@ -57,6 +59,8 @@ kotlin {
implementation(libs.multiplatformSettings.common)
implementation(libs.kotlinx.dateTime)
api(libs.touchlab.kermit)
+ api(libs.moko.resources)
+ api(libs.moko.resources.compose)
}
}
val commonTest by getting {
@@ -65,6 +69,8 @@ kotlin {
}
}
val androidMain by getting {
+ // Below line adds a temporary workaround for https://github.com/icerockdev/moko-resources/issues/531
+ kotlin.srcDirs("build/generated/moko/androidMain/src")
dependencies {
implementation(libs.compose.activity)
implementation(libs.androidx.lifecycle.viewmodel)
@@ -78,6 +84,7 @@ kotlin {
}
}
val iosMain by getting {
+ resources.srcDirs("build/generated/moko/iosMain/src")
dependencies {
implementation(libs.sqlDelight.native)
implementation(libs.ktor.client.ios)
@@ -86,6 +93,7 @@ kotlin {
}
val iosTest by getting
val iosSimulatorArm64Main by getting {
+ resources.srcDirs("build/generated/moko/iosSimulatorArm64Main/src")
dependsOn(iosMain)
}
val iosSimulatorArm64Test by getting {
@@ -105,6 +113,8 @@ kotlin {
isStatic = false // SwiftUI preview requires dynamic framework
linkerOpts("-lsqlite3")
export(libs.touchlab.kermit.simple)
+ export(libs.moko.resources)
+ export(libs.moko.graphics)
}
ios.deploymentTarget = "14.0"
podfile = project.file("../ios/Podfile")
@@ -123,3 +133,9 @@ sqldelight {
packageName.set("co.touchlab.kampkit.db")
}
}
+
+multiplatformResources {
+ multiplatformResourcesPackage = "co.touchlab.kampkit" // required
+ // multiplatformResourcesSourceSet = "iosSimulatorArm64Main"
+ multiplatformResourcesClassName = "MR" // optional, default MR
+}
diff --git a/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..8d69d97d
--- /dev/null
+++ b/shared/src/androidMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,24 @@
+package co.touchlab.kampkit.utils
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import dev.icerock.moko.resources.StringResource
+import dev.icerock.moko.resources.desc.Resource
+import dev.icerock.moko.resources.desc.StringDesc
+import dev.icerock.moko.resources.format
+
+actual class Strings(private val context: Context) {
+ actual fun get(id: StringResource, args: List): String {
+ return when (args.isEmpty()) {
+ true -> StringDesc.Resource(id).toString(context)
+ false -> id.format(*args.toTypedArray()).toString(context)
+ }
+ }
+}
+
+/**
+ * Get a string existing in the shared module.
+ */
+@Composable
+fun getString(id: StringResource, vararg args: Any) = Strings(LocalContext.current).get(id, args.toList())
diff --git a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
similarity index 83%
rename from app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt
rename to shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
index 002c2624..7f03c87b 100644
--- a/app/src/main/kotlin/co/touchlab/kampkit/android/ui/Composables.kt
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/ui/Composables.kt
@@ -1,4 +1,4 @@
-package co.touchlab.kampkit.android.ui
+package co.touchlab.kampkit.ui
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FastOutSlowInEasing
@@ -20,25 +20,26 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import co.touchlab.kampkit.android.R
+import co.touchlab.kampkit.MR
import co.touchlab.kampkit.db.Breed
import co.touchlab.kampkit.models.BreedViewModel
import co.touchlab.kampkit.models.BreedViewState
import co.touchlab.kermit.Logger
+import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.launch
@Composable
@@ -46,7 +47,7 @@ fun MainScreen(
viewModel: BreedViewModel,
log: Logger
) {
- val dogsState by viewModel.breedState.collectAsStateWithLifecycle()
+ val dogsState by viewModel.breedState.collectAsState()
val scope = rememberCoroutineScope()
MainScreenContent(
@@ -106,7 +107,7 @@ fun Empty() {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text(stringResource(R.string.empty_breeds))
+ Text(text = stringResource(MR.strings.empty_breeds))
}
}
@@ -166,27 +167,14 @@ fun FavoriteIcon(breed: Breed) {
) { fav ->
if (fav) {
Image(
- painter = painterResource(id = R.drawable.ic_favorite_border_24px),
- contentDescription = stringResource(R.string.favorite_breed, breed.name)
+ imageVector = Icons.Default.FavoriteBorder,
+ contentDescription = stringResource(MR.strings.favorite_breed, breed.name)
)
} else {
Image(
- painter = painterResource(id = R.drawable.ic_favorite_24px),
- contentDescription = stringResource(R.string.unfavorite_breed, breed.name)
+ imageVector = Icons.Default.Favorite,
+ contentDescription = stringResource(MR.strings.unfavorite_breed, breed.name)
)
}
}
}
-
-@Preview
-@Composable
-fun MainScreenContentPreview_Success() {
- MainScreenContent(
- dogsState = BreedViewState(
- breeds = listOf(
- Breed(0, "appenzeller", false),
- Breed(1, "australian", true)
- )
- )
- )
-}
diff --git a/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..ed868d2f
--- /dev/null
+++ b/shared/src/commonMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,7 @@
+package co.touchlab.kampkit.utils
+
+import dev.icerock.moko.resources.StringResource
+
+expect class Strings {
+ fun get(id: StringResource, args: List): String
+}
diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml
new file mode 100644
index 00000000..5ac1e5a9
--- /dev/null
+++ b/shared/src/commonMain/resources/MR/base/strings.xml
@@ -0,0 +1,7 @@
+
+
+ KaMP Kit
+ Favorite %1$s
+ Unfavorite %1$s
+ Sorry, no doggos found
+
diff --git a/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt b/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
new file mode 100644
index 00000000..1502f5cd
--- /dev/null
+++ b/shared/src/iosMain/kotlin/co/touchlab/kampkit/utils/Strings.kt
@@ -0,0 +1,13 @@
+package co.touchlab.kampkit.utils
+
+import dev.icerock.moko.resources.StringResource
+import dev.icerock.moko.resources.desc.Resource
+import dev.icerock.moko.resources.desc.StringDesc
+import dev.icerock.moko.resources.format
+
+actual class Strings {
+ actual fun get(id: StringResource, args: List) = when (args.isEmpty()) {
+ true -> StringDesc.Resource(id).localized()
+ false -> id.format(*args.toTypedArray()).localized()
+ }
+}