diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 1d7687d1c5..f205321ea2 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -4,6 +4,41 @@ + + + diff --git a/accessibility-toolbox/build.gradle.kts b/accessibility-toolbox/build.gradle.kts index 753586114e..b67ab47556 100644 --- a/accessibility-toolbox/build.gradle.kts +++ b/accessibility-toolbox/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidLibrary.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -50,12 +52,13 @@ android { getByName("androidTest").java.srcDirs("src/androidTest/kotlin") } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } lint { diff --git a/build.gradle.kts b/build.gradle.kts index 8288229ce9..3619a3276d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,8 +29,6 @@ buildscript { } } -val sdkVersion = libs.versions.snabbleSdk.get() - allprojects { repositories { google() diff --git a/core/build.gradle.kts b/core/build.gradle.kts index cb3e9b067c..b92f55fd87 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidLibrary.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -48,12 +50,13 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } buildFeatures { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fbd67a8a22..2e065beb5c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,23 +2,23 @@ compileSdk = "35" targetSdk = "35" minSdk = "21" -gradlePlugin = "8.9.2" -kotlin = "2.0.20" -navigation = "2.8.1" +gradlePlugin = "8.11.0" +kotlin = "2.2.0" +navigation = "2.9.1" snabbleSdk = "0.69.6" -androidx-camera = "1.3.4" -androidx-compose-ui = "1.7.2" +androidx-camera = "1.4.2" +androidx-compose-ui = "1.8.3" com-squareup-okhttp3 = "4.12.0" io-kotest = "5.9.1" dokka = "1.9.20" -androidx-compose-material = "1.7.2" -android-lifecycle = "2.8.6" -koin = "4.0.0" +androidx-compose-material = "1.8.3" +android-lifecycle = "2.9.1" +koin = "4.1.0" [libraries] -airbnb-lottie = "com.airbnb.android:lottie:6.5.2" -androidx-activityCompose = "androidx.activity:activity-compose:1.9.2" -androidx-appcompat = "androidx.appcompat:appcompat:1.7.0" +airbnb-lottie = "com.airbnb.android:lottie:6.6.7" +androidx-activityCompose = "androidx.activity:activity-compose:1.10.1" +androidx-appcompat = "androidx.appcompat:appcompat:1.7.1" androidx-biometric = "androidx.biometric:biometric:1.2.0-alpha05" androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" } androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" } @@ -26,13 +26,13 @@ androidx-camera-extension = { module = "androidx.camera:camera-extensions", vers androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" } androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" } androidx-cardview = "androidx.cardview:cardview:1.0.0" -androidx-core-ktx = "androidx.core:core-ktx:1.13.1" -androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" -androidx-constraintlayoutCompose = "androidx.constraintlayout:constraintlayout-compose:1.0.1" +androidx-core-ktx = "androidx.core:core-ktx:1.16.0" +androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:2.2.1" +androidx-constraintlayoutCompose = "androidx.constraintlayout:constraintlayout-compose:1.1.1" # Compose previews won't work w/o this: https://issuetracker.google.com/issues/227767363 -androidx-customview = "androidx.customview:customview:1.1.0" -androidx-customview-poolingcontainer = "androidx.customview:customview-poolingcontainer:1.0.0" -androidx-gridlayout = "androidx.gridlayout:gridlayout:1.0.0" +androidx-customview = "androidx.customview:customview:1.2.0" +androidx-customview-poolingcontainer = "androidx.customview:customview-poolingcontainer:1.1.0" +androidx-gridlayout = "androidx.gridlayout:gridlayout:1.1.0" androidx-lifecycle-common = {module = "androidx.lifecycle:lifecycle-common", version.ref = "android-lifecycle"} androidx-lifecycleExtension = "androidx.lifecycle:lifecycle-extensions:2.2.0" androidx-lifecycleLiveData = {module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "android-lifecycle"} @@ -45,17 +45,17 @@ androidx-navigation-fragmentKtx = { module = "androidx.navigation:navigation-fra androidx-navigation-runtimeKtx = { module = "androidx.navigation:navigation-runtime-ktx", version.ref = "navigation" } androidx-navigation-uiKtx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" } androidx-preferences = "androidx.preference:preference-ktx:1.2.1" -androidx-recyclerview = "androidx.recyclerview:recyclerview:1.3.2" +androidx-recyclerview = "androidx.recyclerview:recyclerview:1.4.0" androidx-startupRuntime = "androidx.startup:startup-runtime:1.2.0" androidx-swiperefreshlayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" androidx-viewpager2 = "androidx.viewpager2:viewpager2:1.1.0" -androidx-webkit = "androidx.webkit:webkit:1.12.0" +androidx-webkit = "androidx.webkit:webkit:1.14.0" apache-commonsLang3 = "org.apache.commons:commons-lang3:3.17.0" caverock-androidsvgAar = "com.caverock:androidsvg-aar:1.4" -compose-iconsExtended = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose-material" } +compose-iconsExtended = { module = "androidx.compose.material:material-icons-extended", version = "1.7.8" } compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" } compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose-material" } -compose-material3 = "androidx.compose.material3:material3:1.3.0" +compose-material3 = "androidx.compose.material3:material3:1.3.2" compose-material3Themeadapter = "com.google.accompanist:accompanist-themeadapter-material3:0.36.0" compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose-ui" } compose-uiTestManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-compose-ui" } @@ -64,18 +64,18 @@ compose-uiTooling = { module = "androidx.compose.ui:ui-tooling", version.ref = " compose-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "androidx-compose-ui" } compose-uiUtil = { module = "androidx.compose.ui:ui-util", version.ref = "androidx-compose-ui" } compose-uiViewBinding = { module = "androidx.compose.ui:ui-viewbinding", version.ref = "androidx-compose-ui" } -commonsIo = "commons-io:commons-io:2.17.0" +commonsIo = "commons-io:commons-io:2.19.0" datatrans-androidSdk = "ch.datatrans:android-sdk:3.7.0" -desugarJdkLibsNio = "com.android.tools:desugar_jdk_libs_nio:2.1.2" +desugarJdkLibsNio = "com.android.tools:desugar_jdk_libs_nio:2.1.5" glide-compose = "com.github.bumptech.glide:compose:1.0.0-beta01" -googlePlayServices-maps = "com.google.android.gms:play-services-maps:19.0.0" +googlePlayServices-maps = "com.google.android.gms:play-services-maps:19.2.0" googlePlayServices-wallet = "com.google.android.gms:play-services-wallet:19.4.0" google-mlkit-barcodeScanning = "com.google.mlkit:barcode-scanning:17.3.0" google-zxing-core = "com.google.zxing:core:3.5.3" -gson = "com.google.code.gson:gson:2.11.0" +gson = "com.google.code.gson:gson:2.13.1" iban4j = "org.iban4j:iban4j:3.2.10-RELEASE" jakewhartonProcessPhoenix = "com.jakewharton:process-phoenix:3.0.0" -picasso = "com.squareup.picasso:picasso:2.8" +picasso = "com.squareup.picasso:picasso:2.71828" rekisoftLazyWorker = "eu.rekisoft.android.util:LazyWorker:2.1.0" relex-circleindicator = "me.relex:circleindicator:2.1.6" snabble-phoneAuth-countryCodePicker = "io.snabble.phoneauth:countryCodePicker:3.3.0" @@ -93,13 +93,13 @@ kotest-runnerJunit = { module = "io.kotest:kotest-runner-junit5", version.ref = kotest-assertionsCore = { module = "io.kotest:kotest-assertions-core", version.ref = "io-kotest" } koltin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } -kotlinx-serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3" -mockk = "io.mockk:mockk:1.13.12" +kotlinx-serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0" +mockk = "io.mockk:mockk:1.14.4" # @pin requires higher target mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:5.4.0" -roboletric = "org.robolectric:robolectric:4.13" +roboletric = "org.robolectric:robolectric:4.15.1" roboletric-androidAll = "org.robolectric:android-all:13-robolectric-9030017" -sebaslogen-resaca = "io.github.sebaslogen:resaca:4.3.0" +sebaslogen-resaca = "io.github.sebaslogen:resaca:4.4.8" squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" } test-espressoCore = "androidx.test.espresso:espresso-core:3.6.1" test-ext-junit = "androidx.test.ext:junit:1.2.1" @@ -111,7 +111,7 @@ classpath-dokkaGradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugi classpath-dokkaBase = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" } classpath-jlouns-gradleCrossPlatformExecPlugin = "gradle.plugin.com.github.jlouns:gradle-cross-platform-exec-plugin:0.5.0" classpath-qmazzo-sqlitePlugin = "gradle.plugin.gmazzo:sqlite-plugin:0.2" -classpath-bjoernq-unmockPlugin = "com.github.bjoernq:unmockplugin:0.8.0" +classpath-bjoernq-unmockPlugin = "com.github.bjoernq:unmockplugin:0.9.0" [bundles] camera = [ @@ -157,6 +157,6 @@ kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } snabbleSetup = "io.snabble.setup:1.0.1" unmock = "de.mobilej.unmock:0.7.9" -benManesVersions = "com.github.ben-manes.versions:0.51.0" -versionCatalogUpdate = "nl.littlerobots.version-catalog-update:0.8.4" +benManesVersions = "com.github.ben-manes.versions:0.52.0" +versionCatalogUpdate = "nl.littlerobots.version-catalog-update:1.0.0" compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c8200..37f853b1c8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/kotlin-sample/build.gradle.kts b/kotlin-sample/build.gradle.kts index 595789ae42..dc2c557350 100644 --- a/kotlin-sample/build.gradle.kts +++ b/kotlin-sample/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidApplication.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -17,7 +19,10 @@ android { versionCode = 1 versionName = "1.0" - resourceConfigurations.addAll(listOf("de", "en")) + androidResources { + localeFilters.addAll(listOf("de", "en")) + } + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -35,12 +40,13 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } buildFeatures { diff --git a/mlkit-scanner-engine/build.gradle.kts b/mlkit-scanner-engine/build.gradle.kts index 31b95fa357..ff46be9b40 100644 --- a/mlkit-scanner-engine/build.gradle.kts +++ b/mlkit-scanner-engine/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidLibrary.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -43,14 +45,16 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } + lint { abortOnError = false } diff --git a/ui-toolkit/build.gradle.kts b/ui-toolkit/build.gradle.kts index e833257be6..9240e423f5 100644 --- a/ui-toolkit/build.gradle.kts +++ b/ui-toolkit/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidLibrary.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -52,12 +54,13 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } buildFeatures { @@ -97,6 +100,7 @@ dependencies { implementation(libs.sebaslogen.resaca) implementation(libs.bundles.compose) + implementation(libs.androidx.swiperefreshlayout) debugImplementation(libs.bundles.compose.debug) implementation(libs.bundles.koin) diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ButtonItem.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ButtonItem.kt index 441c85cee4..54d4b3a491 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ButtonItem.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ButtonItem.kt @@ -5,7 +5,7 @@ import androidx.annotation.ColorRes data class ButtonItem( override val id: String, val text: String, - @ColorRes val foregroundColor: Int?, - @ColorRes val backgroundColor: Int?, + @param:ColorRes val foregroundColor: Int?, + @param:ColorRes val backgroundColor: Int?, val padding: Padding, ) : Widget diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/CustomerCardItem.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/CustomerCardItem.kt index 85848a5d79..85dc24cbed 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/CustomerCardItem.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/CustomerCardItem.kt @@ -5,6 +5,6 @@ import androidx.annotation.DrawableRes data class CustomerCardItem( override val id: String, val text: String, - @DrawableRes val image: Int?, + @param:DrawableRes val image: Int?, val padding: Padding, ) : Widget diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ImageItem.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ImageItem.kt index b038aeac92..8015572890 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ImageItem.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/ImageItem.kt @@ -4,6 +4,6 @@ import androidx.annotation.DrawableRes data class ImageItem( override val id: String, - @DrawableRes val image: Int?, + @param:DrawableRes val image: Int?, val padding: Padding, ) : Widget diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/TextItem.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/TextItem.kt index 8575802aa7..8cda72006e 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/TextItem.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/domain/model/TextItem.kt @@ -5,7 +5,7 @@ import androidx.annotation.ColorInt data class TextItem( override val id: String, val text: String, - @ColorInt val textColor: Int? = null, + @param:ColorInt val textColor: Int? = null, val textStyle: String? = null, val showDisclosure: Boolean, val padding: Padding, diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/ui/DynamicScreen.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/ui/DynamicScreen.kt index b2133e9a1b..4c4d5fc7b5 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/ui/DynamicScreen.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/dynamicview/ui/DynamicScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel import io.snabble.sdk.dynamicview.domain.model.Configuration import io.snabble.sdk.dynamicview.domain.model.DynamicConfig import io.snabble.sdk.dynamicview.domain.model.ImageItem @@ -55,7 +56,9 @@ internal fun DynamicScreen( @Composable private fun DynamicScreenPreviewWith(config: DynamicConfig) { ThemeWrapper { - DynamicScreen(dynamicViewModel = DynamicViewModel().apply { setConfig(config) }, modifier = Modifier) + val viewModel: DynamicViewModel = viewModel() + viewModel.apply { setConfig(config) } + DynamicScreen(dynamicViewModel = viewModel, modifier = Modifier) } } diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/screens/onboarding/ui/OnboardingFragment.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/screens/onboarding/ui/OnboardingFragment.kt index 4f746684b3..7118116100 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/screens/onboarding/ui/OnboardingFragment.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/screens/onboarding/ui/OnboardingFragment.kt @@ -43,7 +43,7 @@ open class OnboardingFragment : Fragment() { val headerImage = view.findViewById(R.id.image_header) headerImage.resolveImageOrHide(model.configuration?.imageSource) - viewPager = view.findViewById(R.id.view_pager).apply { + viewPager = view.findViewById(R.id.view_pager).apply { adapter = StepAdapter(model) offscreenPageLimit = 1 } diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/purchase/repository/PurchasesRepository.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/purchase/repository/PurchasesRepository.kt index a0418e2717..a0df979a9f 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/purchase/repository/PurchasesRepository.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/purchase/repository/PurchasesRepository.kt @@ -45,7 +45,7 @@ internal class PurchasesRepositoryImpl( val receipts = filter { it.pdfUrl != null } return when { receipts.isEmpty() -> emptyList() - else -> receipts.slice(0 until size.coerceAtMost(count)) + else -> receipts.slice(0 until size.coerceAtMost(count).coerceAtMost(receipts.size)) .map { it.toPurchase(timeFormatter) } } } diff --git a/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/toggle/ui/ToggleWidget.kt b/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/toggle/ui/ToggleWidget.kt index 80157e8673..2506fa0c9a 100644 --- a/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/toggle/ui/ToggleWidget.kt +++ b/ui-toolkit/src/main/kotlin/io/snabble/sdk/widgets/snabble/toggle/ui/ToggleWidget.kt @@ -16,7 +16,9 @@ import org.koin.core.parameter.parametersOf internal fun ToggleWidget( modifier: Modifier = Modifier, model: ToggleItem, - viewModel: ToggleViewModel = viewModelScoped(model.key) { KoinProvider.get { parametersOf(model.key) } }, + viewModel: ToggleViewModel = viewModelScoped(key = model.key) { + KoinProvider.get { parametersOf(model.key) } + }, onAction: OnDynamicAction, ) { val isCheckedState = viewModel.toggleState.collectAsStateWithLifecycle() diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 0d00861273..f9178fbc56 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id(libs.plugins.androidLibrary.get().pluginId) id(libs.plugins.kotlin.android.get().pluginId) @@ -47,12 +49,13 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - freeCompilerArgs = listOf( - *kotlinOptions.freeCompilerArgs.toTypedArray(), - "-Xjvm-default=all" - ) - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + freeCompilerArgs.addAll( + "-Xjvm-default=all" + ) + } } lint { diff --git a/ui/src/main/java/io/snabble/sdk/ui/BaseFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/BaseFragment.kt index b488a033dc..af38219b0e 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/BaseFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/BaseFragment.kt @@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment import io.snabble.sdk.InitializationState import io.snabble.sdk.Snabble -abstract class BaseFragment(@LayoutRes val layoutResId: Int = 0, val waitForProject: Boolean = true) : Fragment() { +abstract class BaseFragment(@param:LayoutRes val layoutResId: Int = 0, val waitForProject: Boolean = true) : Fragment() { private lateinit var sdkNotInitialized: TextView private lateinit var fragmentContainer: ViewGroup diff --git a/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/cartdiscount/model/CartDiscountItem.kt b/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/cartdiscount/model/CartDiscountItem.kt index 26e5656dd9..21cb7ed9cb 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/cartdiscount/model/CartDiscountItem.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/cartdiscount/model/CartDiscountItem.kt @@ -8,8 +8,8 @@ import io.snabble.sdk.ui.cart.shoppingcart.CartItem internal data class CartDiscountItem( override val item: ShoppingCart.Item, - @StringRes val title: Int = R.string.Snabble_Shoppingcart_discounts, + @param:StringRes val title: Int = R.string.Snabble_Shoppingcart_discounts, val discount: String, val name: String, - @DrawableRes val imageResId: Int = R.drawable.snabble_ic_percent + @param:DrawableRes val imageResId: Int = R.drawable.snabble_ic_percent ) : CartItem diff --git a/ui/src/main/java/io/snabble/sdk/ui/checkout/CheckoutActivity.kt b/ui/src/main/java/io/snabble/sdk/ui/checkout/CheckoutActivity.kt index ef386f946f..cdf873365b 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/checkout/CheckoutActivity.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/checkout/CheckoutActivity.kt @@ -1,22 +1,23 @@ package io.snabble.sdk.ui.checkout -import android.annotation.SuppressLint import android.content.Context import android.content.Intent +import android.content.res.Configuration +import android.graphics.Color +import android.os.Build import android.os.Bundle +import android.view.Gravity import android.view.View import android.view.ViewGroup -import android.view.WindowManager import android.widget.FrameLayout import androidx.activity.addCallback import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatDelegate import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams -import androidx.core.view.updateMargins import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer import androidx.navigation.NavController @@ -42,6 +43,9 @@ class CheckoutActivity : FragmentActivity() { const val ARG_PROJECT_ID = "projectId" + private const val HSV_COLOR_SIZE = 3 + private const val DARKEN_FACTOR = 0.2f + @JvmStatic fun startCheckoutFlow(context: Context, newTask: Boolean = false) { val intent = Intent(context, CheckoutActivity::class.java).apply { @@ -157,7 +161,7 @@ class CheckoutActivity : FragmentActivity() { private fun setUpToolBarAndStatusBar() { val showToolBar = resources.getBoolean(R.bool.showToolbarInCheckout) - findViewById(R.id.checkout_toolbar_spacer)?.isVisible = showToolBar + findViewById(R.id.checkout_toolbar)?.isVisible = showToolBar navController.addOnDestinationChangedListener { _, _, arguments -> findViewById(R.id.checkout_toolbar)?.isVisible = @@ -165,19 +169,13 @@ class CheckoutActivity : FragmentActivity() { val toolBarColor = this@CheckoutActivity.toolBarColorForProject(Snabble.checkedInProject.value) + if (showToolBar) toolBarColor?.let { applyStatusBarStyling(toolBarColor) } val onToolBarColor = this@CheckoutActivity.onToolBarColorForProject(Snabble.checkedInProject.value) this.findViewById(R.id.checkout_toolbar).apply { toolBarColor?.let(::setBackgroundColor) onToolBarColor?.let(::setTitleTextColor) } - this.findViewById(R.id.checkout_toolbar_spacer).apply { - toolBarColor?.let(::setBackgroundColor) - } - } - - if (showToolBar) { - applyInsets() } } @@ -256,35 +254,63 @@ class CheckoutActivity : FragmentActivity() { } } - private fun applyInsets() { - val root = findViewById(R.id.root) ?: return - - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) - WindowCompat.setDecorFitsSystemWindows(window, false) + private fun applyStatusBarStyling(color: Int) { + val root = findViewById(R.id.root) val windowInsetsController = WindowInsetsControllerCompat(window, root) + setStatusBarColor(darkenColor(color)) + windowInsetsController.isAppearanceLightStatusBars = !isNightModeActive + } - ViewCompat.setOnApplyWindowInsetsListener(root) { view, windowInsets -> - window.statusBarColor = 0x22000000 - windowInsetsController.isAppearanceLightStatusBars = false - - val currentInsetTypeMask = listOf( - WindowInsetsCompat.Type.navigationBars(), - WindowInsetsCompat.Type.ime(), - WindowInsetsCompat.Type.systemBars(), - WindowInsetsCompat.Type.statusBars() - ).fold(0) { accumulator, type -> accumulator or type } - - @SuppressLint("WrongConstant") - val insets = windowInsets.getInsets(currentInsetTypeMask) - view.updateLayoutParams { - updateMargins(insets.left, 0, insets.right, insets.bottom) - } - findViewById(R.id.checkout_toolbar_spacer)?.apply { - setPadding(0, insets.top, 0, 0) + private fun darkenColor(color: Int): Int { + val hsv = FloatArray(HSV_COLOR_SIZE) + Color.colorToHSV(color, hsv) + hsv[2] *= (1f - DARKEN_FACTOR) // Reduce brightness + return Color.HSVToColor(hsv) + } + + private fun setStatusBarColor(color: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { // API 35+ + // For API 35+, use edge-to-edge but handle status bar coloring properly + WindowCompat.setDecorFitsSystemWindows(window, false) + + // Create a status bar overlay instead of coloring the entire root view + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { view, insets -> + val statusBarInsets = insets.getInsets(WindowInsetsCompat.Type.statusBars()) + + // Create a colored overlay just for the status bar area + createStatusBarOverlay(color, statusBarInsets.top) + + insets } + } else { + // For API < 35, use the traditional method + @Suppress("DEPRECATION") + window.statusBarColor = color + } + } - windowInsets.inset(insets.left, insets.top, insets.right, insets.bottom) + private var statusBarOverlay: View? = null + + private fun createStatusBarOverlay(color: Int, height: Int) { + // Remove any existing overlay + statusBarOverlay?.let { + (it.parent as? ViewGroup)?.removeView(it) } + + // Create new overlay positioned absolutely at the top + statusBarOverlay = View(this).apply { + setBackgroundColor(color) + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + height + ).apply { + gravity = Gravity.TOP // Position at the very top + } + } + + // Add overlay to the window's decor view + val decorView = window.decorView as FrameLayout + decorView.addView(statusBarOverlay) } private fun onStateChanged() { @@ -297,3 +323,14 @@ class CheckoutActivity : FragmentActivity() { }.build()) } } + +val Context.isNightModeActive: Boolean + get() { + return when (AppCompatDelegate.getDefaultNightMode()) { + AppCompatDelegate.MODE_NIGHT_YES -> true + AppCompatDelegate.MODE_NIGHT_NO -> false + else -> + resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == + Configuration.UI_MODE_NIGHT_YES + } + } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentMethodMetaData.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentMethodMetaData.kt index 16fbc38666..14ffe2bd95 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentMethodMetaData.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentMethodMetaData.kt @@ -71,6 +71,6 @@ class PaymentMethodMetaDataHelper( } data class PaymentMethodMetaData( - @DrawableRes val iconResId: Int, + @param:DrawableRes val iconResId: Int, val label: String ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/scanner/ProductResolver.kt b/ui/src/main/java/io/snabble/sdk/ui/scanner/ProductResolver.kt index cbca15667f..f72df60a1a 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/scanner/ProductResolver.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/scanner/ProductResolver.kt @@ -8,7 +8,6 @@ import io.snabble.sdk.Unit import io.snabble.sdk.codes.ScannedCode import io.snabble.sdk.codes.gs1.GS1Code import io.snabble.sdk.ui.R -import io.snabble.sdk.ui.scanner.ProductResolver.* import io.snabble.sdk.ui.telemetry.Telemetry import io.snabble.sdk.ui.utils.DelayedProgressDialog import io.snabble.sdk.ui.utils.UIUtils @@ -112,9 +111,9 @@ class ProductResolver private constructor(private val context: Context, private for (i in scannedCodes.indices) { val scannedCode = scannedCodes[i] productDatabase.findByCodeOnline(scannedCode, object : OnProductAvailableListener { - override fun onProductAvailable(product: Product, wasOnlineProduct: Boolean) { + override fun onProductAvailable(product: Product, wasOnline: Boolean) { result.product = product - result.wasOnlineProduct = wasOnlineProduct + result.wasOnlineProduct = wasOnline result.code = scannedCode result.matchCount++ countDownLatch.countDown() diff --git a/ui/src/main/res/layout/snabble_activity_checkout.xml b/ui/src/main/res/layout/snabble_activity_checkout.xml index 7b99a9bcc5..8dd4ea0553 100644 --- a/ui/src/main/res/layout/snabble_activity_checkout.xml +++ b/ui/src/main/res/layout/snabble_activity_checkout.xml @@ -7,13 +7,10 @@ android:fitsSystemWindows="true" android:orientation="vertical"> - - + android:fitsSystemWindows="true"> - +