diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index e59cc53bc..2c921f091 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -1,7 +1,7 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.1 +androidx.annotation:annotation-experimental:1.4.0 androidx.annotation:annotation-jvm:1.7.0 androidx.annotation:annotation:1.7.0 androidx.appcompat:appcompat-resources:1.6.1 @@ -9,48 +9,50 @@ androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 androidx.browser:browser:1.6.0 -androidx.collection:collection-jvm:1.3.0 -androidx.collection:collection:1.3.0 -androidx.compose.animation:animation-android:1.5.4 -androidx.compose.animation:animation-core-android:1.5.4 -androidx.compose.animation:animation-core:1.5.4 -androidx.compose.animation:animation:1.5.4 -androidx.compose.foundation:foundation-android:1.5.4 -androidx.compose.foundation:foundation-layout-android:1.5.4 -androidx.compose.foundation:foundation-layout:1.5.4 -androidx.compose.foundation:foundation:1.5.4 -androidx.compose.material3:material3:1.1.2 -androidx.compose.material:material-icons-core-android:1.5.4 -androidx.compose.material:material-icons-core:1.5.4 -androidx.compose.material:material-icons-extended-android:1.5.4 -androidx.compose.material:material-icons-extended:1.5.4 -androidx.compose.material:material-ripple-android:1.5.4 -androidx.compose.material:material-ripple:1.5.4 -androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-saveable-android:1.5.4 -androidx.compose.runtime:runtime-saveable:1.5.4 -androidx.compose.runtime:runtime:1.5.4 -androidx.compose.ui:ui-android:1.5.4 -androidx.compose.ui:ui-geometry-android:1.5.4 -androidx.compose.ui:ui-geometry:1.5.4 -androidx.compose.ui:ui-graphics-android:1.5.4 -androidx.compose.ui:ui-graphics:1.5.4 -androidx.compose.ui:ui-text-android:1.5.4 -androidx.compose.ui:ui-text:1.5.4 -androidx.compose.ui:ui-tooling-preview-android:1.5.4 -androidx.compose.ui:ui-tooling-preview:1.5.4 -androidx.compose.ui:ui-unit-android:1.5.4 -androidx.compose.ui:ui-unit:1.5.4 -androidx.compose.ui:ui-util-android:1.5.4 -androidx.compose.ui:ui-util:1.5.4 -androidx.compose.ui:ui:1.5.4 -androidx.compose:compose-bom:2023.10.01 +androidx.collection:collection-jvm:1.4.0 +androidx.collection:collection-ktx:1.4.0 +androidx.collection:collection:1.4.0 +androidx.compose.animation:animation-android:1.6.1 +androidx.compose.animation:animation-core-android:1.6.1 +androidx.compose.animation:animation-core:1.6.1 +androidx.compose.animation:animation:1.6.1 +androidx.compose.foundation:foundation-android:1.6.1 +androidx.compose.foundation:foundation-layout-android:1.6.1 +androidx.compose.foundation:foundation-layout:1.6.1 +androidx.compose.foundation:foundation:1.6.1 +androidx.compose.material3:material3-android:1.2.0 +androidx.compose.material3:material3:1.2.0 +androidx.compose.material:material-icons-core-android:1.6.1 +androidx.compose.material:material-icons-core:1.6.1 +androidx.compose.material:material-icons-extended-android:1.6.1 +androidx.compose.material:material-icons-extended:1.6.1 +androidx.compose.material:material-ripple-android:1.6.1 +androidx.compose.material:material-ripple:1.6.1 +androidx.compose.runtime:runtime-android:1.6.1 +androidx.compose.runtime:runtime-saveable-android:1.6.1 +androidx.compose.runtime:runtime-saveable:1.6.1 +androidx.compose.runtime:runtime:1.6.1 +androidx.compose.ui:ui-android:1.6.1 +androidx.compose.ui:ui-geometry-android:1.6.1 +androidx.compose.ui:ui-geometry:1.6.1 +androidx.compose.ui:ui-graphics-android:1.6.1 +androidx.compose.ui:ui-graphics:1.6.1 +androidx.compose.ui:ui-text-android:1.6.1 +androidx.compose.ui:ui-text:1.6.1 +androidx.compose.ui:ui-tooling-preview-android:1.6.1 +androidx.compose.ui:ui-tooling-preview:1.6.1 +androidx.compose.ui:ui-unit-android:1.6.1 +androidx.compose.ui:ui-unit:1.6.1 +androidx.compose.ui:ui-util-android:1.6.1 +androidx.compose.ui:ui-util:1.6.1 +androidx.compose.ui:ui:1.6.1 +androidx.compose:compose-bom:2024.02.00 androidx.concurrent:concurrent-futures:1.1.0 androidx.core:core-ktx:1.12.0 androidx.core:core:1.12.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.0.0 -androidx.emoji2:emoji2:1.4.0 +androidx.emoji2:emoji2:1.3.0 androidx.exifinterface:exifinterface:1.3.6 androidx.fragment:fragment:1.5.1 androidx.interpolator:interpolator:1.0.0 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index 6cbde1cae..6264ac1d3 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -1,7 +1,7 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.1 +androidx.annotation:annotation-experimental:1.4.0 androidx.annotation:annotation-jvm:1.7.0 androidx.annotation:annotation:1.7.0 androidx.appcompat:appcompat-resources:1.6.1 @@ -10,44 +10,46 @@ androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 androidx.browser:browser:1.6.0 -androidx.collection:collection-jvm:1.3.0 -androidx.collection:collection-ktx:1.3.0 -androidx.collection:collection:1.3.0 -androidx.compose.animation:animation-android:1.5.4 -androidx.compose.animation:animation-core-android:1.5.4 -androidx.compose.animation:animation-core:1.5.4 -androidx.compose.animation:animation:1.5.4 -androidx.compose.foundation:foundation-android:1.5.4 -androidx.compose.foundation:foundation-layout-android:1.5.4 -androidx.compose.foundation:foundation-layout:1.5.4 -androidx.compose.foundation:foundation:1.5.4 -androidx.compose.material3:material3-window-size-class:1.1.2 -androidx.compose.material3:material3:1.1.2 -androidx.compose.material:material-icons-core-android:1.5.4 -androidx.compose.material:material-icons-core:1.5.4 -androidx.compose.material:material-icons-extended-android:1.5.4 -androidx.compose.material:material-icons-extended:1.5.4 -androidx.compose.material:material-ripple-android:1.5.4 -androidx.compose.material:material-ripple:1.5.4 -androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-saveable-android:1.5.4 -androidx.compose.runtime:runtime-saveable:1.5.4 -androidx.compose.runtime:runtime:1.5.4 -androidx.compose.ui:ui-android:1.5.4 -androidx.compose.ui:ui-geometry-android:1.5.4 -androidx.compose.ui:ui-geometry:1.5.4 -androidx.compose.ui:ui-graphics-android:1.5.4 -androidx.compose.ui:ui-graphics:1.5.4 -androidx.compose.ui:ui-text-android:1.5.4 -androidx.compose.ui:ui-text:1.5.4 -androidx.compose.ui:ui-tooling-preview-android:1.5.4 -androidx.compose.ui:ui-tooling-preview:1.5.4 -androidx.compose.ui:ui-unit-android:1.5.4 -androidx.compose.ui:ui-unit:1.5.4 -androidx.compose.ui:ui-util-android:1.5.4 -androidx.compose.ui:ui-util:1.5.4 -androidx.compose.ui:ui:1.5.4 -androidx.compose:compose-bom:2023.10.01 +androidx.collection:collection-jvm:1.4.0 +androidx.collection:collection-ktx:1.4.0 +androidx.collection:collection:1.4.0 +androidx.compose.animation:animation-android:1.6.1 +androidx.compose.animation:animation-core-android:1.6.1 +androidx.compose.animation:animation-core:1.6.1 +androidx.compose.animation:animation:1.6.1 +androidx.compose.foundation:foundation-android:1.6.1 +androidx.compose.foundation:foundation-layout-android:1.6.1 +androidx.compose.foundation:foundation-layout:1.6.1 +androidx.compose.foundation:foundation:1.6.1 +androidx.compose.material3:material3-android:1.2.0 +androidx.compose.material3:material3-window-size-class-android:1.2.0 +androidx.compose.material3:material3-window-size-class:1.2.0 +androidx.compose.material3:material3:1.2.0 +androidx.compose.material:material-icons-core-android:1.6.1 +androidx.compose.material:material-icons-core:1.6.1 +androidx.compose.material:material-icons-extended-android:1.6.1 +androidx.compose.material:material-icons-extended:1.6.1 +androidx.compose.material:material-ripple-android:1.6.1 +androidx.compose.material:material-ripple:1.6.1 +androidx.compose.runtime:runtime-android:1.6.1 +androidx.compose.runtime:runtime-saveable-android:1.6.1 +androidx.compose.runtime:runtime-saveable:1.6.1 +androidx.compose.runtime:runtime:1.6.1 +androidx.compose.ui:ui-android:1.6.1 +androidx.compose.ui:ui-geometry-android:1.6.1 +androidx.compose.ui:ui-geometry:1.6.1 +androidx.compose.ui:ui-graphics-android:1.6.1 +androidx.compose.ui:ui-graphics:1.6.1 +androidx.compose.ui:ui-text-android:1.6.1 +androidx.compose.ui:ui-text:1.6.1 +androidx.compose.ui:ui-tooling-preview-android:1.6.1 +androidx.compose.ui:ui-tooling-preview:1.6.1 +androidx.compose.ui:ui-unit-android:1.6.1 +androidx.compose.ui:ui-unit:1.6.1 +androidx.compose.ui:ui-util-android:1.6.1 +androidx.compose.ui:ui-util:1.6.1 +androidx.compose.ui:ui:1.6.1 +androidx.compose:compose-bom:2024.02.00 androidx.concurrent:concurrent-futures:1.1.0 androidx.core:core-ktx:1.12.0 androidx.core:core-splashscreen:1.0.1 @@ -61,8 +63,8 @@ androidx.datastore:datastore-preferences:1.0.0 androidx.datastore:datastore:1.0.0 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 -androidx.emoji2:emoji2-views-helper:1.4.0 -androidx.emoji2:emoji2:1.4.0 +androidx.emoji2:emoji2-views-helper:1.3.0 +androidx.emoji2:emoji2:1.3.0 androidx.exifinterface:exifinterface:1.3.6 androidx.fragment:fragment:1.5.1 androidx.hilt:hilt-common:1.1.0 diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index dcbc1e5c0..f7345f04f 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -37,6 +37,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepositor import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.BindValue @@ -140,13 +141,15 @@ class NiaAppScreenSizesScreenshotTests { ) { TestHarness(size = DpSize(width, height)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaTheme { + NiaApp( + windowSizeClass = WindowSizeClass.calculateFromSize( + DpSize(maxWidth, maxHeight), + ), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + ) + } } } } diff --git a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png index 04ccb8424..011a97e37 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png index 833ecdf07..1c9213f3e 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png index e9e0c76cf..0754d5b35 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png index bcaf32ebe..f4dfb09aa 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png index d40d06be0..70af31fa7 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png index ce2c055ef..c5b7fe883 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png index bbde94375..5ed3d9445 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png index 7591fb871..233718a57 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png index e2961bddf..f914a0454 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png differ diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 5f3fb97c0..f3bf46fc2 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -53,7 +53,9 @@ internal fun Project.configureKotlinMultiplatform() { } // tier 1 - linuxX64() +// :core:datastore:linuxMain: Could not resolve com.russhwolf:multiplatform-settings-no-arg:1.1.1. +// https://github.com/russhwolf/multiplatform-settings/issues/113 +// linuxX64() macosX64() macosArm64() iosSimulatorArm64() @@ -82,8 +84,8 @@ internal fun Project.configureKotlinMultiplatform() { // linking fails for the linux test build if not built on a linux host // ensure the tests and linking for them is only done on linux hosts - project.tasks.named("linuxX64Test") { enabled = HostManager.hostIsLinux } - project.tasks.named("linkDebugTestLinuxX64") { enabled = HostManager.hostIsLinux } +// project.tasks.named("linuxX64Test") { enabled = HostManager.hostIsLinux } +// project.tasks.named("linkDebugTestLinuxX64") { enabled = HostManager.hostIsLinux } // Suppress 'expect'/'actual' classes are in Beta. targets.configureEach { diff --git a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index e5eb5090b..eb2c9ec5a 100644 --- a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -24,7 +24,6 @@ import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.android.AndroidSqliteDriver import me.tatarka.inject.annotations.Component -import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Provides @Component diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index 7fec4df6f..ba69cfc03 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -23,7 +23,6 @@ import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryD import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import com.google.samples.apps.nowinandroid.core.di.IODispatcher -import kotlinx.coroutines.Dispatchers import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides diff --git a/core/datastore-proto/build.gradle.kts b/core/datastore-proto/build.gradle.kts index b39c544a6..53f2d19f6 100644 --- a/core/datastore-proto/build.gradle.kts +++ b/core/datastore-proto/build.gradle.kts @@ -17,6 +17,7 @@ plugins { alias(libs.plugins.nowinandroid.kmp.library) alias(libs.plugins.protobuf) + id("kotlinx-serialization") } android { @@ -31,9 +32,6 @@ protobuf { generateProtoTasks { all().forEach { task -> task.builtins { - register("java") { - option("lite") - } register("kotlin") { option("lite") } @@ -42,18 +40,11 @@ protobuf { } } -androidComponents.beforeVariants { - android.sourceSets.register(it.name) { - val buildDir = layout.buildDirectory.get().asFile - java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java")) - kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin")) - } -} - kotlin { sourceSets { commonMain.dependencies { api(libs.protobuf.kotlin.lite) + implementation(libs.kotlinx.serialization.core) } } } diff --git a/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/DarkThemeConfigProto.kt b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/DarkThemeConfigProto.kt new file mode 100644 index 000000000..38c047828 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/DarkThemeConfigProto.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.datastore + +enum class DarkThemeConfigProto { + DARK_THEME_CONFIG_UNSPECIFIED, + DARK_THEME_CONFIG_FOLLOW_SYSTEM, + DARK_THEME_CONFIG_LIGHT, + DARK_THEME_CONFIG_DARK, +} diff --git a/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ThemeBrandProto.kt b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ThemeBrandProto.kt new file mode 100644 index 000000000..1b10258ff --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ThemeBrandProto.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.datastore + +enum class ThemeBrandProto { + THEME_BRAND_UNSPECIFIED, + THEME_BRAND_DEFAULT, + THEME_BRAND_ANDROID, +} diff --git a/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferences.kt b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferences.kt new file mode 100644 index 000000000..f8168fc0c --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferences.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.datastore + +import com.google.samples.apps.nowinandroid.core.datastore.DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM +import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_UNSPECIFIED +import kotlinx.serialization.Serializable + +// A lot of workaround brought by Proto +@Serializable +data class UserPreferences( + val topicChangeListVersion: Int, + val authorChangeListVersion: Int, + val newsResourceChangeListVersion: Int, + val hasDoneIntToStringIdMigration: Boolean, + val hasDoneListToMapMigration: Boolean, + val followedTopicIds: Set = emptySet(), + val followedAuthorIds: Set = emptySet(), + val bookmarkedNewsResourceIds: Set = emptySet(), + val viewedNewsResourceIds: Set = emptySet(), + val themeBrand: ThemeBrandProto, + val darkThemeConfig: DarkThemeConfigProto, + val shouldHideOnboarding: Boolean, + val useDynamicColor: Boolean, +) { + companion object { + val DEFAULT = UserPreferences( + topicChangeListVersion = 0, + authorChangeListVersion = 0, + newsResourceChangeListVersion = 0, + hasDoneIntToStringIdMigration = false, + hasDoneListToMapMigration = false, + themeBrand = THEME_BRAND_UNSPECIFIED, + darkThemeConfig = DARK_THEME_CONFIG_FOLLOW_SYSTEM, + shouldHideOnboarding = false, + useDynamicColor = false, + ) + } +} diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index c6cf6bdac..883b16025 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -39,17 +39,15 @@ kotlin { implementation(libs.multiplatform.settings.serialization) implementation(libs.multiplatform.settings.coroutines) implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.core) implementation(projects.core.model) implementation(projects.core.common) + implementation(projects.core.datastoreProto) } commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) - } - androidMain.dependencies { - implementation(libs.androidx.datastore.core) - implementation(libs.androidx.datastore.preferences) - implementation(libs.multiplatform.settings.datastore) + implementation(libs.multiplatform.settings.test) } } } diff --git a/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreComponent.kt b/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreComponent.kt deleted file mode 100644 index 6ac6bcd41..000000000 --- a/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreComponent.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore.di - -import android.content.Context -import androidx.datastore.core.DataStore -import androidx.datastore.core.DataStoreFactory -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.preferencesDataStore -import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer -import me.tatarka.inject.annotations.Component -import me.tatarka.inject.annotations.Provides - -@Component -abstract class DataStoreComponent { - - private val Context.dataStore by preferencesDataStore("user_preferences") - - @Provides - fun providesDataStore(context: Context): DataStore { - return context.dataStore - } -} \ No newline at end of file diff --git a/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt b/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt deleted file mode 100644 index 0ef09c647..000000000 --- a/core/datastore/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore.di - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import com.russhwolf.settings.ExperimentalSettingsApi -import com.russhwolf.settings.coroutines.FlowSettings -import com.russhwolf.settings.datastore.DataStoreSettings -import me.tatarka.inject.annotations.Component -import me.tatarka.inject.annotations.Provides - -@Component -actual abstract class SettingsComponent { - @OptIn(ExperimentalSettingsApi::class) - @Provides - actual fun providesFlowSettings( - dataStore: DataStore - ): FlowSettings { - return DataStoreSettings() - } -} \ No newline at end of file diff --git a/core/datastore/src/commonMain/AndroidManifest.xml b/core/datastore/src/commonMain/AndroidManifest.xml deleted file mode 100644 index 51d0cfc2e..000000000 --- a/core/datastore/src/commonMain/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - \ No newline at end of file diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt deleted file mode 100644 index ef9c1dd03..000000000 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import androidx.datastore.core.DataMigration - -/** - * Migrates saved ids from [Int] to [String] types - */ -internal object IntToStringIdsMigration : DataMigration { - - override suspend fun cleanUp() = Unit - - override suspend fun migrate(currentData: UserPreferences): UserPreferences = - currentData.copy { - // Migrate topic ids - deprecatedFollowedTopicIds.clear() - deprecatedFollowedTopicIds.addAll( - currentData.deprecatedIntFollowedTopicIdsList.map(Int::toString), - ) - deprecatedIntFollowedTopicIds.clear() - - // Migrate author ids - deprecatedFollowedAuthorIds.clear() - deprecatedFollowedAuthorIds.addAll( - currentData.deprecatedIntFollowedAuthorIdsList.map(Int::toString), - ) - deprecatedIntFollowedAuthorIds.clear() - - // Mark migration as complete - hasDoneIntToStringIdMigration = true - } - - override suspend fun shouldMigrate(currentData: UserPreferences): Boolean = - !currentData.hasDoneIntToStringIdMigration -} diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt deleted file mode 100644 index 5675aee05..000000000 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import androidx.datastore.core.DataMigration - -/** - * Migrates from using lists to maps for user data. - */ -internal object ListToMapMigration : DataMigration { - - override suspend fun cleanUp() = Unit - - override suspend fun migrate(currentData: UserPreferences): UserPreferences = - currentData.copy { - // Migrate topic id lists - followedTopicIds.clear() - followedTopicIds.putAll( - currentData.deprecatedFollowedTopicIdsList.associateWith { true }, - ) - deprecatedFollowedTopicIds.clear() - - // Migrate author ids - followedAuthorIds.clear() - followedAuthorIds.putAll( - currentData.deprecatedFollowedAuthorIdsList.associateWith { true }, - ) - deprecatedFollowedAuthorIds.clear() - - // Migrate bookmarks - bookmarkedNewsResourceIds.clear() - bookmarkedNewsResourceIds.putAll( - currentData.deprecatedBookmarkedNewsResourceIdsList.associateWith { true }, - ) - deprecatedBookmarkedNewsResourceIds.clear() - - // Mark migration as complete - hasDoneListToMapMigration = true - } - - override suspend fun shouldMigrate(currentData: UserPreferences): Boolean = - !currentData.hasDoneListToMapMigration -} diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index e68e19468..078af8243 100644 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -16,193 +16,199 @@ package com.google.samples.apps.nowinandroid.core.datastore -import android.util.Log -import androidx.datastore.core.DataStore +import com.google.samples.apps.nowinandroid.core.di.IODispatcher import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand -import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.russhwolf.settings.ExperimentalSettingsApi -import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.Settings -import com.russhwolf.settings.coroutines.FlowSettings -import com.russhwolf.settings.coroutines.toFlowSettings -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map -import java.io.IOException -import javax.inject.Inject - -@OptIn(ExperimentalSettingsApi::class) -class NiaPreferencesDataSource constructor( - private val settings: FlowSettings, +import com.russhwolf.settings.serialization.decodeValue +import com.russhwolf.settings.serialization.decodeValueOrNull +import com.russhwolf.settings.serialization.encodeValue +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi + +private const val USER_DATA_KEY = "userData" + +@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class) +class NiaPreferencesDataSource( + private val settings: Settings, + private val dispatcher: IODispatcher, ) { + // FlowSettings did not support JS, use a workaround instead + // https://github.com/russhwolf/multiplatform-settings/issues/139 + val userData = MutableStateFlow( + settings.decodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + defaultValue = settings.decodeValueOrNull( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + ) ?: UserPreferences.DEFAULT, + ), + ) + + suspend fun setFollowedTopicIds(topicIds: Set) = withContext(dispatcher) { + val preference = settings.getUserPreference() + .copy(followedTopicIds = topicIds) + .updateShouldHideOnboardingIfNecessary() + settings.putUserPreference(preference) + userData.value = preference + } - val userData = userPreferences.data - .map { - UserData( - bookmarkedNewsResources = it.bookmarkedNewsResourceIdsMap.keys, - viewedNewsResources = it.viewedNewsResourceIdsMap.keys, - followedTopics = it.followedTopicIdsMap.keys, - themeBrand = when (it.themeBrand) { - null, - ThemeBrandProto.THEME_BRAND_UNSPECIFIED, - ThemeBrandProto.UNRECOGNIZED, - ThemeBrandProto.THEME_BRAND_DEFAULT, - -> ThemeBrand.DEFAULT - ThemeBrandProto.THEME_BRAND_ANDROID -> ThemeBrand.ANDROID - }, - darkThemeConfig = when (it.darkThemeConfig) { - null, - DarkThemeConfigProto.DARK_THEME_CONFIG_UNSPECIFIED, - DarkThemeConfigProto.UNRECOGNIZED, - DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM, - -> - DarkThemeConfig.FOLLOW_SYSTEM - DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT -> - DarkThemeConfig.LIGHT - DarkThemeConfigProto.DARK_THEME_CONFIG_DARK -> DarkThemeConfig.DARK + suspend fun setTopicIdFollowed(topicId: String, followed: Boolean) = withContext(dispatcher) { + val preference = settings.getUserPreference() + val newPreference = preference + .copy( + followedTopicIds = if (followed) { + preference.followedTopicIds + topicId + } else { + preference.followedTopicIds - topicId }, - useDynamicColor = it.useDynamicColor, - shouldHideOnboarding = it.shouldHideOnboarding, ) - } - - suspend fun setFollowedTopicIds(topicIds: Set) { - try { - userPreferences.updateData { - it.copy { - followedTopicIds.clear() - followedTopicIds.putAll(topicIds.associateWith { true }) - updateShouldHideOnboardingIfNecessary() - } - } - } catch (ioException: IOException) { - Log.e("NiaPreferences", "Failed to update user preferences", ioException) - } + .updateShouldHideOnboardingIfNecessary() + settings.putUserPreference(newPreference) + userData.value = newPreference } - suspend fun setTopicIdFollowed(topicId: String, followed: Boolean) { - try { - userPreferences.updateData { - it.copy { - if (followed) { - followedTopicIds.put(topicId, true) - } else { - followedTopicIds.remove(topicId) - } - updateShouldHideOnboardingIfNecessary() - } - } - } catch (ioException: IOException) { - Log.e("NiaPreferences", "Failed to update user preferences", ioException) - } + suspend fun setThemeBrand(themeBrand: ThemeBrand) = withContext(dispatcher) { + val newPreference = settings.getUserPreference() + .copy(themeBrand = themeBrand.toThemeBrandProto()) + settings.putUserPreference(newPreference) + userData.value = newPreference } - suspend fun setThemeBrand(themeBrand: ThemeBrand) { - userPreferences.updateData { - it.copy { - this.themeBrand = when (themeBrand) { - ThemeBrand.DEFAULT -> ThemeBrandProto.THEME_BRAND_DEFAULT - ThemeBrand.ANDROID -> ThemeBrandProto.THEME_BRAND_ANDROID - } - } - } + suspend fun setDynamicColorPreference(useDynamicColor: Boolean) = withContext(dispatcher) { + val newPreference = settings.getUserPreference() + .copy(useDynamicColor = useDynamicColor) + settings.putUserPreference(newPreference) + userData.value = newPreference } - suspend fun setDynamicColorPreference(useDynamicColor: Boolean) { - userPreferences.updateData { - it.copy { this.useDynamicColor = useDynamicColor } - } + suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) = withContext(dispatcher) { + val newPreference = settings.getUserPreference() + .copy(darkThemeConfig = darkThemeConfig.toDarkThemeConfigProto()) + settings.putUserPreference(newPreference) + userData.value = newPreference } - suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { - userPreferences.updateData { - it.copy { - this.darkThemeConfig = when (darkThemeConfig) { - DarkThemeConfig.FOLLOW_SYSTEM -> - DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM - DarkThemeConfig.LIGHT -> DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT - DarkThemeConfig.DARK -> DarkThemeConfigProto.DARK_THEME_CONFIG_DARK - } - } - } - } - - suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) { - try { - userPreferences.updateData { - it.copy { - if (bookmarked) { - bookmarkedNewsResourceIds.put(newsResourceId, true) + suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) = + withContext(dispatcher) { + val preference = settings.getUserPreference() + val newPreferences = preference + .copy( + bookmarkedNewsResourceIds = if (bookmarked) { + preference.bookmarkedNewsResourceIds + newsResourceId } else { - bookmarkedNewsResourceIds.remove(newsResourceId) - } - } - } - } catch (ioException: IOException) { - Log.e("NiaPreferences", "Failed to update user preferences", ioException) + preference.bookmarkedNewsResourceIds - newsResourceId + }, + ) + settings.putUserPreference(newPreferences) + userData.value = newPreferences } - } suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { setNewsResourcesViewed(listOf(newsResourceId), viewed) } - suspend fun setNewsResourcesViewed(newsResourceIds: List, viewed: Boolean) { - userPreferences.updateData { prefs -> - prefs.copy { - newsResourceIds.forEach { id -> - if (viewed) { - viewedNewsResourceIds.put(id, true) + suspend fun setNewsResourcesViewed(newsResourceIds: List, viewed: Boolean) = + withContext(dispatcher) { + val preference = settings.getUserPreference() + val newPreferences = preference + .copy( + viewedNewsResourceIds = if (viewed) { + preference.viewedNewsResourceIds + newsResourceIds } else { - viewedNewsResourceIds.remove(id) - } - } - } + preference.viewedNewsResourceIds - newsResourceIds.toSet() + }, + ) + settings.putUserPreference(newPreferences) + userData.value = newPreferences } - } - suspend fun getChangeListVersions() = userPreferences.data - .map { - ChangeListVersions( - topicVersion = it.topicChangeListVersion, - newsResourceVersion = it.newsResourceChangeListVersion, - ) - } - .firstOrNull() ?: ChangeListVersions() + suspend fun getChangeListVersions(): ChangeListVersions = withContext(dispatcher) { + val preferences = settings.getUserPreference() + return@withContext ChangeListVersions( + topicVersion = preferences.topicChangeListVersion, + newsResourceVersion = preferences.newsResourceChangeListVersion, + ) + } /** * Update the [ChangeListVersions] using [update]. */ - suspend fun updateChangeListVersion(update: ChangeListVersions.() -> ChangeListVersions) { - try { - userPreferences.updateData { currentPreferences -> - val updatedChangeListVersions = update( - ChangeListVersions( - topicVersion = currentPreferences.topicChangeListVersion, - newsResourceVersion = currentPreferences.newsResourceChangeListVersion, - ), - ) - - currentPreferences.copy { - topicChangeListVersion = updatedChangeListVersions.topicVersion - newsResourceChangeListVersion = updatedChangeListVersions.newsResourceVersion - } - } - } catch (ioException: IOException) { - Log.e("NiaPreferences", "Failed to update user preferences", ioException) + suspend fun updateChangeListVersion(update: ChangeListVersions.() -> ChangeListVersions) = + withContext(dispatcher) { + val currentPreferences = settings.getUserPreference() + val updatedChangeListVersions = update( + ChangeListVersions( + topicVersion = currentPreferences.topicChangeListVersion, + newsResourceVersion = currentPreferences.newsResourceChangeListVersion, + ), + ) + val updatedPreference = currentPreferences.copy( + topicChangeListVersion = updatedChangeListVersions.topicVersion, + newsResourceChangeListVersion = updatedChangeListVersions.newsResourceVersion, + ) + settings.putUserPreference(updatedPreference) + userData.value = updatedPreference } + + suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) = withContext(dispatcher) { + val newPreference = settings.getUserPreference() + .copy(shouldHideOnboarding = shouldHideOnboarding) + settings.putUserPreference(newPreference) + userData.value = newPreference } +} - suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) { - userPreferences.updateData { - it.copy { this.shouldHideOnboarding = shouldHideOnboarding } - } +private fun UserPreferences.updateShouldHideOnboardingIfNecessary(): UserPreferences { + return if (followedTopicIds.isEmpty() && followedAuthorIds.isEmpty()) { + this.copy(shouldHideOnboarding = false) + } else { + this + } +} + +@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class) +private fun Settings.putUserPreference(preference: UserPreferences) { + encodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + value = preference, + ) +} + +@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class) +private fun Settings.getUserPreference(): UserPreferences { + return decodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + defaultValue = UserPreferences.DEFAULT, + ) +} + +fun ThemeBrandProto.toThemeBrand(): ThemeBrand { + return when (this) { + ThemeBrandProto.THEME_BRAND_UNSPECIFIED, + ThemeBrandProto.THEME_BRAND_DEFAULT, + -> ThemeBrand.DEFAULT + + ThemeBrandProto.THEME_BRAND_ANDROID -> ThemeBrand.ANDROID + } +} + +private fun ThemeBrand.toThemeBrandProto(): ThemeBrandProto { + return when (this) { + ThemeBrand.DEFAULT -> ThemeBrandProto.THEME_BRAND_DEFAULT + ThemeBrand.ANDROID -> ThemeBrandProto.THEME_BRAND_ANDROID } } -private fun UserPreferencesKt.Dsl.updateShouldHideOnboardingIfNecessary() { - if (followedTopicIds.isEmpty() && followedAuthorIds.isEmpty()) { - shouldHideOnboarding = false +private fun DarkThemeConfig.toDarkThemeConfigProto(): DarkThemeConfigProto { + return when (this) { + DarkThemeConfig.FOLLOW_SYSTEM -> DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM + DarkThemeConfig.DARK -> DarkThemeConfigProto.DARK_THEME_CONFIG_DARK + DarkThemeConfig.LIGHT -> DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT } } diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt deleted file mode 100644 index 40c1e210f..000000000 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import androidx.datastore.core.CorruptionException -import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import java.io.InputStream -import java.io.OutputStream -import javax.inject.Inject - -/** - * An [androidx.datastore.core.Serializer] for the [UserPreferences] proto. - */ -class UserPreferencesSerializer @Inject constructor() : Serializer { - override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance() - - override suspend fun readFrom(input: InputStream): UserPreferences = - try { - // readFrom is already called on the data store background thread - UserPreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } - - override suspend fun writeTo(t: UserPreferences, output: OutputStream) { - // writeTo is already called on the data store background thread - t.writeTo(output) - } -} diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt index 86bca8793..a475d3e08 100644 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt +++ b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/SettingsComponent.kt @@ -16,12 +16,12 @@ package com.google.samples.apps.nowinandroid.core.datastore.di -import com.russhwolf.settings.ExperimentalSettingsApi -import com.russhwolf.settings.coroutines.FlowSettings +import com.russhwolf.settings.Settings +import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides -expect abstract class SettingsComponent { - @OptIn(ExperimentalSettingsApi::class) +@Component +abstract class SettingsComponent { @Provides - fun providesFlowSettings(): FlowSettings -} \ No newline at end of file + fun providesSettings(): Settings = Settings() +} diff --git a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt b/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt deleted file mode 100644 index 8b97cff34..000000000 --- a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import kotlinx.coroutines.test.runTest -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -/** - * Unit test for [IntToStringIdsMigration] - */ -class IntToStringIdsMigrationTest { - - @Test - fun IntToStringIdsMigration_should_migrate_topic_ids() = runTest { - // Set up existing preferences with topic int ids - val preMigrationUserPreferences = userPreferences { - deprecatedIntFollowedTopicIds.addAll(listOf(1, 2, 3)) - } - // Assert that there are no string topic ids yet - assertEquals( - emptyList(), - preMigrationUserPreferences.deprecatedFollowedTopicIdsList, - ) - - // Run the migration - val postMigrationUserPreferences = - IntToStringIdsMigration.migrate(preMigrationUserPreferences) - - // Assert the deprecated int topic ids have been migrated to the string topic ids - assertEquals( - userPreferences { - deprecatedFollowedTopicIds.addAll(listOf("1", "2", "3")) - hasDoneIntToStringIdMigration = true - }, - postMigrationUserPreferences, - ) - - // Assert that the migration has been marked complete - assertTrue(postMigrationUserPreferences.hasDoneIntToStringIdMigration) - } - - @Test - fun IntToStringIdsMigration_should_migrate_author_ids() = runTest { - // Set up existing preferences with author int ids - val preMigrationUserPreferences = userPreferences { - deprecatedIntFollowedAuthorIds.addAll(listOf(4, 5, 6)) - } - // Assert that there are no string author ids yet - assertEquals( - emptyList(), - preMigrationUserPreferences.deprecatedFollowedAuthorIdsList, - ) - - // Run the migration - val postMigrationUserPreferences = - IntToStringIdsMigration.migrate(preMigrationUserPreferences) - - // Assert the deprecated int author ids have been migrated to the string author ids - assertEquals( - userPreferences { - deprecatedFollowedAuthorIds.addAll(listOf("4", "5", "6")) - hasDoneIntToStringIdMigration = true - }, - postMigrationUserPreferences, - ) - - // Assert that the migration has been marked complete - assertTrue(postMigrationUserPreferences.hasDoneIntToStringIdMigration) - } -} diff --git a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt b/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt deleted file mode 100644 index f7e083b45..000000000 --- a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import kotlinx.coroutines.test.runTest -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class ListToMapMigrationTest { - - @Test - fun ListToMapMigration_should_migrate_topic_ids() = runTest { - // Set up existing preferences with topic ids - val preMigrationUserPreferences = userPreferences { - deprecatedFollowedTopicIds.addAll(listOf("1", "2", "3")) - } - // Assert that there are no topic ids in the map yet - assertEquals( - emptyMap(), - preMigrationUserPreferences.followedTopicIdsMap, - ) - - // Run the migration - val postMigrationUserPreferences = - ListToMapMigration.migrate(preMigrationUserPreferences) - - // Assert the deprecated topic ids have been migrated to the topic ids map - assertEquals( - mapOf("1" to true, "2" to true, "3" to true), - postMigrationUserPreferences.followedTopicIdsMap, - ) - - // Assert that the migration has been marked complete - assertTrue(postMigrationUserPreferences.hasDoneListToMapMigration) - } - - @Test - fun ListToMapMigration_should_migrate_author_ids() = runTest { - // Set up existing preferences with author ids - val preMigrationUserPreferences = userPreferences { - deprecatedFollowedAuthorIds.addAll(listOf("4", "5", "6")) - } - // Assert that there are no author ids in the map yet - assertEquals( - emptyMap(), - preMigrationUserPreferences.followedAuthorIdsMap, - ) - - // Run the migration - val postMigrationUserPreferences = - ListToMapMigration.migrate(preMigrationUserPreferences) - - // Assert the deprecated author ids have been migrated to the author ids map - assertEquals( - mapOf("4" to true, "5" to true, "6" to true), - postMigrationUserPreferences.followedAuthorIdsMap, - ) - - // Assert that the migration has been marked complete - assertTrue(postMigrationUserPreferences.hasDoneListToMapMigration) - } - - @Test - fun ListToMapMigration_should_migrate_bookmarks() = runTest { - // Set up existing preferences with bookmarks - val preMigrationUserPreferences = userPreferences { - deprecatedBookmarkedNewsResourceIds.addAll(listOf("7", "8", "9")) - } - // Assert that there are no bookmarks in the map yet - assertEquals( - emptyMap(), - preMigrationUserPreferences.bookmarkedNewsResourceIdsMap, - ) - - // Run the migration - val postMigrationUserPreferences = - ListToMapMigration.migrate(preMigrationUserPreferences) - - // Assert the deprecated bookmarks have been migrated to the bookmarks map - assertEquals( - mapOf("7" to true, "8" to true, "9" to true), - postMigrationUserPreferences.bookmarkedNewsResourceIdsMap, - ) - - // Assert that the migration has been marked complete - assertTrue(postMigrationUserPreferences.hasDoneListToMapMigration) - } -} diff --git a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt b/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt index 433812808..2ed30784f 100644 --- a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt +++ b/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt @@ -16,31 +16,30 @@ package com.google.samples.apps.nowinandroid.core.datastore -import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore +import com.russhwolf.settings.MapSettings +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue class NiaPreferencesDataSourceTest { + @OptIn(ExperimentalCoroutinesApi::class) private val testScope = TestScope(UnconfinedTestDispatcher()) private lateinit var subject: NiaPreferencesDataSource - @get:Rule - val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() - - @Before + @BeforeTest fun setup() { subject = NiaPreferencesDataSource( - tmpFolder.testUserPreferencesDataStore(testScope), + settings = MapSettings(), + dispatcher = Dispatchers.Unconfined, ) } @@ -84,7 +83,8 @@ class NiaPreferencesDataSourceTest { } @Test - fun shouldUseDynamicColorFalseByDefault() = testScope.runTest { + fun shouldUseDynamicColorFalseWhenSet() = testScope.runTest { + subject.setDynamicColorPreference(false) assertFalse(subject.userData.first().useDynamicColor) } diff --git a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt b/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt deleted file mode 100644 index ad7664fe5..000000000 --- a/core/datastore/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.datastore - -import androidx.datastore.core.CorruptionException -import kotlinx.coroutines.test.runTest -import org.junit.Test -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import kotlin.test.assertEquals - -class UserPreferencesSerializerTest { - private val userPreferencesSerializer = UserPreferencesSerializer() - - @Test - fun defaultUserPreferences_isEmpty() { - assertEquals( - userPreferences { - // Default value - }, - userPreferencesSerializer.defaultValue, - ) - } - - @Test - fun writingAndReadingUserPreferences_outputsCorrectValue() = runTest { - val expectedUserPreferences = userPreferences { - followedTopicIds.put("0", true) - followedTopicIds.put("1", true) - } - - val outputStream = ByteArrayOutputStream() - - expectedUserPreferences.writeTo(outputStream) - - val inputStream = ByteArrayInputStream(outputStream.toByteArray()) - - val actualUserPreferences = userPreferencesSerializer.readFrom(inputStream) - - assertEquals( - expectedUserPreferences, - actualUserPreferences, - ) - } - - @Test(expected = CorruptionException::class) - fun readingInvalidUserPreferences_throwsCorruptionException() = runTest { - userPreferencesSerializer.readFrom(ByteArrayInputStream(byteArrayOf(0))) - } -} diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt index d490ff13e..795c88d72 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt @@ -285,16 +285,6 @@ fun NiaOutlinedButtonPreview() { } } -@ThemePreviews -@Composable -fun NiaButtonPreview2() { - NiaTheme { - NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) { - NiaButton(onClick = {}, text = { Text("Test button") }) - } - } -} - @ThemePreviews @Composable fun NiaButtonLeadingIconPreview() { diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt index 106f0b839..9497bd92d 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt @@ -73,6 +73,8 @@ fun NiaFilterChip( }, shape = CircleShape, border = FilterChipDefaults.filterChipBorder( + enabled = enabled, + selected = selected, borderColor = MaterialTheme.colorScheme.onBackground, selectedBorderColor = MaterialTheme.colorScheme.onBackground, disabledBorderColor = MaterialTheme.colorScheme.onBackground.copy( diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt index 21dfbc8c3..ca168b4be 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt @@ -96,8 +96,8 @@ fun NiaLoadingWheel( animationSpec = infiniteRepeatable( animation = keyframes { durationMillis = ROTATION_TIME / 2 - progressLineColor at ROTATION_TIME / NUM_OF_LINES / 2 with LinearEasing - baseLineColor at ROTATION_TIME / NUM_OF_LINES with LinearEasing + progressLineColor at ROTATION_TIME / NUM_OF_LINES / 2 using LinearEasing + baseLineColor at ROTATION_TIME / NUM_OF_LINES using LinearEasing }, repeatMode = RepeatMode.Restart, initialStartOffset = StartOffset(ROTATION_TIME / NUM_OF_LINES / 2 * index), diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt index f1db03f66..9f8f71f14 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt @@ -167,7 +167,7 @@ fun NiaNavigationRail( @ThemePreviews @Composable -fun NiaNavigationPreview() { +fun NiaNavigationBarPreview() { val items = listOf("For you", "Saved", "Interests") val icons = listOf( NiaIcons.UpcomingBorder, @@ -205,6 +205,46 @@ fun NiaNavigationPreview() { } } +@ThemePreviews +@Composable +fun NiaNavigationRailPreview() { + val items = listOf("For you", "Saved", "Interests") + val icons = listOf( + NiaIcons.UpcomingBorder, + NiaIcons.BookmarksBorder, + NiaIcons.Grid3x3, + ) + val selectedIcons = listOf( + NiaIcons.Upcoming, + NiaIcons.Bookmarks, + NiaIcons.Grid3x3, + ) + + NiaTheme { + NiaNavigationRail { + items.forEachIndexed { index, item -> + NiaNavigationRailItem( + icon = { + Icon( + imageVector = icons[index], + contentDescription = item, + ) + }, + selectedIcon = { + Icon( + imageVector = selectedIcons[index], + contentDescription = item, + ) + }, + label = { Text(item) }, + selected = index == 0, + onClick = { }, + ) + } + } + } +} + /** * Now in Android navigation default values. */ diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt index 92cd9aa8f..74753ca9b 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt @@ -91,7 +91,7 @@ fun NiaTabRow( containerColor = Color.Transparent, contentColor = MaterialTheme.colorScheme.onSurface, indicator = { tabPositions -> - TabRowDefaults.Indicator( + TabRowDefaults.SecondaryIndicator( modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]), height = 2.dp, color = MaterialTheme.colorScheme.onSurface, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt index 9c716918a..f85c65677 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -77,11 +78,13 @@ fun NiaTopAppBar( @Preview("Top App Bar") @Composable private fun NiaTopAppBarPreview() { - NiaTopAppBar( - titleRes = android.R.string.untitled, - navigationIcon = NiaIcons.Search, - navigationIconContentDescription = "Navigation icon", - actionIcon = NiaIcons.MoreVert, - actionIconContentDescription = "Action icon", - ) + NiaTheme { + NiaTopAppBar( + titleRes = android.R.string.untitled, + navigationIcon = NiaIcons.Search, + navigationIconContentDescription = "Navigation icon", + actionIcon = NiaIcons.MoreVert, + actionIconContentDescription = "Action icon", + ) + } } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt index 8db20689f..6b77f7394 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt @@ -17,6 +17,8 @@ package com.google.samples.apps.nowinandroid.core.designsystem.icon import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material.icons.automirrored.rounded.ShortText import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.Bookmarks import androidx.compose.material.icons.outlined.Upcoming @@ -41,7 +43,7 @@ import androidx.compose.ui.graphics.vector.ImageVector */ object NiaIcons { val Add = Icons.Rounded.Add - val ArrowBack = Icons.Rounded.ArrowBack + val ArrowBack = Icons.AutoMirrored.Rounded.ArrowBack val Bookmark = Icons.Rounded.Bookmark val BookmarkBorder = Icons.Rounded.BookmarkBorder val Bookmarks = Icons.Rounded.Bookmarks @@ -53,7 +55,7 @@ object NiaIcons { val Person = Icons.Rounded.Person val Search = Icons.Rounded.Search val Settings = Icons.Rounded.Settings - val ShortText = Icons.Rounded.ShortText + val ShortText = Icons.AutoMirrored.Rounded.ShortText val Upcoming = Icons.Rounded.Upcoming val UpcomingBorder = Icons.Outlined.Upcoming val ViewDay = Icons.Rounded.ViewDay diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt index 0d3b06457..82d769863 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt @@ -19,6 +19,9 @@ package com.google.samples.apps.nowinandroid.core.designsystem.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.LineHeightStyle +import androidx.compose.ui.text.style.LineHeightStyle.Alignment +import androidx.compose.ui.text.style.LineHeightStyle.Trim import androidx.compose.ui.unit.sp /** @@ -60,12 +63,20 @@ internal val NiaTypography = Typography( fontSize = 24.sp, lineHeight = 32.sp, letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Bottom, + trim = Trim.None, + ), ), titleLarge = TextStyle( fontWeight = FontWeight.Bold, fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Bottom, + trim = Trim.LastLineBottom, + ), ), titleMedium = TextStyle( fontWeight = FontWeight.Bold, @@ -79,11 +90,16 @@ internal val NiaTypography = Typography( lineHeight = 20.sp, letterSpacing = 0.1.sp, ), + // Default text style bodyLarge = TextStyle( fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp, letterSpacing = 0.5.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.None, + ), ), bodyMedium = TextStyle( fontWeight = FontWeight.Normal, @@ -97,22 +113,37 @@ internal val NiaTypography = Typography( lineHeight = 16.sp, letterSpacing = 0.4.sp, ), + // Used for Button labelLarge = TextStyle( fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp, letterSpacing = 0.1.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), ), + // Used for Navigation items labelMedium = TextStyle( fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.5.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), ), + // Used for Tag labelSmall = TextStyle( fontWeight = FontWeight.Medium, fontSize = 10.sp, - lineHeight = 16.sp, + lineHeight = 14.sp, letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), ), ) diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png index c4a4d7440..67cafa03d 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png index 4eb46a8e6..3f187d9d2 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png index d2914c451..ebcf62c08 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png index 9b4d62d86..7f910a34b 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png index 22eaf5833..912480c6a 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png index 35449ae14..a9b2c8694 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png index ce588b0ee..f88a672c4 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png index d2914c451..ebcf62c08 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png index 84a3dcaa7..6fef6436a 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png index cc8ccd997..e619f1332 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png index 9ddd58d4b..cf0656fbd 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png index c867bf469..7774a18bc 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png index 386760d24..01538b44b 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png index d817e5c3f..fdbbb820d 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png index 4a8fe7a98..6fce27976 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png index 4dce1bcab..c18a86878 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png index d73f023c8..4b5c91914 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png index e363c8dad..865368ca1 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png index 7c0371ddc..8f90977fd 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png index 56ddf9792..2dc430ca8 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png index c73df7f89..2f3749cf3 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png index 5e732c373..fe4b54ae2 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png index b7eb3db4a..92079273a 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png index 7109265c7..013aac763 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png index e42f9855c..36d79ab6c 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png index 41dbc1aea..0133bc71a 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png index 1541e6133..142051d68 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png index a698536d8..4d4b10caa 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png index 01183397f..589628199 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png index 92317be34..bfa5a8367 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png index a432cc93c..6e951e42f 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png index 238b60c25..3c1ab3d40 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png index ff60eb547..391de3204 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png index badd8e1c7..6a342f7bd 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png index dbfb08f1f..15cb061a0 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png index 9a60923d9..f62ea3ced 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png index a11d82680..0564b3881 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png index 2d819edf7..13345c365 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png b/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png index fc66ddf58..475707556 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png and b/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png index 5683b4c6b..00144ba15 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png index 0e53746d9..1c2d9b3ec 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png index ed2f04eb1..234304db1 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png index 00e313d2b..fbf61adc4 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png differ diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index 9eca6b141..0500bb455 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -121,7 +121,7 @@ fun NewsResourceCardExpanded( Spacer(modifier = Modifier.weight(1f)) BookmarkButton(isBookmarked, onToggleBookmark) } - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(14.dp)) Row(verticalAlignment = Alignment.CenterVertically) { if (!hasBeenViewed) { NotificationDot( @@ -132,7 +132,7 @@ fun NewsResourceCardExpanded( } NewsResourceMetaData(userNewsResource.publishDate, userNewsResource.type) } - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(14.dp)) NewsResourceShortDescription(userNewsResource.content) Spacer(modifier = Modifier.height(12.dp)) NewsResourceTopics( diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index e1418d747..885020636 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -508,23 +507,21 @@ fun ForYouScreenPopulatedFeed( @PreviewParameter(UserNewsResourcePreviewParameterProvider::class) userNewsResources: List, ) { - BoxWithConstraints { - NiaTheme { - ForYouScreen( - isSyncing = false, - onboardingUiState = OnboardingUiState.NotShown, - feedState = NewsFeedUiState.Success( - feed = userNewsResources, - ), - deepLinkedUserNewsResource = null, - onTopicCheckedChanged = { _, _ -> }, - saveFollowedTopics = {}, - onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourceViewed = {}, - onTopicClick = {}, - onDeepLinkOpened = {}, - ) - } + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.NotShown, + feedState = NewsFeedUiState.Success( + feed = userNewsResources, + ), + deepLinkedUserNewsResource = null, + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + onDeepLinkOpened = {}, + ) } } @@ -534,23 +531,21 @@ fun ForYouScreenOfflinePopulatedFeed( @PreviewParameter(UserNewsResourcePreviewParameterProvider::class) userNewsResources: List, ) { - BoxWithConstraints { - NiaTheme { - ForYouScreen( - isSyncing = false, - onboardingUiState = OnboardingUiState.NotShown, - feedState = NewsFeedUiState.Success( - feed = userNewsResources, - ), - deepLinkedUserNewsResource = null, - onTopicCheckedChanged = { _, _ -> }, - saveFollowedTopics = {}, - onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourceViewed = {}, - onTopicClick = {}, - onDeepLinkOpened = {}, - ) - } + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.NotShown, + feedState = NewsFeedUiState.Success( + feed = userNewsResources, + ), + deepLinkedUserNewsResource = null, + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + onDeepLinkOpened = {}, + ) } } @@ -560,47 +555,43 @@ fun ForYouScreenTopicSelection( @PreviewParameter(UserNewsResourcePreviewParameterProvider::class) userNewsResources: List, ) { - BoxWithConstraints { - NiaTheme { - ForYouScreen( - isSyncing = false, - onboardingUiState = OnboardingUiState.Shown( - topics = userNewsResources.flatMap { news -> news.followableTopics } - .distinctBy { it.topic.id }, - ), - feedState = NewsFeedUiState.Success( - feed = userNewsResources, - ), - deepLinkedUserNewsResource = null, - onTopicCheckedChanged = { _, _ -> }, - saveFollowedTopics = {}, - onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourceViewed = {}, - onTopicClick = {}, - onDeepLinkOpened = {}, - ) - } + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.Shown( + topics = userNewsResources.flatMap { news -> news.followableTopics } + .distinctBy { it.topic.id }, + ), + feedState = NewsFeedUiState.Success( + feed = userNewsResources, + ), + deepLinkedUserNewsResource = null, + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + onDeepLinkOpened = {}, + ) } } @DevicePreviews @Composable fun ForYouScreenLoading() { - BoxWithConstraints { - NiaTheme { - ForYouScreen( - isSyncing = false, - onboardingUiState = OnboardingUiState.Loading, - feedState = NewsFeedUiState.Loading, - deepLinkedUserNewsResource = null, - onTopicCheckedChanged = { _, _ -> }, - saveFollowedTopics = {}, - onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourceViewed = {}, - onTopicClick = {}, - onDeepLinkOpened = {}, - ) - } + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.Loading, + feedState = NewsFeedUiState.Loading, + deepLinkedUserNewsResource = null, + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + onDeepLinkOpened = {}, + ) } } @@ -610,22 +601,20 @@ fun ForYouScreenPopulatedAndLoading( @PreviewParameter(UserNewsResourcePreviewParameterProvider::class) userNewsResources: List, ) { - BoxWithConstraints { - NiaTheme { - ForYouScreen( - isSyncing = true, - onboardingUiState = OnboardingUiState.Loading, - feedState = NewsFeedUiState.Success( - feed = userNewsResources, - ), - deepLinkedUserNewsResource = null, - onTopicCheckedChanged = { _, _ -> }, - saveFollowedTopics = {}, - onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourceViewed = {}, - onTopicClick = {}, - onDeepLinkOpened = {}, - ) - } + NiaTheme { + ForYouScreen( + isSyncing = true, + onboardingUiState = OnboardingUiState.Loading, + feedState = NewsFeedUiState.Success( + feed = userNewsResources, + ), + deepLinkedUserNewsResource = null, + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + onDeepLinkOpened = {}, + ) } } diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png index 7d01a13eb..1972b1ca2 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png index e03dd3450..16df589f9 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png index a0e2c9e10..d28704e49 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png index 2c294af94..c2a01f2d8 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png index d8a99e736..0b539aeca 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png index 708cd5107..b19c8d708 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png index 098a7801d..bdf44b2a3 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png index 3f2a9e6ff..b095c1a7a 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png index 0bbe04955..140fa8d6d 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png index 1ba8943ef..5d90732a0 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png index 58d898dcf..3dd62e765 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png differ diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index 032515c53..db60a6447 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -37,7 +37,7 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Text @@ -115,7 +115,7 @@ fun SettingsDialog( ) }, text = { - Divider() + HorizontalDivider() Column(Modifier.verticalScroll(rememberScrollState())) { when (settingsUiState) { Loading -> { @@ -135,7 +135,7 @@ fun SettingsDialog( ) } } - Divider(Modifier.padding(top = 8.dp)) + HorizontalDivider(Modifier.padding(top = 8.dp)) LinksPanel() } TrackScreenViewEvent(screenName = "Settings") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cc814e0bf..7ec074eab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ androidTools = "31.2.2" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" -androidxComposeBom = "2023.10.01" +androidxComposeBom = "2024.02.00" androidxComposeCompiler = "1.5.7" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" @@ -136,6 +136,7 @@ kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } +kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinxSerializationJson" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "androidTools" } lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "androidTools" } @@ -170,8 +171,8 @@ sqldelight-coroutines-extensions = { group = "app.cash.sqldelight", name = "coro sqldelight-primitive-adapters = { group = "app.cash.sqldelight", name = "primitive-adapters", version.ref = "sqldelight" } kotlin-inject-compiler-ksp = { group = "me.tatarka.inject", name = "kotlin-inject-compiler-ksp", version.ref = "kotlinInject" } kotlin-inject-runtime = { group = "me.tatarka.inject", name = "kotlin-inject-runtime", version.ref = "kotlinInject" } -multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings", version.ref = "multiplatform-settings" } -multiplatform-settings-datastore = { group = "com.russhwolf", name = "multiplatform-settings-datastore", version.ref = "multiplatform-settings" } +multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" } +multiplatform-settings-test = { group = "com.russhwolf", name = "multiplatform-settings-test", version.ref = "multiplatform-settings" } multiplatform-settings-serialization = { group = "com.russhwolf", name = "multiplatform-settings-serialization", version.ref = "multiplatform-settings" } multiplatform-settings-coroutines = { group = "com.russhwolf", name = "multiplatform-settings-coroutines", version.ref = "multiplatform-settings" }