diff --git a/.gitignore b/.gitignore index 048586ae..99fc23f4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /captures /projectFilesBackup* /keystore.properties +/.kotlin diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8c1ef0f..f6de9129 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,8 @@ import java.io.FileInputStream plugins { alias(libs.plugins.android.application) alias(libs.plugins.unmock) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) } // Place actual signing configuration in "keystore.properties" @@ -74,9 +76,14 @@ android { targetCompatibility = JavaVersion.VERSION_11 } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { viewBinding = true buildConfig = true + compose = true } lint { @@ -99,7 +106,10 @@ dependencies { implementation(libs.okhttp) implementation(libs.typed.preferences) implementation(libs.material) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) implementation(libs.androidx.core) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.androidx.annotation) implementation(libs.androidx.exifinterface) @@ -109,6 +119,7 @@ dependencies { annotationProcessor(libs.dagger.compiler) debugImplementation(libs.leakcanary) + debugImplementation(libs.androidx.ui.tooling) testImplementation(libs.junit) testImplementation(libs.com.google.truth) diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Color.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Color.kt new file mode 100644 index 00000000..72d1514d --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Color.kt @@ -0,0 +1,75 @@ +package de.stephanlindauer.criticalmaps.ui.theme + +import androidx.compose.ui.graphics.Color + +val primaryLight = Color(0xFF111111) +val onPrimaryLight = Color(0xFFFFFFFF) +val primaryContainerLight = Color(0xFF262626) +val onPrimaryContainerLight = Color(0xFF8E8D8C) +val secondaryLight = Color(0xFF5C5B5C) +val onSecondaryLight = Color(0xFFFFFFFF) +val secondaryContainerLight = Color(0xFF757474) +val onSecondaryContainerLight = Color(0xFFFEFBFB) +val tertiaryLight = Color(0xFF715D00) +val onTertiaryLight = Color(0xFFFFFFFF) +val tertiaryContainerLight = Color(0xFFFFD733) +val onTertiaryContainerLight = Color(0xFF725D00) +val errorLight = Color(0xFF91000A) +val onErrorLight = Color(0xFFFFFFFF) +val errorContainerLight = Color(0xFFB71C1C) +val onErrorContainerLight = Color(0xFFFFCAC4) +val backgroundLight = Color(0xFFFDF8F8) +val onBackgroundLight = Color(0xFF1C1B1B) +val surfaceLight = Color(0xFFFDF8F8) +val onSurfaceLight = Color(0xFF1C1B1B) +val surfaceVariantLight = Color(0xFFE0E3E3) +val onSurfaceVariantLight = Color(0xFF444748) +val outlineLight = Color(0xFF747878) +val outlineVariantLight = Color(0xFFC4C7C7) +val scrimLight = Color(0xFF000000) +val inverseSurfaceLight = Color(0xFF313030) +val inverseOnSurfaceLight = Color(0xFFF4F0EF) +val inversePrimaryLight = Color(0xFFC8C6C5) +val surfaceDimLight = Color(0xFFDDD9D8) +val surfaceBrightLight = Color(0xFFFDF8F8) +val surfaceContainerLowestLight = Color(0xFFFFFFFF) +val surfaceContainerLowLight = Color(0xFFF7F3F2) +val surfaceContainerLight = Color(0xFFF1EDEC) +val surfaceContainerHighLight = Color(0xFFEBE7E6) +val surfaceContainerHighestLight = Color(0xFFE5E2E1) + +val primaryDark = Color(0xFFC8C6C5) +val onPrimaryDark = Color(0xFF303030) +val primaryContainerDark = Color(0xFF262626) +val onPrimaryContainerDark = Color(0xFF8E8D8C) +val secondaryDark = Color(0xFFC8C6C6) +val onSecondaryDark = Color(0xFF303030) +val secondaryContainerDark = Color(0xFF757474) +val onSecondaryContainerDark = Color(0xFFFEFBFB) +val tertiaryDark = Color(0xFFFFF6E2) +val onTertiaryDark = Color(0xFF3B2F00) +val tertiaryContainerDark = Color(0xFFFFD733) +val onTertiaryContainerDark = Color(0xFF725D00) +val errorDark = Color(0xFFFFB4AB) +val onErrorDark = Color(0xFF690005) +val errorContainerDark = Color(0xFFB71C1C) +val onErrorContainerDark = Color(0xFFFFCAC4) +val backgroundDark = Color(0xFF141313) +val onBackgroundDark = Color(0xFFE5E2E1) +val surfaceDark = Color(0xFF141313) +val onSurfaceDark = Color(0xFFE5E2E1) +val surfaceVariantDark = Color(0xFF444748) +val onSurfaceVariantDark = Color(0xFFC4C7C7) +val outlineDark = Color(0xFF8E9192) +val outlineVariantDark = Color(0xFF444748) +val scrimDark = Color(0xFF000000) +val inverseSurfaceDark = Color(0xFFE5E2E1) +val inverseOnSurfaceDark = Color(0xFF313030) +val inversePrimaryDark = Color(0xFF5F5E5E) +val surfaceDimDark = Color(0xFF141313) +val surfaceBrightDark = Color(0xFF3A3939) +val surfaceContainerLowestDark = Color(0xFF0E0E0E) +val surfaceContainerLowDark = Color(0xFF1C1B1B) +val surfaceContainerDark = Color(0xFF201F1F) +val surfaceContainerHighDark = Color(0xFF2B2A2A) +val surfaceContainerHighestDark = Color(0xFF353434) diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Theme.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Theme.kt new file mode 100644 index 00000000..6a806306 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Theme.kt @@ -0,0 +1,114 @@ +package de.stephanlindauer.criticalmaps.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext + +private val lightScheme = lightColorScheme( + primary = primaryLight, + onPrimary = onPrimaryLight, + primaryContainer = primaryContainerLight, + onPrimaryContainer = onPrimaryContainerLight, + secondary = secondaryLight, + onSecondary = onSecondaryLight, + secondaryContainer = secondaryContainerLight, + onSecondaryContainer = onSecondaryContainerLight, + tertiary = tertiaryLight, + onTertiary = onTertiaryLight, + tertiaryContainer = tertiaryContainerLight, + onTertiaryContainer = onTertiaryContainerLight, + error = errorLight, + onError = onErrorLight, + errorContainer = errorContainerLight, + onErrorContainer = onErrorContainerLight, + background = backgroundLight, + onBackground = onBackgroundLight, + surface = surfaceLight, + onSurface = onSurfaceLight, + surfaceVariant = surfaceVariantLight, + onSurfaceVariant = onSurfaceVariantLight, + outline = outlineLight, + outlineVariant = outlineVariantLight, + scrim = scrimLight, + inverseSurface = inverseSurfaceLight, + inverseOnSurface = inverseOnSurfaceLight, + inversePrimary = inversePrimaryLight, + surfaceDim = surfaceDimLight, + surfaceBright = surfaceBrightLight, + surfaceContainerLowest = surfaceContainerLowestLight, + surfaceContainerLow = surfaceContainerLowLight, + surfaceContainer = surfaceContainerLight, + surfaceContainerHigh = surfaceContainerHighLight, + surfaceContainerHighest = surfaceContainerHighestLight, +) + +private val darkScheme = darkColorScheme( + primary = primaryDark, + onPrimary = onPrimaryDark, + primaryContainer = primaryContainerDark, + onPrimaryContainer = onPrimaryContainerDark, + secondary = secondaryDark, + onSecondary = onSecondaryDark, + secondaryContainer = secondaryContainerDark, + onSecondaryContainer = onSecondaryContainerDark, + tertiary = tertiaryDark, + onTertiary = onTertiaryDark, + tertiaryContainer = tertiaryContainerDark, + onTertiaryContainer = onTertiaryContainerDark, + error = errorDark, + onError = onErrorDark, + errorContainer = errorContainerDark, + onErrorContainer = onErrorContainerDark, + background = backgroundDark, + onBackground = onBackgroundDark, + surface = surfaceDark, + onSurface = onSurfaceDark, + surfaceVariant = surfaceVariantDark, + onSurfaceVariant = onSurfaceVariantDark, + outline = outlineDark, + outlineVariant = outlineVariantDark, + scrim = scrimDark, + inverseSurface = inverseSurfaceDark, + inverseOnSurface = inverseOnSurfaceDark, + inversePrimary = inversePrimaryDark, + surfaceDim = surfaceDimDark, + surfaceBright = surfaceBrightDark, + surfaceContainerLowest = surfaceContainerLowestDark, + surfaceContainerLow = surfaceContainerLowDark, + surfaceContainer = surfaceContainerDark, + surfaceContainerHigh = surfaceContainerHighDark, + surfaceContainerHighest = surfaceContainerHighestDark, +) + +@Composable +fun AppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = false, + content: @Composable() () -> Unit +) { + + val colorScheme = darkScheme /*when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> darkScheme + else -> lightScheme + } + */ + + MaterialTheme( + colorScheme = colorScheme, + typography = AppTypography, + content = content + ) +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Type.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Type.kt new file mode 100644 index 00000000..7ac8250e --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/ui/theme/Type.kt @@ -0,0 +1,29 @@ +package de.stephanlindauer.criticalmaps.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.font.FontFamily + +val bodyFontFamily = FontFamily.Default + +val displayFontFamily = FontFamily.Serif + +// Default Material 3 typography values +val baseline = Typography() + +val AppTypography = Typography( + displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily), + displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily), + displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily), + headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily), + headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily), + headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily), + titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily), + titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily), + titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily), + bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily), + bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily), + bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily), + labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily), + labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily), + labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily), +) diff --git a/build.gradle.kts b/build.gradle.kts index ae04fa97..c3536ba6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,6 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.unmock) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9419839e..379c977a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ timber = "5.0.1" okhttp = "5.3.2" typedPreferences = "2.1.0" material = "1.13.0" +material3 = "1.4.0" core = "1.17.0" appcompat = "1.7.1" annotation = "1.9.1" @@ -22,6 +23,9 @@ androidTest = "1.7.0" espressoCore = "3.7.0" agp = "8.13.2" unmock = "0.9.0" +kotlin = "2.2.21" +uiToolingPreview = "1.9.4" +uiTooling = "1.9.4" [libraries] otto = { group = "com.squareup", name = "otto", version.ref = "otto" } @@ -31,7 +35,9 @@ timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "tim okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } typed-preferences = { group = "info.metadude.android", name = "typed-preferences", version.ref = "typedPreferences" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } androidx-core = { group = "androidx.core", name = "core", version.ref = "core" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" } androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } @@ -47,7 +53,11 @@ androidx-test-core = { group = "androidx.test", name = "core", version.ref = "an androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidTest" } androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidTest" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "uiToolingPreview" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } -unmock = {id = "de.mobilej.unmock", version.ref= "unmock"} \ No newline at end of file +unmock = {id = "de.mobilej.unmock", version.ref= "unmock"} +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }