Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
indent_style = space
indent_size = 4
max_line_length = 120
insert_final_newline = true
trim_trailing_whitespace = true

[*.{kt,kts}]
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ktlint_standard_filename = disabled
ktlint_standard_function-naming = disabled
ktlint_standard_package-name = disabled
ktlint_standard_property-naming = disabled

[**/build/generated/**/*.kt]
ktlint = disabled
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ To build a debug APK for Android from the command line, run:
Once the build completes successfully, the generated APK will be located at:
`composeApp/build/outputs/apk/debug/composeApp-debug.apk`

### Code Style
Kotlin formatting is enforced with the Gradle ktlint plugin. Run the check before submitting Kotlin changes:
```sh
./gradlew ktlintCheck
```

Most formatting issues can be fixed automatically with:
```sh
./gradlew ktlintFormat
```

The ktlint setup lives in the Gradle version catalog, the root and `composeApp` Gradle scripts, and `.editorconfig`.
Generated Kotlin files under `build/generated` are skipped, and a few existing project conventions are configured there:
the `smiling_pixel` package segment, Compose-style function names, existing interface file names, and existing preference
key naming.

### Running Tests
To run all multiplatform tests across configured targets:
```sh
Expand All @@ -78,3 +94,11 @@ Or to run tests for a specific platform (e.g., Desktop/JVM):
```sh
./gradlew composeApp:jvmTest
```

The broader Gradle verification command is:
```sh
./gradlew check
```

At the time ktlint was added, `ktlintCheck` passed, while `check` was intentionally not pursued further because the
remaining failure was the unrelated Wasm yarn lock actualization task `kotlinWasmStoreYarnLock`.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ plugins {
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
}
alias(libs.plugins.ktlint) apply false
}
67 changes: 46 additions & 21 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ plugins {
alias(libs.plugins.composeCompiler)
alias(libs.plugins.composeHotReload)
alias(libs.plugins.kotlinSerialization)
alias(libs.plugins.ktlint)
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
Expand All @@ -18,33 +19,46 @@ plugins {

val enableIos = project.findProperty("enableIos")?.toString()?.toBoolean() == true

ktlint {
filter {
exclude("**/ActualResourceCollectors.kt")
exclude("**/AppDatabaseConstructor.kt")
exclude("**/AppDatabase_Impl.kt")
exclude("**/DiaryRoomDao_Impl.kt")
exclude("**/Drawable*.kt")
exclude("**/ExpectResourceCollectors.kt")
exclude("**/FileMetadataRoomDao_Impl.kt")
exclude("**/Res.kt")
}
}

kotlin {
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}

if (enableIos) {
listOf(
iosArm64(),
iosSimulatorArm64()
iosSimulatorArm64(),
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "ComposeApp"
isStatic = true
}
}
}

jvm()

@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
binaries.executable()
}

sourceSets {
all {
languageSettings.optIn("kotlin.time.ExperimentalTime")
Expand All @@ -71,7 +85,7 @@ kotlin {
implementation(libs.kotlinx.coroutines.core)
// Kotlinx serialization (used by typed navigation routes)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0")

implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
Expand All @@ -81,7 +95,7 @@ kotlin {
implementation(libs.coil.compose)
implementation(libs.coil.network.ktor)
}

val nonWebMain by creating {
dependsOn(commonMain.get())
dependencies {
Expand Down Expand Up @@ -134,12 +148,21 @@ room {

android {
namespace = "io.github.smiling_pixel.mark_day"
compileSdk = libs.versions.android.compileSdk.get().toInt()
compileSdk =
libs.versions.android.compileSdk
.get()
.toInt()

defaultConfig {
applicationId = "io.github.smiling_pixel.mark_day"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
minSdk =
libs.versions.android.minSdk
.get()
.toInt()
targetSdk =
libs.versions.android.targetSdk
.get()
.toInt()
versionCode = 1
versionName = "1.0"
}
Expand All @@ -148,18 +171,20 @@ android {
// Exclude metadata files from Java libraries (like Google API clients)
// that cause packaging conflicts (duplicate files) when merging into the APK.
// These files are only used by desktop JVMs and are unnecessary on Android.
excludes += setOf(
"/META-INF/{AL2.0,LGPL2.1}",
"META-INF/INDEX.LIST",
"META-INF/DEPENDENCIES"
)
excludes +=
setOf(
"/META-INF/{AL2.0,LGPL2.1}",
"META-INF/INDEX.LIST",
"META-INF/DEPENDENCIES",
)
// Keep a single copy of license/notice files for OSS compliance
pickFirsts += setOf(
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt"
)
pickFirsts +=
setOf(
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt",
)
}
}
buildTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import io.github.smiling_pixel.database.createDatabase
import androidx.lifecycle.lifecycleScope
import io.github.smiling_pixel.client.GoogleSignInHelper
import io.github.smiling_pixel.database.DiaryRepository
import io.github.smiling_pixel.database.createDatabase
import io.github.smiling_pixel.filesystem.FileRepository
import io.github.smiling_pixel.filesystem.fileManager
import io.github.smiling_pixel.preference.AndroidContextProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

import androidx.activity.result.contract.ActivityResultContracts
import io.github.smiling_pixel.client.GoogleSignInHelper

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
Expand All @@ -30,7 +28,7 @@ class MainActivity : ComponentActivity() {
lifecycleScope.launch {
GoogleSignInHelper.onActivityResult(result)
}
}
},
)

// Build Room-backed repository on Android and pass it into App
Expand All @@ -53,4 +51,4 @@ class MainActivity : ComponentActivity() {
@Composable
fun AppAndroidPreview() {
App()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}

actual fun getPlatform(): Platform = AndroidPlatform()
actual fun getPlatform(): Platform = AndroidPlatform()
Loading
Loading