From 44cd995d3d82511472edff18e54e57081f9d87a7 Mon Sep 17 00:00:00 2001 From: Arash Ebrahimzade <68777571+aarash709@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:21:26 +0330 Subject: [PATCH 01/37] Updated screenshot tests link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f13f5de2..be1270b16 100644 --- a/README.md +++ b/README.md @@ -122,9 +122,9 @@ tests against _all_ build variants which is both unecessary and will result in f A screenshot test takes a screenshot of a screen or a UI component within the app, and compares it with a previously recorded screenshot which is known to be rendered correctly. -For example, Now in Android has [screenshot tests](https://github.com/android/nowinandroid/blob/main/app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt) +For example, Now in Android has [screenshot tests](https://github.com/android/nowinandroid/blob/main/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt) to verify that the navigation is displayed correctly on different screen sizes -([known correct screenshots](https://github.com/android/nowinandroid/tree/main/app/src/testDemoDebug/screenshots)). +([known correct screenshots](https://github.com/android/nowinandroid/tree/main/app/src/testDemo/screenshots)). Now In Android uses [Roborazzi](https://github.com/takahirom/roborazzi) to run screenshot tests of certain screens and UI components. When working with screenshot tests the following gradle tasks are useful: From d932cde11049e4f457c1db33382f81c96dfd2efc Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 27 Jan 2024 10:05:21 +0000 Subject: [PATCH 02/37] Move `androidx.compose.ui:ui-tooling` dependency to convention plugin --- .../com/google/samples/apps/nowinandroid/AndroidCompose.kt | 1 + core/designsystem/build.gradle.kts | 3 --- gradle/libs.versions.toml | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt index 039987cac..6e08c2658 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt @@ -41,6 +41,7 @@ internal fun Project.configureAndroidCompose( val bom = libs.findLibrary("androidx-compose-bom").get() add("implementation", platform(bom)) add("androidTestImplementation", platform(bom)) + add("debugImplementation", libs.findLibrary("androidx-compose-ui-tooling").get()) } testOptions { diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index d68117d06..75e59f255 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -35,11 +35,8 @@ dependencies { api(libs.androidx.compose.material.iconsExtended) api(libs.androidx.compose.material3) api(libs.androidx.compose.runtime) - api(libs.androidx.compose.ui.tooling.preview) api(libs.androidx.compose.ui.util) - debugApi(libs.androidx.compose.ui.tooling) - implementation(libs.coil.kt.compose) testImplementation(libs.androidx.compose.ui.test) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..ab11f9279 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -75,7 +75,6 @@ androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } From db4bf1c78286428892e9aff2701f8b4dd37c1036 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 27 Jan 2024 10:25:24 +0000 Subject: [PATCH 03/37] Keep `androidx.compose.ui:ui-tooling-preview` on all buildTypes --- .../com/google/samples/apps/nowinandroid/AndroidCompose.kt | 1 + gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt index 6e08c2658..a4b3d0501 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt @@ -41,6 +41,7 @@ internal fun Project.configureAndroidCompose( val bom = libs.findLibrary("androidx-compose-bom").get() add("implementation", platform(bom)) add("androidTestImplementation", platform(bom)) + add("implementation", libs.findLibrary("androidx-compose-ui-tooling-preview").get()) add("debugImplementation", libs.findLibrary("androidx-compose-ui-tooling").get()) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab11f9279..795510bce 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -75,6 +75,7 @@ androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } From 20c8db6b0c1a2f9bd71bce5f22aecf82ee93cce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:18:10 +0000 Subject: [PATCH 04/37] Bump gradle/gradle-build-action from 2 to 3 Bumps [gradle/gradle-build-action](https://github.com/gradle/gradle-build-action) from 2 to 3. - [Release notes](https://github.com/gradle/gradle-build-action/releases) - [Commits](https://github.com/gradle/gradle-build-action/compare/v2...v3) --- updated-dependencies: - dependency-name: gradle/gradle-build-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/Build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 9bd84abc0..76b85a5a2 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -37,7 +37,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 - name: Check build-logic run: ./gradlew check -p build-logic @@ -144,7 +144,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 - name: Build projects before running emulator run: ./gradlew packageDemoDebug packageDemoDebugAndroidTest From 395b9853df15de34ad76d80e054221583e2a8376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Mon, 29 Jan 2024 15:59:12 +0100 Subject: [PATCH 05/37] Add Composition Tracing Change-Id: I3ad60930de2fab71a700a0c2df3f0a1f566cafd8 --- app/build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 38166f7ff..aded864ee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -104,6 +104,7 @@ dependencies { implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.tracing.ktx) implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.androidx.compose.runtime.tracing) implementation(libs.androidx.compose.material3.windowSizeClass) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.profileinstaller) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..1ef4cc225 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,6 +72,7 @@ androidx-compose-material-iconsExtended = { group = "androidx.compose.material", androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" } androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } +androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } From cb00d2c8cbd9c783f220bc99ad1a7e3d72e24007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Mon, 29 Jan 2024 22:58:47 +0100 Subject: [PATCH 06/37] Add TimeZoneMonitor to prevent multiple TimeZoneBroadcastReceivers This way, we can save ~1ms per composed item on screen. Change-Id: Ib9ada3cea53304fca4fb2b36c48c175845bc683d --- .../apps/nowinandroid/ui/NavigationUiTest.kt | 86 +++++------------ .../apps/nowinandroid/ui/NiaAppStateTest.kt | 31 ++++++ .../samples/apps/nowinandroid/MainActivity.kt | 27 ++++-- .../samples/apps/nowinandroid/ui/NiaApp.kt | 31 +++--- .../apps/nowinandroid/ui/NiaAppState.kt | 28 ++++-- .../ui/NiaAppScreenSizesScreenshotTests.kt | 8 +- .../foryou/ScrollForYouFeedBenchmark.kt | 4 + .../apps/nowinandroid/AndroidCompose.kt | 10 +- compose_compiler_config.conf | 6 ++ .../data/test/DefaultZoneIdTimeZoneMonitor.kt | 27 ++++++ .../core/data/test/TestDataModule.kt | 4 + .../nowinandroid/core/data/di/DataModule.kt | 5 + .../core/data/util/TimeZoneMonitor.kt | 94 +++++++++++++++++++ .../core/testing/util/TestTimeZoneMonitor.kt | 41 ++++++++ .../nowinandroid/core/ui/LocalTimeZone.kt | 26 +++++ .../nowinandroid/core/ui/NewsResourceCard.kt | 29 +----- .../core/ui/TimeZoneBroadcastReceiver.kt | 50 ---------- gradle/libs.versions.toml | 6 +- 18 files changed, 337 insertions(+), 176 deletions(-) create mode 100644 compose_compiler_config.conf create mode 100644 core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt create mode 100644 core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt create mode 100644 core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt create mode 100644 core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt delete mode 100644 core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/TimeZoneBroadcastReceiver.kt diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt index d92390918..5d2e12b5c 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt @@ -19,14 +19,17 @@ package com.google.samples.apps.nowinandroid.ui import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.google.accompanist.testharness.TestHarness import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository @@ -81,6 +84,9 @@ class NavigationUiTest { @Inject lateinit var networkMonitor: NetworkMonitor + @Inject + lateinit var timeZoneMonitor: TimeZoneMonitor + @Before fun setup() { hiltRule.inject() @@ -91,13 +97,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(400.dp, 400.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -111,13 +111,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(610.dp, 400.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -131,13 +125,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(900.dp, 400.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -151,13 +139,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(400.dp, 500.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -171,13 +153,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(610.dp, 500.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -191,13 +167,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(900.dp, 500.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -211,13 +181,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(400.dp, 1000.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -231,13 +195,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(610.dp, 1000.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -251,13 +209,7 @@ class NavigationUiTest { composeTestRule.setContent { TestHarness(size = DpSize(900.dp, 1000.dp)) { BoxWithConstraints { - NiaApp( - windowSizeClass = WindowSizeClass.calculateFromSize( - DpSize(maxWidth, maxHeight), - ), - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(fakeAppState(maxWidth, maxHeight)) } } } @@ -265,4 +217,12 @@ class NavigationUiTest { composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed() composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist() } + + @Composable + private fun fakeAppState(maxWidth: Dp, maxHeight: Dp) = rememberNiaAppState( + windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, + ) } diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt index 1560a74eb..18afc6a09 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt @@ -34,12 +34,14 @@ import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNe import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor +import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import java.time.ZoneId import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -59,6 +61,8 @@ class NiaAppStateTest { // Create the test dependencies. private val networkMonitor = TestNetworkMonitor() + private val timeZoneMonitor = TestTimeZoneMonitor() + private val userNewsResourceRepository = CompositeUserNewsResourceRepository(TestNewsRepository(), TestUserDataRepository()) @@ -78,6 +82,7 @@ class NiaAppStateTest { windowSizeClass = getCompactWindowClass(), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -100,6 +105,7 @@ class NiaAppStateTest { windowSizeClass = getCompactWindowClass(), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -118,6 +124,7 @@ class NiaAppStateTest { windowSizeClass = getCompactWindowClass(), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -134,6 +141,7 @@ class NiaAppStateTest { windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(800.dp, 800.dp)), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -150,6 +158,7 @@ class NiaAppStateTest { windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -166,6 +175,7 @@ class NiaAppStateTest { windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } @@ -177,6 +187,27 @@ class NiaAppStateTest { ) } + @Test + fun niaAppState_differentTZ_withTimeZoneMonitorChange() = runTest(UnconfinedTestDispatcher()) { + composeTestRule.setContent { + state = NiaAppState( + navController = NavHostController(LocalContext.current), + coroutineScope = backgroundScope, + windowSizeClass = getCompactWindowClass(), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, + ) + } + val changedTz = ZoneId.of("Europe/Prague") + backgroundScope.launch { state.currentTimeZone.collect() } + timeZoneMonitor.setTimeZone(changedTz) + assertEquals( + changedTz, + state.currentTimeZone.value, + ) + } + private fun getCompactWindowClass() = WindowSizeClass.calculateFromSize(DpSize(500.dp, 300.dp)) } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 6ce134ef4..779b6edbe 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -28,6 +28,7 @@ import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -42,10 +43,13 @@ import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper 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.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme 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.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.ui.NiaApp +import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach @@ -67,6 +71,9 @@ class MainActivity : ComponentActivity() { @Inject lateinit var networkMonitor: NetworkMonitor + @Inject + lateinit var timeZoneMonitor: TimeZoneMonitor + @Inject lateinit var analyticsHelper: AnalyticsHelper @@ -126,17 +133,25 @@ class MainActivity : ComponentActivity() { onDispose {} } - CompositionLocalProvider(LocalAnalyticsHelper provides analyticsHelper) { + val appState = rememberNiaAppState( + windowSizeClass = calculateWindowSizeClass(this), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, + ) + + val currentTimeZone by appState.currentTimeZone.collectAsState() + + CompositionLocalProvider( + LocalAnalyticsHelper provides analyticsHelper, + LocalTimeZone provides currentTimeZone, + ) { NiaTheme( darkTheme = darkTheme, androidTheme = shouldUseAndroidTheme(uiState), disableDynamicTheming = shouldDisableDynamicTheming(uiState), ) { - NiaApp( - networkMonitor = networkMonitor, - windowSizeClass = calculateWindowSizeClass(this), - userNewsResourceRepository = userNewsResourceRepository, - ) + NiaApp(appState) } } } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt index 2beda99ea..b2eabe2ed 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt @@ -39,7 +39,6 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult.ActionPerformed import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -62,8 +61,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy import com.google.samples.apps.nowinandroid.R -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.component.NiaBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar @@ -85,16 +82,7 @@ import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR ExperimentalComposeUiApi::class, ) @Composable -fun NiaApp( - windowSizeClass: WindowSizeClass, - networkMonitor: NetworkMonitor, - userNewsResourceRepository: UserNewsResourceRepository, - appState: NiaAppState = rememberNiaAppState( - networkMonitor = networkMonitor, - windowSizeClass = windowSizeClass, - userNewsResourceRepository = userNewsResourceRepository, - ), -) { +fun NiaApp(appState: NiaAppState) { val shouldShowGradientBackground = appState.currentTopLevelDestination == TopLevelDestination.FOR_YOU var showSettingsDialog by rememberSaveable { mutableStateOf(false) } @@ -195,13 +183,16 @@ fun NiaApp( ) } - NiaNavHost(appState = appState, onShowSnackbar = { message, action -> - snackbarHostState.showSnackbar( - message = message, - actionLabel = action, - duration = Short, - ) == ActionPerformed - }) + NiaNavHost( + appState = appState, + onShowSnackbar = { message, action -> + snackbarHostState.showSnackbar( + message = message, + actionLabel = action, + duration = Short, + ) == ActionPerformed + }, + ) } // TODO: We may want to add padding or spacer when the snackbar is shown so that diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index 7b66efb06..ffd0c16ec 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -32,6 +32,7 @@ import androidx.navigation.navOptions import androidx.tracing.trace 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.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.ui.TrackDisposableJank import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.BOOKMARKS_ROUTE import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.navigateToBookmarks @@ -50,12 +51,14 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import java.time.ZoneId @Composable fun rememberNiaAppState( windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, userNewsResourceRepository: UserNewsResourceRepository, + timeZoneMonitor: TimeZoneMonitor, coroutineScope: CoroutineScope = rememberCoroutineScope(), navController: NavHostController = rememberNavController(), ): NiaAppState { @@ -66,13 +69,15 @@ fun rememberNiaAppState( windowSizeClass, networkMonitor, userNewsResourceRepository, + timeZoneMonitor, ) { NiaAppState( - navController, - coroutineScope, - windowSizeClass, - networkMonitor, - userNewsResourceRepository, + navController = navController, + coroutineScope = coroutineScope, + windowSizeClass = windowSizeClass, + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) } } @@ -80,10 +85,11 @@ fun rememberNiaAppState( @Stable class NiaAppState( val navController: NavHostController, - val coroutineScope: CoroutineScope, + coroutineScope: CoroutineScope, val windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, userNewsResourceRepository: UserNewsResourceRepository, + private val timeZoneMonitor: TimeZoneMonitor, ) { val currentDestination: NavDestination? @Composable get() = navController @@ -127,12 +133,20 @@ class NiaAppState( FOR_YOU.takeIf { forYouNewsResources.any { !it.hasBeenViewed } }, BOOKMARKS.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } }, ) - }.stateIn( + } + .stateIn( coroutineScope, SharingStarted.WhileSubscribed(5_000), initialValue = emptySet(), ) + val currentTimeZone = timeZoneMonitor.currentZoneId + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(5_000), + ZoneId.systemDefault(), + ) + /** * UI logic for navigating to a top level destination in the app. Top level destinations have * only one copy of the destination of the back stack, and save and restore state whenever you 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..f3514b02f 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.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.BindValue @@ -93,6 +94,9 @@ class NiaAppScreenSizesScreenshotTests { @Inject lateinit var networkMonitor: NetworkMonitor + @Inject + lateinit var timeZoneMonitor: TimeZoneMonitor + @Inject lateinit var userDataRepository: UserDataRepository @@ -140,13 +144,15 @@ class NiaAppScreenSizesScreenshotTests { ) { TestHarness(size = DpSize(width, height)) { BoxWithConstraints { - NiaApp( + val fakeAppState = rememberNiaAppState( windowSizeClass = WindowSizeClass.calculateFromSize( DpSize(maxWidth, maxHeight), ), networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, ) + NiaApp(fakeAppState) } } } diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt index 6d0091cd4..24a50dd79 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt @@ -38,6 +38,9 @@ class ScrollForYouFeedBenchmark { @Test fun scrollFeedCompilationBaselineProfile() = scrollFeed(CompilationMode.Partial()) + @Test + fun scrollFeedCompilationFull() = scrollFeed(CompilationMode.Full()) + private fun scrollFeed(compilationMode: CompilationMode) = benchmarkRule.measureRepeated( packageName = PACKAGE_NAME, metrics = listOf(FrameTimingMetric()), @@ -55,3 +58,4 @@ class ScrollForYouFeedBenchmark { forYouScrollFeedDownUp() } } + diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt index 039987cac..72d37db1b 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt @@ -53,7 +53,8 @@ internal fun Project.configureAndroidCompose( tasks.withType().configureEach { kotlinOptions { - freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters() + freeCompilerArgs += buildComposeMetricsParameters() + freeCompilerArgs += stabilityConfiguration() } } } @@ -68,7 +69,7 @@ private fun Project.buildComposeMetricsParameters(): List { val metricsFolder = buildDir.resolve("compose-metrics").resolve(relativePath) metricParameters.add("-P") metricParameters.add( - "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath, ) } @@ -83,3 +84,8 @@ private fun Project.buildComposeMetricsParameters(): List { } return metricParameters.toList() } + +private fun Project.stabilityConfiguration() = listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=${project.rootDir.absolutePath}/compose_compiler_config.conf", +) diff --git a/compose_compiler_config.conf b/compose_compiler_config.conf new file mode 100644 index 000000000..2341256f4 --- /dev/null +++ b/compose_compiler_config.conf @@ -0,0 +1,6 @@ +// This file contains classes (with possible wildcards) that the Compose Compiler will treat as stable. +// It allows us to define classes that our not part of our codebase without wrapping them in a stable class. +// For more information, check https://developer.android.com/jetpack/compose/performance/stability/fix#configuration-file + +java.time.ZoneId +java.time.ZoneOffset diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt new file mode 100644 index 000000000..ee8fe97a3 --- /dev/null +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt @@ -0,0 +1,27 @@ +/* + * 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.data.test + +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import java.time.ZoneId +import javax.inject.Inject + +class DefaultZoneIdTimeZoneMonitor @Inject constructor() : TimeZoneMonitor { + override val currentZoneId: Flow = flowOf(ZoneId.of("Europe/Warsaw")) +} diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt index 2ec2bcf9c..5bd2b8a43 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt @@ -28,6 +28,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeSearch import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeTopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeUserDataRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import dagger.Binds import dagger.Module import dagger.hilt.components.SingletonComponent @@ -68,4 +69,7 @@ interface TestDataModule { fun bindsNetworkMonitor( networkMonitor: AlwaysOnlineNetworkMonitor, ): NetworkMonitor + + @Binds + fun binds(impl: DefaultZoneIdTimeZoneMonitor): TimeZoneMonitor } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt index e135d7f58..fa4bde8b8 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt @@ -28,6 +28,8 @@ 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.util.ConnectivityManagerNetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneBroadcastMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -66,4 +68,7 @@ abstract class DataModule { internal abstract fun bindsNetworkMonitor( networkMonitor: ConnectivityManagerNetworkMonitor, ): NetworkMonitor + + @Binds + internal abstract fun binds(impl: TimeZoneBroadcastMonitor): TimeZoneMonitor } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt new file mode 100644 index 000000000..33b37934a --- /dev/null +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -0,0 +1,94 @@ +/* + * 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.data.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import androidx.core.os.trace +import com.google.samples.apps.nowinandroid.core.network.Dispatcher +import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO +import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.shareIn +import java.time.ZoneId +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Utility for reporting current timezone the device has set. + * It always emits at least once with default setting and then for each TZ change. + */ +interface TimeZoneMonitor { + val currentZoneId: Flow +} + +@Singleton +internal class TimeZoneBroadcastMonitor @Inject constructor( + @ApplicationContext private val context: Context, + @ApplicationScope appScope: CoroutineScope, + @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, +) : TimeZoneMonitor { + + override val currentZoneId: SharedFlow = + callbackFlow { + // Send the default time zone first. + trySend(ZoneId.systemDefault()) + + // Registers BroadcastReceiver for the TimeZone changes + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_TIMEZONE_CHANGED) return + + val zoneIdFromIntent = if (VERSION.SDK_INT < VERSION_CODES.R) { + null + } else { + // Starting Android R we also get the new TimeZone. + intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { zoneId -> + // We need to convert it from java.util.Timezone to java.time.ZoneId + ZoneId.of(zoneId, ZoneId.SHORT_IDS) + } + } + + // If there isn't a zoneId in the intent, fallback to the systemDefault, which should also reflect the change + trySend(zoneIdFromIntent ?: ZoneId.systemDefault()) + } + } + + trace("TimeZoneBroadcastReceiver.register") { + context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED)) + } + + awaitClose { + context.unregisterReceiver(receiver) + } + } + .flowOn(ioDispatcher) + // Sharing the callback to prevent multiple BroadcastReceivers being registered + .shareIn(appScope, SharingStarted.WhileSubscribed(5_000), 1) +} diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt new file mode 100644 index 000000000..8eb438fb1 --- /dev/null +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt @@ -0,0 +1,41 @@ +/* + * 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.testing.util + +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import java.time.ZoneId + +class TestTimeZoneMonitor : TimeZoneMonitor { + + private val timeZoneFlow = MutableStateFlow(defaultTimeZone) + + override val currentZoneId: Flow = timeZoneFlow + + /** + * A test-only API to set the from tests. + */ + fun setTimeZone(zoneId: ZoneId) { + timeZoneFlow.value = zoneId + } + + companion object { + val defaultTimeZone: ZoneId = ZoneId.of("Europe/Warsaw") + } +} + diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt new file mode 100644 index 000000000..cda5040bd --- /dev/null +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt @@ -0,0 +1,26 @@ +/* + * 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.ui + +import androidx.compose.runtime.compositionLocalOf +import java.time.ZoneId + +/** + * TimeZone that can be provided with the TimeZoneMonitor. + * This way, it's not needed to pass every single composable the time zone to show in UI. + */ +val LocalTimeZone = compositionLocalOf { ZoneId.systemDefault() } 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..c922d9687 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 @@ -40,7 +40,6 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -49,7 +48,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -71,7 +69,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Instant import kotlinx.datetime.toJavaInstant -import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.util.Locale @@ -244,27 +241,11 @@ fun NotificationDot( } @Composable -fun dateFormatted(publishDate: Instant): String { - var zoneId by remember { mutableStateOf(ZoneId.systemDefault()) } - - val context = LocalContext.current - - DisposableEffect(context) { - val receiver = TimeZoneBroadcastReceiver( - onTimeZoneChanged = { zoneId = ZoneId.systemDefault() }, - ) - receiver.register(context) - onDispose { - receiver.unregister(context) - } - } - - return DateTimeFormatter - .ofLocalizedDate(FormatStyle.MEDIUM) - .withLocale(Locale.getDefault()) - .withZone(zoneId) - .format(publishDate.toJavaInstant()) -} +fun dateFormatted(publishDate: Instant): String = DateTimeFormatter + .ofLocalizedDate(FormatStyle.MEDIUM) + .withLocale(Locale.getDefault()) + .withZone(LocalTimeZone.current) + .format(publishDate.toJavaInstant()) @Composable fun NewsResourceMetaData( diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/TimeZoneBroadcastReceiver.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/TimeZoneBroadcastReceiver.kt deleted file mode 100644 index f7ae813c4..000000000 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/TimeZoneBroadcastReceiver.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.ui - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter - -class TimeZoneBroadcastReceiver( - val onTimeZoneChanged: () -> Unit, -) : BroadcastReceiver() { - private var registered = false - - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == Intent.ACTION_TIMEZONE_CHANGED) { - onTimeZoneChanged() - } - } - - fun register(context: Context) { - if (!registered) { - val filter = IntentFilter() - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED) - context.registerReceiver(this, filter) - registered = true - } - } - - fun unregister(context: Context) { - if (registered) { - context.unregisterReceiver(this) - registered = false - } - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ef4cc225..5bcdaf41c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" androidxComposeBom = "2023.10.01" -androidxComposeCompiler = "1.5.7" +androidxComposeCompiler = "1.5.8" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" @@ -40,11 +40,11 @@ hilt = "2.50" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" -kotlin = "1.9.21" +kotlin = "1.9.22" kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" -ksp = "1.9.21-1.0.16" +ksp = "1.9.22-1.0.16" okhttp = "4.12.0" protobuf = "3.24.4" protobufPlugin = "0.9.4" From 0b3ece1bb9629a7440aceab98e5d5b9e046831af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 30 Jan 2024 13:34:01 +0100 Subject: [PATCH 07/37] Fix spotless Change-Id: I6583fa9ed7c1563f6164653be9bbaf7904ec0215 --- .../apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt | 1 - .../apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt index 24a50dd79..c74d79307 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ScrollForYouFeedBenchmark.kt @@ -58,4 +58,3 @@ class ScrollForYouFeedBenchmark { forYouScrollFeedDownUp() } } - diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt index 8eb438fb1..1d7f21543 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt @@ -38,4 +38,3 @@ class TestTimeZoneMonitor : TimeZoneMonitor { val defaultTimeZone: ZoneId = ZoneId.of("Europe/Warsaw") } } - From 1e925ed99fecb43973ba53168fc3a1266e369d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 30 Jan 2024 20:26:17 +0100 Subject: [PATCH 08/37] Conflate TimeZoneMonitor emissions Co-authored-by: Alex Vanyo --- .../samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index 33b37934a..04b2ef352 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -88,6 +88,7 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( context.unregisterReceiver(receiver) } } + .conflate() .flowOn(ioDispatcher) // Sharing the callback to prevent multiple BroadcastReceivers being registered .shareIn(appScope, SharingStarted.WhileSubscribed(5_000), 1) From 44ae059a2be020018e926f7a787566b241d7b3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 30 Jan 2024 20:29:12 +0100 Subject: [PATCH 09/37] Rename LocalTimeZone -> LocalZoneId + collects with lifecycle Change-Id: I429c563765a489965d0a1741e36c98a30e5d0ef3 --- .../com/google/samples/apps/nowinandroid/MainActivity.kt | 8 ++++---- .../core/ui/{LocalTimeZone.kt => LocalZoneId.kt} | 4 ++-- .../samples/apps/nowinandroid/core/ui/NewsResourceCard.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/{LocalTimeZone.kt => LocalZoneId.kt} (87%) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 779b6edbe..0bf45f125 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -28,12 +28,12 @@ import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.metrics.performance.JankStats @@ -47,7 +47,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme 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.ui.LocalTimeZone +import com.google.samples.apps.nowinandroid.core.ui.LocalZoneId import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import dagger.hilt.android.AndroidEntryPoint @@ -140,11 +140,11 @@ class MainActivity : ComponentActivity() { timeZoneMonitor = timeZoneMonitor, ) - val currentTimeZone by appState.currentTimeZone.collectAsState() + val currentZoneId by appState.currentTimeZone.collectAsStateWithLifecycle() CompositionLocalProvider( LocalAnalyticsHelper provides analyticsHelper, - LocalTimeZone provides currentTimeZone, + LocalZoneId provides currentZoneId, ) { NiaTheme( darkTheme = darkTheme, diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt similarity index 87% rename from core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt rename to core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt index cda5040bd..54aa7931d 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt @@ -20,7 +20,7 @@ import androidx.compose.runtime.compositionLocalOf import java.time.ZoneId /** - * TimeZone that can be provided with the TimeZoneMonitor. + * ZoneId that can be provided with the TimeZoneMonitor. * This way, it's not needed to pass every single composable the time zone to show in UI. */ -val LocalTimeZone = compositionLocalOf { ZoneId.systemDefault() } +val LocalZoneId = compositionLocalOf { ZoneId.systemDefault() } 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 c922d9687..9c4377b9d 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 @@ -244,7 +244,7 @@ fun NotificationDot( fun dateFormatted(publishDate: Instant): String = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.getDefault()) - .withZone(LocalTimeZone.current) + .withZone(LocalZoneId.current) .format(publishDate.toJavaInstant()) @Composable From a16d5f726c987641f78500e0004ff7ff028eb716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 30 Jan 2024 20:29:34 +0100 Subject: [PATCH 10/37] More readable zoneId creation Change-Id: I912dc021554e4886a5d09e63799aa5c81e8fd16a --- .../samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index 04b2ef352..99613d0f6 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import java.time.ZoneId +import java.util.TimeZone import javax.inject.Inject import javax.inject.Singleton @@ -72,6 +73,7 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { zoneId -> // We need to convert it from java.util.Timezone to java.time.ZoneId ZoneId.of(zoneId, ZoneId.SHORT_IDS) + TimeZone.getTimeZone(zoneId).toZoneId() } } From 894e817f8f88d076d09613d26c59e29868dd9570 Mon Sep 17 00:00:00 2001 From: yongsuk Date: Wed, 31 Jan 2024 18:52:53 +0900 Subject: [PATCH 11/37] Update typo setup.sh --- tools/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/setup.sh b/tools/setup.sh index 1467bbad0..b0f204268 100755 --- a/tools/setup.sh +++ b/tools/setup.sh @@ -35,7 +35,7 @@ cp "${GIT_ROOT}/tools/pre-push" "${GIT_DIR}/hooks/pre-push" \ cat <<-EOF Checking the following settings helps avoid miscellaneous issues: * Settings -> Editor -> General -> Remove trailing spaces on: Modified lines - * Settings -> Editor -> General -> Ensure every file ends with a line break + * Settings -> Editor -> General -> Ensure every saved file ends with a line break * Settings -> Editor -> General -> Auto Import -> Optimize imports on the fly (for both Kotlin\ and Java) EOF From ea1573fcc1d61713839ed5d510b4fc8d8f36b211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 11:29:14 +0100 Subject: [PATCH 12/37] Use kotlinx.datetime.Timezone instead of java ZoneId Change-Id: I62c4d044b319a9b59e06bb42fd12971d992e8628 --- .../apps/nowinandroid/ui/NiaAppStateTest.kt | 4 ++-- .../samples/apps/nowinandroid/MainActivity.kt | 4 ++-- .../apps/nowinandroid/ui/NiaAppState.kt | 6 +++--- .../data/test/DefaultZoneIdTimeZoneMonitor.kt | 4 ++-- .../core/data/util/TimeZoneMonitor.kt | 19 ++++++++++--------- .../core/testing/util/TestTimeZoneMonitor.kt | 8 ++++---- .../ui/{LocalZoneId.kt => LocalTimeZone.kt} | 6 +++--- .../nowinandroid/core/ui/NewsResourceCard.kt | 3 ++- 8 files changed, 28 insertions(+), 26 deletions(-) rename core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/{LocalZoneId.kt => LocalTimeZone.kt} (83%) diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt index 18afc6a09..732e527bb 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt @@ -39,9 +39,9 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest +import kotlinx.datetime.TimeZone import org.junit.Rule import org.junit.Test -import java.time.ZoneId import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -199,7 +199,7 @@ class NiaAppStateTest { timeZoneMonitor = timeZoneMonitor, ) } - val changedTz = ZoneId.of("Europe/Prague") + val changedTz = TimeZone.of("Europe/Prague") backgroundScope.launch { state.currentTimeZone.collect() } timeZoneMonitor.setTimeZone(changedTz) assertEquals( diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 0bf45f125..afe3b51eb 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -47,7 +47,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme 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.ui.LocalZoneId +import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import dagger.hilt.android.AndroidEntryPoint @@ -144,7 +144,7 @@ class MainActivity : ComponentActivity() { CompositionLocalProvider( LocalAnalyticsHelper provides analyticsHelper, - LocalZoneId provides currentZoneId, + LocalTimeZone provides currentZoneId, ) { NiaTheme( darkTheme = darkTheme, diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index ffd0c16ec..ec2be6511 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import java.time.ZoneId +import kotlinx.datetime.TimeZone @Composable fun rememberNiaAppState( @@ -140,11 +140,11 @@ class NiaAppState( initialValue = emptySet(), ) - val currentTimeZone = timeZoneMonitor.currentZoneId + val currentTimeZone = timeZoneMonitor.currentTimeZone .stateIn( coroutineScope, SharingStarted.WhileSubscribed(5_000), - ZoneId.systemDefault(), + TimeZone.currentSystemDefault(), ) /** diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt index ee8fe97a3..5a21ae337 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt @@ -19,9 +19,9 @@ package com.google.samples.apps.nowinandroid.core.data.test import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import java.time.ZoneId +import kotlinx.datetime.TimeZone import javax.inject.Inject class DefaultZoneIdTimeZoneMonitor @Inject constructor() : TimeZoneMonitor { - override val currentZoneId: Flow = flowOf(ZoneId.of("Europe/Warsaw")) + override val currentTimeZone: Flow = flowOf(TimeZone.of("Europe/Warsaw")) } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index 99613d0f6..f7cc73565 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -34,10 +34,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn +import kotlinx.datetime.TimeZone import java.time.ZoneId -import java.util.TimeZone import javax.inject.Inject import javax.inject.Singleton @@ -46,7 +47,7 @@ import javax.inject.Singleton * It always emits at least once with default setting and then for each TZ change. */ interface TimeZoneMonitor { - val currentZoneId: Flow + val currentTimeZone: Flow } @Singleton @@ -56,10 +57,10 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, ) : TimeZoneMonitor { - override val currentZoneId: SharedFlow = - callbackFlow { + override val currentTimeZone: SharedFlow = + callbackFlow { // Send the default time zone first. - trySend(ZoneId.systemDefault()) + trySend(TimeZone.currentSystemDefault()) // Registers BroadcastReceiver for the TimeZone changes val receiver = object : BroadcastReceiver() { @@ -70,15 +71,15 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( null } else { // Starting Android R we also get the new TimeZone. - intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { zoneId -> + intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { timeZoneId -> // We need to convert it from java.util.Timezone to java.time.ZoneId - ZoneId.of(zoneId, ZoneId.SHORT_IDS) - TimeZone.getTimeZone(zoneId).toZoneId() + val zoneId = ZoneId.of(timeZoneId, ZoneId.SHORT_IDS) + TimeZone.of(zoneId.id) } } // If there isn't a zoneId in the intent, fallback to the systemDefault, which should also reflect the change - trySend(zoneIdFromIntent ?: ZoneId.systemDefault()) + trySend(zoneIdFromIntent ?: TimeZone.currentSystemDefault()) } } diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt index 1d7f21543..cc71ab2ca 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt @@ -19,22 +19,22 @@ package com.google.samples.apps.nowinandroid.core.testing.util import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import java.time.ZoneId +import kotlinx.datetime.TimeZone class TestTimeZoneMonitor : TimeZoneMonitor { private val timeZoneFlow = MutableStateFlow(defaultTimeZone) - override val currentZoneId: Flow = timeZoneFlow + override val currentTimeZone: Flow = timeZoneFlow /** * A test-only API to set the from tests. */ - fun setTimeZone(zoneId: ZoneId) { + fun setTimeZone(zoneId: TimeZone) { timeZoneFlow.value = zoneId } companion object { - val defaultTimeZone: ZoneId = ZoneId.of("Europe/Warsaw") + val defaultTimeZone: TimeZone = TimeZone.of("Europe/Warsaw") } } diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt similarity index 83% rename from core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt rename to core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt index 54aa7931d..2d9948488 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalZoneId.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/LocalTimeZone.kt @@ -17,10 +17,10 @@ package com.google.samples.apps.nowinandroid.core.ui import androidx.compose.runtime.compositionLocalOf -import java.time.ZoneId +import kotlinx.datetime.TimeZone /** - * ZoneId that can be provided with the TimeZoneMonitor. + * TimeZone that can be provided with the TimeZoneMonitor. * This way, it's not needed to pass every single composable the time zone to show in UI. */ -val LocalZoneId = compositionLocalOf { ZoneId.systemDefault() } +val LocalTimeZone = compositionLocalOf { TimeZone.currentSystemDefault() } 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 9c4377b9d..2b22c7ea2 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 @@ -69,6 +69,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Instant import kotlinx.datetime.toJavaInstant +import kotlinx.datetime.toJavaZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.util.Locale @@ -244,7 +245,7 @@ fun NotificationDot( fun dateFormatted(publishDate: Instant): String = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.getDefault()) - .withZone(LocalZoneId.current) + .withZone(LocalTimeZone.current.toJavaZoneId()) .format(publishDate.toJavaInstant()) @Composable From 6ec3b3c7a232d8c9abc108fa90e1cdb827ce9a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 11:32:49 +0100 Subject: [PATCH 13/37] Use androidx.tracing everywhere Change-Id: I9a77c4434d271e2e23700823f9f513aa61d177f8 --- .../src/main/kotlin/AndroidFeatureConventionPlugin.kt | 2 ++ .../src/main/kotlin/AndroidLibraryConventionPlugin.kt | 3 +++ .../apps/nowinandroid/core/data/util/TimeZoneMonitor.kt | 4 ++-- .../samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt | 2 +- gradle/libs.versions.toml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 7a334beb3..8a76a1ac9 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -44,6 +44,8 @@ class AndroidFeatureConventionPlugin : Plugin { add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index 8eb329f58..d442d94ef 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -21,6 +21,7 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests +import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -51,6 +52,8 @@ class AndroidLibraryConventionPlugin : Plugin { } dependencies { add("testImplementation", kotlin("test")) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index f7cc73565..d75f81efc 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -22,7 +22,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.Build.VERSION import android.os.Build.VERSION_CODES -import androidx.core.os.trace +import androidx.tracing.trace import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope @@ -58,7 +58,7 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( ) : TimeZoneMonitor { override val currentTimeZone: SharedFlow = - callbackFlow { + callbackFlow { // Send the default time zone first. trySend(TimeZone.currentSystemDefault()) 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 30134715b..e8b19594f 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 @@ -81,7 +81,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.compose.ui.util.trace +import androidx.tracing.trace import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5bcdaf41c..d85a8c3ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidxTestCore = "1.5.0" androidxTestExt = "1.1.5" androidxTestRules = "1.5.0" androidxTestRunner = "1.5.2" -androidxTracing = "1.1.0" +androidxTracing = "1.3.0-alpha02" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.2.0" androidxWork = "2.9.0" From 6faf3310a13cd64175fab18a53bd09eb60020f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 12:11:22 +0100 Subject: [PATCH 14/37] Fix spotless Change-Id: I26a64df036d17bcd455e782306fb31c099dd53ed --- .../samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e8b19594f..e1418d747 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 @@ -81,9 +81,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.tracing.trace import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.tracing.trace import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionStatus.Denied import com.google.accompanist.permissions.rememberPermissionState From 3d339e1658dcf9c55ae37ac24ff4c3c175fc3556 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 6 Jan 2024 10:50:26 +0100 Subject: [PATCH 15/37] Move `:core:data` fake repositories to `:core:data-test` Now that these fake implementations are no longer used in the production source code (only in tests), we can safely extract them out of this module. Hopefully, we'll later be able to use Kotlin testFixtures for that. References: - #709 - #875 --- .../nowinandroid/core/data/test/TestDataModule.kt | 12 ++++++------ .../core/data/test/repository}/FakeNewsRepository.kt | 4 ++-- .../test/repository}/FakeRecentSearchRepository.kt | 4 ++-- .../test/repository}/FakeSearchContentsRepository.kt | 4 ++-- .../data/test/repository}/FakeTopicsRepository.kt | 4 ++-- .../data/test/repository}/FakeUserDataRepository.kt | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename core/{data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake => data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository}/FakeNewsRepository.kt (96%) rename core/{data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake => data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository}/FakeRecentSearchRepository.kt (88%) rename core/{data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake => data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository}/FakeSearchContentsRepository.kt (87%) rename core/{data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake => data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository}/FakeTopicsRepository.kt (94%) rename core/{data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake => data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository}/FakeUserDataRepository.kt (95%) diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt index 2ec2bcf9c..0ae4865ec 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt @@ -22,11 +22,11 @@ import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRep import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeNewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeRecentSearchRepository -import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeSearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeTopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.fake.FakeUserDataRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeNewsRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeRecentSearchRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeSearchContentsRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeTopicsRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import dagger.Binds import dagger.Module @@ -38,7 +38,7 @@ import dagger.hilt.testing.TestInstallIn components = [SingletonComponent::class], replaces = [DataModule::class], ) -interface TestDataModule { +internal interface TestDataModule { @Binds fun bindsTopicRepository( fakeTopicsRepository: FakeTopicsRepository, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeNewsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt similarity index 96% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeNewsRepository.kt rename to core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt index 39ad05d1e..5ceff4dd0 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeNewsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository.fake +package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.model.asEntity @@ -39,7 +39,7 @@ import javax.inject.Inject * This allows us to run the app with fake data, without needing an internet connection or working * backend. */ -class FakeNewsRepository @Inject constructor( +internal class FakeNewsRepository @Inject constructor( @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, private val datasource: FakeNiaNetworkDataSource, ) : NewsRepository { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeRecentSearchRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt similarity index 88% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeRecentSearchRepository.kt rename to core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt index 025b51f68..b8d949efe 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeRecentSearchRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository.fake +package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository @@ -25,7 +25,7 @@ import javax.inject.Inject /** * Fake implementation of the [RecentSearchRepository] */ -class FakeRecentSearchRepository @Inject constructor() : RecentSearchRepository { +internal class FakeRecentSearchRepository @Inject constructor() : RecentSearchRepository { override suspend fun insertOrReplaceRecentSearch(searchQuery: String) = Unit override fun getRecentSearchQueries(limit: Int): Flow> = diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeSearchContentsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt similarity index 87% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeSearchContentsRepository.kt rename to core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt index 65cced452..1feeb6dcc 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeSearchContentsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository.fake +package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.model.data.SearchResult @@ -25,7 +25,7 @@ import javax.inject.Inject /** * Fake implementation of the [SearchContentsRepository] */ -class FakeSearchContentsRepository @Inject constructor() : SearchContentsRepository { +internal class FakeSearchContentsRepository @Inject constructor() : SearchContentsRepository { override suspend fun populateFtsData() = Unit override fun searchContents(searchQuery: String): Flow = flowOf() diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeTopicsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt similarity index 94% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeTopicsRepository.kt rename to core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt index 0eefc8451..f8ebca29e 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeTopicsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository.fake +package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository @@ -36,7 +36,7 @@ import javax.inject.Inject * This allows us to run the app with fake data, without needing an internet connection or working * backend. */ -class FakeTopicsRepository @Inject constructor( +internal class FakeTopicsRepository @Inject constructor( @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, private val datasource: FakeNiaNetworkDataSource, ) : TopicsRepository { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt similarity index 95% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt rename to core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt index a9da29b56..cdd23429f 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository.fake +package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource @@ -30,7 +30,7 @@ import javax.inject.Inject * This allows us to run the app with fake data, without needing an internet connection or working * backend. */ -class FakeUserDataRepository @Inject constructor( +internal class FakeUserDataRepository @Inject constructor( private val niaPreferencesDataSource: NiaPreferencesDataSource, ) : UserDataRepository { From f8e797fd2698b84eb62974e537b9981f7812079e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 6 Feb 2024 08:53:04 +0100 Subject: [PATCH 16/37] Rename currentTimezone variable Change-Id: I92630f6851b0253294a8d3ec2e5b3cbe7db74b4d --- .../com/google/samples/apps/nowinandroid/MainActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index afe3b51eb..ad95c297f 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -140,11 +140,11 @@ class MainActivity : ComponentActivity() { timeZoneMonitor = timeZoneMonitor, ) - val currentZoneId by appState.currentTimeZone.collectAsStateWithLifecycle() + val currentTimeZone by appState.currentTimeZone.collectAsStateWithLifecycle() CompositionLocalProvider( LocalAnalyticsHelper provides analyticsHelper, - LocalTimeZone provides currentZoneId, + LocalTimeZone provides currentTimeZone, ) { NiaTheme( darkTheme = darkTheme, From 08bfc548326de731c3facb1eaa613e1a9f97edc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 6 Feb 2024 08:56:43 +0100 Subject: [PATCH 17/37] Update dependency guard baseline Change-Id: I2557753f1eb4c6845aca6473a6aba5b7e758ebbf --- .../dependencies/releaseRuntimeClasspath.txt | 7 ++++--- app/dependencies/prodReleaseRuntimeClasspath.txt | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 10f2dfa32..e59cc53bc 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -70,7 +70,8 @@ androidx.profileinstaller:profileinstaller:1.3.1 androidx.savedstate:savedstate-ktx:1.2.1 androidx.savedstate:savedstate:1.2.1 androidx.startup:startup-runtime:1.1.1 -androidx.tracing:tracing:1.0.0 +androidx.tracing:tracing-ktx:1.3.0-alpha02 +androidx.tracing:tracing:1.3.0-alpha02 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 @@ -90,10 +91,10 @@ io.coil-kt:coil-compose-base:2.5.0 io.coil-kt:coil-compose:2.5.0 io.coil-kt:coil:2.5.0 javax.inject:javax.inject:1 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib:1.9.22 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index e9e3ebf8e..6eb888042 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -32,6 +32,7 @@ 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-tracing:1.0.0-beta01 androidx.compose.runtime:runtime:1.5.4 androidx.compose.ui:ui-android:1.5.4 androidx.compose.ui:ui-geometry-android:1.5.4 @@ -105,8 +106,9 @@ androidx.savedstate:savedstate:1.2.1 androidx.sqlite:sqlite-framework:2.4.0 androidx.sqlite:sqlite:2.4.0 androidx.startup:startup-runtime:1.1.1 -androidx.tracing:tracing-ktx:1.1.0 -androidx.tracing:tracing:1.1.0 +androidx.tracing:tracing-ktx:1.3.0-alpha02 +androidx.tracing:tracing-perfetto:1.0.0 +androidx.tracing:tracing:1.3.0-alpha02 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 @@ -185,10 +187,10 @@ io.github.aakira:napier-android:1.4.1 io.github.aakira:napier:1.4.1 javax.inject:javax.inject:1 org.checkerframework:checker-qual:3.12.0 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib:1.9.22 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 From 12a3a6eddea9ddf686a82b70c2149146d8df9682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 6 Feb 2024 15:04:24 +0100 Subject: [PATCH 18/37] Use trySend multiple times Change-Id: Ie00c4180d07fc4bb9d457647f3b7ddadd5a3c032 --- .../com/google/samples/apps/nowinandroid/ui/NiaAppState.kt | 2 +- .../apps/nowinandroid/core/data/util/TimeZoneMonitor.kt | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index ec2be6511..d423adfbf 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -89,7 +89,7 @@ class NiaAppState( val windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, userNewsResourceRepository: UserNewsResourceRepository, - private val timeZoneMonitor: TimeZoneMonitor, + timeZoneMonitor: TimeZoneMonitor, ) { val currentDestination: NavDestination? @Composable get() = navController diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index d75f81efc..6e206e67c 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import kotlinx.datetime.TimeZone @@ -87,10 +88,16 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED)) } + // Send here again, because registering the Broadcast Receiver can take up to several milliseconds. + // This way, we can reduce the likelihood that a TZ change wouldn't be caught with the Broadcast Receiver. + trySend(TimeZone.currentSystemDefault()) + awaitClose { context.unregisterReceiver(receiver) } } + // We use to prevent multiple emissions of the same type, because we use trySend multiple times. + .distinctUntilChanged() .conflate() .flowOn(ioDispatcher) // Sharing the callback to prevent multiple BroadcastReceivers being registered From 9daedc9690f4af3df92b472219736e8bd09be6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 6 Feb 2024 15:07:12 +0100 Subject: [PATCH 19/37] Improve converting to kotlin timezone Change-Id: I2af929c341cb274ae6b93bb8dffe696de6ed1c63 --- .../apps/nowinandroid/core/data/util/TimeZoneMonitor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index 6e206e67c..031bc9388 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import kotlinx.datetime.TimeZone +import kotlinx.datetime.toKotlinTimeZone import java.time.ZoneId import javax.inject.Inject import javax.inject.Singleton @@ -75,7 +76,8 @@ internal class TimeZoneBroadcastMonitor @Inject constructor( intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { timeZoneId -> // We need to convert it from java.util.Timezone to java.time.ZoneId val zoneId = ZoneId.of(timeZoneId, ZoneId.SHORT_IDS) - TimeZone.of(zoneId.id) + // Convert to kotlinx.datetime.TimeZone + zoneId.toKotlinTimeZone() } } From ea5c6922667698d14267a918c0ba049b37b9d449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Alc=C3=A9rreca?= Date: Wed, 7 Feb 2024 10:28:34 +0100 Subject: [PATCH 20/37] Dpm --- .github/workflows/Build.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index fa3a77f55..68b1f5f64 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -176,6 +176,12 @@ jobs: - name: Build projects before running emulator run: ./gradlew packageDemoDebug packageDemoDebugAndroidTest + - name: Print available space + run: df -h + + - name: Print disk usage + run: du -h -d 2 /home/runner/ + - name: Run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 with: From 949929366455fda5f60a125539b10425adb9df81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Alc=C3=A9rreca?= Date: Wed, 7 Feb 2024 10:51:52 +0100 Subject: [PATCH 21/37] Figuring out what is using 66Gb in the runner --- .github/workflows/Build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 68b1f5f64..d79dd2a5e 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -180,7 +180,7 @@ jobs: run: df -h - name: Print disk usage - run: du -h -d 2 /home/runner/ + run: du -h -d 2 / - name: Run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 From 8b56fd31605d2ab38f3b598cb82002e6ca45f343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Tue, 20 Feb 2024 13:58:30 +0100 Subject: [PATCH 22/37] Remove disk usage testing Change-Id: I57bb3778dc41a7c21b8e923e05ee1c68eb15def3 --- .github/workflows/Build.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 930c62d39..5f501b6c0 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -183,12 +183,6 @@ jobs: - name: Build projects before running emulator run: ./gradlew packageDemoDebug packageDemoDebugAndroidTest - - name: Print available space - run: df -h - - - name: Print disk usage - run: du -h -d 2 / - - name: Run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 with: From 9b488bf70ab1b3ceded4f7a3c6071ac9e2748919 Mon Sep 17 00:00:00 2001 From: Jaehwa Noh Date: Sun, 3 Mar 2024 13:57:03 +0900 Subject: [PATCH 23/37] Move java to kotlin folder. Change-Id: Iaf4723dea6169fbf3f9a8d3bf8779179ac56ab86 --- .../samples/apps/nowinandroid/util/ProfileVerifierLogger.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/{java => kotlin}/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt (100%) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt similarity index 100% rename from app/src/main/java/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt rename to app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt From c90804f198d1a272c19494403c6a661cca8098c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 09:00:57 +0000 Subject: [PATCH 24/37] Bump com.google.truth:truth from 1.1.5 to 1.4.2 Bumps [com.google.truth:truth](https://github.com/google/truth) from 1.1.5 to 1.4.2. - [Release notes](https://github.com/google/truth/releases) - [Commits](https://github.com/google/truth/compare/v1.1.5...v1.4.2) --- updated-dependencies: - dependency-name: com.google.truth:truth dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c26e134ca..5c4c3b0f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,7 +54,7 @@ robolectric = "4.11.1" roborazzi = "1.7.0" room = "2.6.1" secrets = "2.0.1" -truth = "1.1.5" +truth = "1.4.2" turbine = "1.0.0" [libraries] From 6b6bba9db3b6926d3c5765647a7bef2815290a30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 09:01:16 +0000 Subject: [PATCH 25/37] Bump hilt from 2.50 to 2.51 Bumps `hilt` from 2.50 to 2.51. Updates `com.google.dagger:hilt-android` from 2.50 to 2.51 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.50...dagger-2.51) Updates `com.google.dagger:hilt-android-testing` from 2.50 to 2.51 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.50...dagger-2.51) Updates `com.google.dagger:hilt-android-compiler` from 2.50 to 2.51 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.50...dagger-2.51) Updates `com.google.dagger.hilt.android` from 2.50 to 2.51 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.50...dagger-2.51) --- updated-dependencies: - dependency-name: com.google.dagger:hilt-android dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.dagger:hilt-android-testing dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.dagger:hilt-android-compiler dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.dagger.hilt.android dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c26e134ca..18586cb35 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,7 @@ firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" googleOss = "17.0.1" googleOssPlugin = "0.10.6" -hilt = "2.50" +hilt = "2.51" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" From b2b9f12915cbaf8681125ea3f55ff3398e14f390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2024 09:04:03 +0000 Subject: [PATCH 26/37] =?UTF-8?q?=F0=9F=A4=96=20Updates=20baselines=20for?= =?UTF-8?q?=20Dependency=20Guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-nia-catalog/dependencies/releaseRuntimeClasspath.txt | 8 ++++---- app/dependencies/prodReleaseRuntimeClasspath.txt | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 182ff5451..f51d155e9 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -80,10 +80,10 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.code.findbugs:jsr305:3.0.2 -com.google.dagger:dagger-lint-aar:2.50 -com.google.dagger:dagger:2.50 -com.google.dagger:hilt-android:2.50 -com.google.dagger:hilt-core:2.50 +com.google.dagger:dagger-lint-aar:2.51 +com.google.dagger:dagger:2.51 +com.google.dagger:hilt-android:2.51 +com.google.dagger:hilt-core:2.51 com.google.guava:listenablefuture:1.0 com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.6.0 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index e7a4ac804..81b7343dd 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -137,10 +137,10 @@ com.google.android.gms:play-services-oss-licenses:17.0.1 com.google.android.gms:play-services-stats:17.0.2 com.google.android.gms:play-services-tasks:18.0.2 com.google.code.findbugs:jsr305:3.0.2 -com.google.dagger:dagger-lint-aar:2.50 -com.google.dagger:dagger:2.50 -com.google.dagger:hilt-android:2.50 -com.google.dagger:hilt-core:2.50 +com.google.dagger:dagger-lint-aar:2.51 +com.google.dagger:dagger:2.51 +com.google.dagger:hilt-android:2.51 +com.google.dagger:hilt-core:2.51 com.google.errorprone:error_prone_annotations:2.11.0 com.google.firebase:firebase-abt:21.1.1 com.google.firebase:firebase-analytics-ktx:21.4.0 From eb6b344463304756d6f418547897931af1eaed88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 09:07:19 +0000 Subject: [PATCH 27/37] Bump the kotlin-ksp-compose group with 6 updates Bumps the kotlin-ksp-compose group with 6 updates: | Package | From | To | | --- | --- | --- | | [org.jetbrains.kotlin:kotlin-stdlib-jdk8](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [org.jetbrains.kotlin.plugin.serialization](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) | `1.9.21` | `1.9.22` | | [com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin](https://github.com/google/ksp) | `1.9.21-1.0.16` | `1.9.22-1.0.18` | | [com.google.devtools.ksp](https://github.com/google/ksp) | `1.9.21-1.0.16` | `1.9.22-1.0.18` | Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `org.jetbrains.kotlin.jvm` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `org.jetbrains.kotlin.plugin.serialization` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `org.jetbrains.kotlin.jvm` from 1.9.21 to 1.9.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.21...v1.9.22) Updates `com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin` from 1.9.21-1.0.16 to 1.9.22-1.0.18 - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/commits) Updates `com.google.devtools.ksp` from 1.9.21-1.0.16 to 1.9.22-1.0.18 - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/commits) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: org.jetbrains.kotlin.plugin.serialization dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - dependency-name: com.google.devtools.ksp dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c26e134ca..dab6c3ab2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,11 +40,11 @@ hilt = "2.50" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" -kotlin = "1.9.21" +kotlin = "1.9.22" kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" -ksp = "1.9.21-1.0.16" +ksp = "1.9.22-1.0.18" okhttp = "4.12.0" protobuf = "3.25.2" protobufPlugin = "0.9.4" From 6996c88e91d4d92254fa326976987d85f8d6e52b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 4 Mar 2024 09:10:40 +0000 Subject: [PATCH 28/37] =?UTF-8?q?=F0=9F=A4=96=20Updates=20baselines=20for?= =?UTF-8?q?=20Dependency=20Guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-nia-catalog/dependencies/releaseRuntimeClasspath.txt | 4 ++-- app/dependencies/prodReleaseRuntimeClasspath.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 182ff5451..2c921f091 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -93,10 +93,10 @@ io.coil-kt:coil-compose-base:2.5.0 io.coil-kt:coil-compose:2.5.0 io.coil-kt:coil:2.5.0 javax.inject:javax.inject:1 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib:1.9.22 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index e7a4ac804..70262d4ac 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -188,10 +188,10 @@ io.github.aakira:napier-android:1.4.1 io.github.aakira:napier:1.4.1 javax.inject:javax.inject:1 org.checkerframework:checker-qual:3.12.0 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.21 +org.jetbrains.kotlin:kotlin-stdlib:1.9.22 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 From 52c0d7ae0a7b48fb2b8498c952d8ac41c7e31d17 Mon Sep 17 00:00:00 2001 From: Jaehwa Noh Date: Mon, 4 Mar 2024 19:51:12 +0900 Subject: [PATCH 29/37] Remove Insert function - Insert is only used in test. - Upsert do same thing as Insert. Change-Id: Ib5f8e693e71c895b83e43f6b51cebdc698bf744b --- .../core/data/testdoubles/TestNewsResourceDao.kt | 15 --------------- .../core/database/dao/NewsResourceDao.kt | 6 ------ 2 files changed, 21 deletions(-) diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt index dc4b78e01..a3e373918 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt @@ -92,21 +92,6 @@ class TestNewsResourceDao : NewsResourceDao { result.map { it.entity.id } } - override suspend fun insertOrIgnoreNewsResources( - entities: List, - ): List { - entitiesStateFlow.update { oldValues -> - // Old values come first so new values don't overwrite them - (oldValues + entities) - .distinctBy(NewsResourceEntity::id) - .sortedWith( - compareBy(NewsResourceEntity::publishDate).reversed(), - ) - } - // Assume no conflicts on insert - return entities.map { it.id.toLong() } - } - override suspend fun upsertNewsResources(newsResourceEntities: List) { entitiesStateFlow.update { oldValues -> // New values come first so they overwrite old values diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt index 0ad1e4f7d..929b88ce6 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt @@ -96,12 +96,6 @@ interface NewsResourceDao { filterNewsIds: Set = emptySet(), ): Flow> - /** - * Inserts [entities] into the db if they don't exist, and ignores those that do - */ - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreNewsResources(entities: List): List - /** * Inserts or updates [newsResourceEntities] in the db under the specified primary keys */ From 9cd430ba7d6b48d1ee155a055972752048610d86 Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Tue, 2 Jan 2024 16:31:21 -0800 Subject: [PATCH 30/37] Update AGP to 8.3.0 Change-Id: I6ea5aeaf357dafbae7be0e7f5f0443fbb1a8e68b --- .../com/google/samples/apps/nowinandroid/AndroidCompose.kt | 2 +- .../google/samples/apps/nowinandroid/GradleManagedDevices.kt | 2 +- .../com/google/samples/apps/nowinandroid/KotlinAndroid.kt | 2 +- .../kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt | 2 +- gradle/libs.versions.toml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt index 72d37db1b..3284a2499 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile * Configure Compose-specific options */ internal fun Project.configureAndroidCompose( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { commonExtension.apply { buildFeatures { diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt index 6aa896444..f67e9093d 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt @@ -25,7 +25,7 @@ import org.gradle.kotlin.dsl.invoke * Configure project for Gradle managed devices */ internal fun configureGradleManagedDevices( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { val pixel4 = DeviceConfig("Pixel 4", 30, "aosp-atd") val pixel6 = DeviceConfig("Pixel 6", 31, "aosp") diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt index 903c84d8f..f9a6717c3 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt @@ -30,7 +30,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile * Configure base Kotlin with Android options */ internal fun Project.configureKotlinAndroid( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { commonExtension.apply { compileSdk = 34 diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt index 60d059ac0..633098604 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt @@ -20,7 +20,7 @@ enum class NiaFlavor(val dimension: FlavorDimension, val applicationIdSuffix: St } fun configureFlavors( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {} ) { commonExtension.apply { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fafee176..680d0542b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,8 +2,8 @@ accompanist = "0.32.0" androidDesugarJdkLibs = "2.0.4" # AGP and tools should be updated together -androidGradlePlugin = "8.2.0" -androidTools = "31.2.0" +androidGradlePlugin = "8.3.0" +androidTools = "31.3.0" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" From e3c7018124603a424245106f05f49ae5c269272f Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Mon, 4 Mar 2024 16:34:45 -0800 Subject: [PATCH 31/37] Extract ScreenshotHelper to separate testing module Change-Id: Icf98a4acc9963656530a1a9fc8b156a553a070af --- app/build.gradle.kts | 2 ++ core/designsystem/build.gradle.kts | 1 + core/screenshot-testing/.gitignore | 1 + core/screenshot-testing/build.gradle.kts | 34 +++++++++++++++++++ .../src/main/AndroidManifest.xml | 17 ++++++++++ .../core/testing/util/ScreenshotHelper.kt | 0 core/testing/build.gradle.kts | 4 --- feature/foryou/build.gradle.kts | 1 + gradle/libs.versions.toml | 1 - settings.gradle.kts | 7 ++-- 10 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 core/screenshot-testing/.gitignore create mode 100644 core/screenshot-testing/build.gradle.kts create mode 100644 core/screenshot-testing/src/main/AndroidManifest.xml rename core/{testing => screenshot-testing}/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt (100%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d674f4bec..51fbb1c0d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -113,10 +113,12 @@ dependencies { testDemoImplementation(libs.robolectric) testDemoImplementation(libs.roborazzi) + testDemoImplementation(projects.core.screenshotTesting) androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.dataTest) androidTestImplementation(projects.core.datastoreTest) + androidTestImplementation(libs.androidx.test.espresso.core) androidTestImplementation(libs.androidx.navigation.testing) androidTestImplementation(libs.accompanist.testharness) androidTestImplementation(libs.hilt.android.testing) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index d68117d06..2bd79a442 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) testImplementation(libs.roborazzi) + testImplementation(projects.core.screenshotTesting) testImplementation(projects.core.testing) androidTestImplementation(libs.androidx.compose.ui.test) diff --git a/core/screenshot-testing/.gitignore b/core/screenshot-testing/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/screenshot-testing/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/screenshot-testing/build.gradle.kts b/core/screenshot-testing/build.gradle.kts new file mode 100644 index 000000000..4e9a931b0 --- /dev/null +++ b/core/screenshot-testing/build.gradle.kts @@ -0,0 +1,34 @@ +/* + * 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. + */ +plugins { + alias(libs.plugins.nowinandroid.android.library) + alias(libs.plugins.nowinandroid.android.library.compose) + alias(libs.plugins.nowinandroid.android.hilt) +} + +android { + namespace = "com.google.samples.apps.nowinandroid.core.screenshottesting" +} + +dependencies { + api(libs.roborazzi) + implementation(libs.accompanist.testharness) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.compose.ui.test) + implementation(libs.robolectric) + implementation(projects.core.common) + implementation(projects.core.designsystem) +} diff --git a/core/screenshot-testing/src/main/AndroidManifest.xml b/core/screenshot-testing/src/main/AndroidManifest.xml new file mode 100644 index 000000000..51d0cfc2e --- /dev/null +++ b/core/screenshot-testing/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt b/core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt rename to core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts index 275555d80..02729ceff 100644 --- a/core/testing/build.gradle.kts +++ b/core/testing/build.gradle.kts @@ -26,7 +26,6 @@ android { dependencies { api(kotlin("test")) api(libs.androidx.compose.ui.test) - api(libs.roborazzi) api(projects.core.analytics) api(projects.core.data) api(projects.core.model) @@ -34,13 +33,10 @@ dependencies { debugApi(libs.androidx.compose.ui.testManifest) - implementation(libs.accompanist.testharness) - implementation(libs.androidx.activity.compose) implementation(libs.androidx.test.rules) implementation(libs.hilt.android.testing) implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.datetime) - implementation(libs.robolectric.shadows) implementation(projects.core.common) implementation(projects.core.designsystem) } diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index da0a7ec5b..fd41d9a13 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) testImplementation(projects.core.testing) + testImplementation(projects.core.screenshotTesting) testDemoImplementation(libs.roborazzi) androidTestImplementation(projects.core.testing) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 680d0542b..17d482a75 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -128,7 +128,6 @@ protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" } robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } -robolectric-shadows = { group = "org.robolectric", name = "shadows-framework", version.ref = "robolectric" } roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } diff --git a/settings.gradle.kts b/settings.gradle.kts index fa043c955..949dbfdd1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,6 +36,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include(":app") include(":app-nia-catalog") include(":benchmarks") +include(":core:analytics") include(":core:common") include(":core:data") include(":core:data-test") @@ -47,10 +48,10 @@ include(":core:designsystem") include(":core:domain") include(":core:model") include(":core:network") -include(":core:ui") -include(":core:testing") -include(":core:analytics") include(":core:notifications") +include(":core:screenshot-testing") +include(":core:testing") +include(":core:ui") include(":feature:foryou") include(":feature:interests") From 06104076b2f5b16ae65180a4e3f3b422468286b9 Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Mon, 4 Mar 2024 16:56:57 -0800 Subject: [PATCH 32/37] Add explicit guava-android dependency for :sync:work Change-Id: I3c2c201eb0669165e3594bedfea83bff29c28ce2 --- gradle/libs.versions.toml | 2 ++ sync/work/build.gradle.kts | 1 + 2 files changed, 3 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17d482a75..e7f00830a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,6 +36,7 @@ firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" googleOss = "17.0.1" googleOssPlugin = "0.10.6" +guavaAndroid = "33.0.0-android" hilt = "2.51" hiltExt = "1.1.0" jacoco = "0.8.7" @@ -108,6 +109,7 @@ firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashly firebase-performance = { group = "com.google.firebase", name = "firebase-perf-ktx" } google-oss-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "googleOss" } google-oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "googleOssPlugin" } +guava-android = { group = "com.google.guava", name = "guava", version.ref = "guavaAndroid" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } diff --git a/sync/work/build.gradle.kts b/sync/work/build.gradle.kts index 7e61c7389..f949dde9b 100644 --- a/sync/work/build.gradle.kts +++ b/sync/work/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(libs.hilt.ext.work) implementation(projects.core.analytics) implementation(projects.core.data) + implementation(libs.guava.android) prodImplementation(libs.firebase.cloud.messaging) prodImplementation(platform(libs.firebase.bom)) From 1d91231f49a9774e32e87516edaa579769721cd4 Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Mon, 4 Mar 2024 17:07:15 -0800 Subject: [PATCH 33/37] Remove kotlinx-coroutines-guava dependency from :sync:work Change-Id: Ic5abbd36436a7d8c6382cb02b4ebd7538f8ae1f6 --- gradle/libs.versions.toml | 2 -- sync/work/build.gradle.kts | 2 -- 2 files changed, 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7f00830a..17d482a75 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,6 @@ firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" googleOss = "17.0.1" googleOssPlugin = "0.10.6" -guavaAndroid = "33.0.0-android" hilt = "2.51" hiltExt = "1.1.0" jacoco = "0.8.7" @@ -109,7 +108,6 @@ firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashly firebase-performance = { group = "com.google.firebase", name = "firebase-perf-ktx" } google-oss-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "googleOss" } google-oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "googleOssPlugin" } -guava-android = { group = "com.google.guava", name = "guava", version.ref = "guavaAndroid" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } diff --git a/sync/work/build.gradle.kts b/sync/work/build.gradle.kts index f949dde9b..97e3eace2 100644 --- a/sync/work/build.gradle.kts +++ b/sync/work/build.gradle.kts @@ -34,13 +34,11 @@ dependencies { implementation(libs.hilt.ext.work) implementation(projects.core.analytics) implementation(projects.core.data) - implementation(libs.guava.android) prodImplementation(libs.firebase.cloud.messaging) prodImplementation(platform(libs.firebase.bom)) androidTestImplementation(libs.androidx.work.testing) androidTestImplementation(libs.hilt.android.testing) - androidTestImplementation(libs.kotlinx.coroutines.guava) androidTestImplementation(projects.core.testing) } From 3fa75a037ab63243ef09488d6410dd7fa6a0b6a6 Mon Sep 17 00:00:00 2001 From: kim-yongsuk Date: Tue, 5 Mar 2024 12:30:06 +0900 Subject: [PATCH 34/37] Apply Composable Parameter Ordering Guidelines Change-Id: Id456e91b5ce8bd5807c40df67c3deac008c61670 --- .../core/designsystem/component/Navigation.kt | 12 ++-- .../component/scrollbar/AppScrollbars.kt | 4 +- .../component/scrollbar/Scrollbar.kt | 6 +- .../feature/search/SearchScreen.kt | 67 ++++++++++--------- 4 files changed, 45 insertions(+), 44 deletions(-) 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 9f8f71f14..59f4f48a2 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 @@ -53,12 +53,12 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme fun RowScope.NiaNavigationBarItem( selected: Boolean, onClick: () -> Unit, - icon: @Composable () -> Unit, modifier: Modifier = Modifier, - selectedIcon: @Composable () -> Unit = icon, enabled: Boolean = true, - label: @Composable (() -> Unit)? = null, alwaysShowLabel: Boolean = true, + icon: @Composable () -> Unit, + selectedIcon: @Composable () -> Unit = icon, + label: @Composable (() -> Unit)? = null, ) { NavigationBarItem( selected = selected, @@ -117,12 +117,12 @@ fun NiaNavigationBar( fun NiaNavigationRailItem( selected: Boolean, onClick: () -> Unit, - icon: @Composable () -> Unit, modifier: Modifier = Modifier, - selectedIcon: @Composable () -> Unit = icon, enabled: Boolean = true, - label: @Composable (() -> Unit)? = null, alwaysShowLabel: Boolean = true, + icon: @Composable () -> Unit, + selectedIcon: @Composable () -> Unit = icon, + label: @Composable (() -> Unit)? = null, ) { NavigationRailItem( selected = selected, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt index c8102073a..1086e280b 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt @@ -75,10 +75,10 @@ private const val SCROLLBAR_INACTIVE_TO_DORMANT_TIME_IN_MS = 2_000L */ @Composable fun ScrollableState.DraggableScrollbar( - modifier: Modifier = Modifier, state: ScrollbarState, orientation: Orientation, onThumbMoved: (Float) -> Unit, + modifier: Modifier = Modifier, ) { val interactionSource = remember { MutableInteractionSource() } Scrollbar( @@ -105,9 +105,9 @@ fun ScrollableState.DraggableScrollbar( */ @Composable fun ScrollableState.DecorativeScrollbar( - modifier: Modifier = Modifier, state: ScrollbarState, orientation: Orientation, + modifier: Modifier = Modifier, ) { val interactionSource = remember { MutableInteractionSource() } Scrollbar( diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt index 8c85e5be5..002f36b31 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt @@ -195,13 +195,13 @@ internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) { */ @Composable fun Scrollbar( - modifier: Modifier = Modifier, orientation: Orientation, state: ScrollbarState, - minThumbSize: Dp = 40.dp, + modifier: Modifier = Modifier, interactionSource: MutableInteractionSource? = null, - thumb: @Composable () -> Unit, + minThumbSize: Dp = 40.dp, onThumbMoved: ((Float) -> Unit)? = null, + thumb: @Composable () -> Unit, ) { // Using Offset.Unspecified and Float.NaN instead of null // to prevent unnecessary boxing of primitives diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt index d05f02b22..ca159c80b 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt @@ -100,10 +100,10 @@ import com.google.samples.apps.nowinandroid.feature.search.R as searchR @Composable internal fun SearchRoute( - modifier: Modifier = Modifier, onBackClick: () -> Unit, onInterestsClick: () -> Unit, onTopicClick: (String) -> Unit, + modifier: Modifier = Modifier, bookmarksViewModel: BookmarksViewModel = hiltViewModel(), interestsViewModel: InterestsViewModel = hiltViewModel(), searchViewModel: SearchViewModel = hiltViewModel(), @@ -114,36 +114,36 @@ internal fun SearchRoute( val searchQuery by searchViewModel.searchQuery.collectAsStateWithLifecycle() SearchScreen( modifier = modifier, - onBackClick = onBackClick, - onClearRecentSearches = searchViewModel::clearRecentSearches, - onFollowButtonClick = interestsViewModel::followTopic, - onInterestsClick = onInterestsClick, + searchQuery = searchQuery, + recentSearchesUiState = recentSearchQueriesUiState, + searchResultUiState = searchResultUiState, onSearchQueryChanged = searchViewModel::onSearchQueryChanged, onSearchTriggered = searchViewModel::onSearchTriggered, - onTopicClick = onTopicClick, + onClearRecentSearches = searchViewModel::clearRecentSearches, onNewsResourcesCheckedChanged = forYouViewModel::updateNewsResourceSaved, onNewsResourceViewed = { bookmarksViewModel.setNewsResourceViewed(it, true) }, - recentSearchesUiState = recentSearchQueriesUiState, - searchQuery = searchQuery, - searchResultUiState = searchResultUiState, + onFollowButtonClick = interestsViewModel::followTopic, + onBackClick = onBackClick, + onInterestsClick = onInterestsClick, + onTopicClick = onTopicClick, ) } @Composable internal fun SearchScreen( modifier: Modifier = Modifier, - onBackClick: () -> Unit = {}, + searchQuery: String = "", + recentSearchesUiState: RecentSearchQueriesUiState = RecentSearchQueriesUiState.Loading, + searchResultUiState: SearchResultUiState = SearchResultUiState.Loading, + onSearchQueryChanged: (String) -> Unit = {}, + onSearchTriggered: (String) -> Unit = {}, onClearRecentSearches: () -> Unit = {}, - onFollowButtonClick: (String, Boolean) -> Unit = { _, _ -> }, - onInterestsClick: () -> Unit = {}, onNewsResourcesCheckedChanged: (String, Boolean) -> Unit = { _, _ -> }, onNewsResourceViewed: (String) -> Unit = {}, - onSearchQueryChanged: (String) -> Unit = {}, - onSearchTriggered: (String) -> Unit = {}, + onFollowButtonClick: (String, Boolean) -> Unit = { _, _ -> }, + onBackClick: () -> Unit = {}, + onInterestsClick: () -> Unit = {}, onTopicClick: (String) -> Unit = {}, - searchQuery: String = "", - recentSearchesUiState: RecentSearchQueriesUiState = RecentSearchQueriesUiState.Loading, - searchResultUiState: SearchResultUiState = SearchResultUiState.Loading, ) { TrackScreenViewEvent(screenName = "Search") Column(modifier = modifier) { @@ -177,8 +177,8 @@ internal fun SearchScreen( is SearchResultUiState.Success -> { if (searchResultUiState.isEmpty()) { EmptySearchResultBody( - onInterestsClick = onInterestsClick, searchQuery = searchQuery, + onInterestsClick = onInterestsClick, ) if (recentSearchesUiState is RecentSearchQueriesUiState.Success) { RecentSearchesBody( @@ -192,14 +192,14 @@ internal fun SearchScreen( } } else { SearchResultBody( + searchQuery = searchQuery, topics = searchResultUiState.topics, - onFollowButtonClick = onFollowButtonClick, - onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged, - onNewsResourceViewed = onNewsResourceViewed, + newsResources = searchResultUiState.newsResources, onSearchTriggered = onSearchTriggered, onTopicClick = onTopicClick, - newsResources = searchResultUiState.newsResources, - searchQuery = searchQuery, + onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged, + onNewsResourceViewed = onNewsResourceViewed, + onFollowButtonClick = onFollowButtonClick, ) } } @@ -210,8 +210,8 @@ internal fun SearchScreen( @Composable fun EmptySearchResultBody( - onInterestsClick: () -> Unit, searchQuery: String, + onInterestsClick: () -> Unit, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -286,14 +286,14 @@ private fun SearchNotReadyBody() { @Composable private fun SearchResultBody( + searchQuery: String, topics: List, newsResources: List, - onFollowButtonClick: (String, Boolean) -> Unit, - onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, - onNewsResourceViewed: (String) -> Unit, onSearchTriggered: (String) -> Unit, onTopicClick: (String) -> Unit, - searchQuery: String = "", + onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, + onFollowButtonClick: (String, Boolean) -> Unit, ) { val state = rememberLazyStaggeredGridState() Box( @@ -392,9 +392,9 @@ private fun SearchResultBody( @Composable private fun RecentSearchesBody( + recentSearchQueries: List, onClearRecentSearches: () -> Unit, onRecentSearchClicked: (String) -> Unit, - recentSearchQueries: List, ) { Column { Row( @@ -444,11 +444,11 @@ private fun RecentSearchesBody( @Composable private fun SearchToolbar( - modifier: Modifier = Modifier, - onBackClick: () -> Unit, + searchQuery: String, onSearchQueryChanged: (String) -> Unit, - searchQuery: String = "", onSearchTriggered: (String) -> Unit, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -473,8 +473,8 @@ private fun SearchToolbar( @OptIn(ExperimentalComposeUiApi::class) @Composable private fun SearchTextField( - onSearchQueryChanged: (String) -> Unit, searchQuery: String, + onSearchQueryChanged: (String) -> Unit, onSearchTriggered: (String) -> Unit, ) { val focusRequester = remember { FocusRequester() } @@ -556,6 +556,7 @@ private fun SearchTextField( private fun SearchToolbarPreview() { NiaTheme { SearchToolbar( + searchQuery = "", onBackClick = {}, onSearchQueryChanged = {}, onSearchTriggered = {}, From 77cbdb62a438487c9a8fa843dbe47a7f262ef90a Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 5 Mar 2024 11:33:45 +0000 Subject: [PATCH 35/37] Remove unused release variable. Fixes #1231 Change-Id: I8c03d3d2e1aee85a22d1a03b97e3bec4069e0fcb --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d674f4bec..f377e0a33 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -45,7 +45,7 @@ android { debug { applicationIdSuffix = NiaBuildType.DEBUG.applicationIdSuffix } - val release = getByName("release") { + release { isMinifyEnabled = true applicationIdSuffix = NiaBuildType.RELEASE.applicationIdSuffix proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") From a96526a79b7dc01e0c9d25a90c92cac21382c164 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 19:07:43 +0000 Subject: [PATCH 36/37] Bump gradle/wrapper-validation-action from 1 to 2 Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1 to 2. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1...v2) --- updated-dependencies: - dependency-name: gradle/wrapper-validation-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/Build.yaml | 2 +- .github/workflows/Release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 2a1c60c8d..001140a87 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index f738ae105..7de3cb11e 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties From ab9033421017962c5af6d7a1dd221997439210da Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Wed, 6 Mar 2024 08:38:17 +0000 Subject: [PATCH 37/37] Disable animations of instrumented tests (#1167) https://developer.android.com/reference/tools/gradle-api/com/android/build/api/dsl/TestOptions#animationsDisabled() --- .../src/main/kotlin/AndroidApplicationConventionPlugin.kt | 2 ++ .../src/main/kotlin/AndroidFeatureConventionPlugin.kt | 1 + .../src/main/kotlin/AndroidLibraryConventionPlugin.kt | 1 + 3 files changed, 4 insertions(+) diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt index cd8bcfeb0..f4d5bb0d0 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -39,6 +39,8 @@ class AndroidApplicationConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) defaultConfig.targetSdk = 34 + @Suppress("UnstableApiUsage") + testOptions.animationsDisabled = true configureGradleManagedDevices(this) } extensions.configure { diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index b8699a05d..52c337521 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -34,6 +34,7 @@ class AndroidFeatureConventionPlugin : Plugin { testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" } + testOptions.animationsDisabled = true configureGradleManagedDevices(this) } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index d442d94ef..be5b41d07 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -40,6 +40,7 @@ class AndroidLibraryConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) defaultConfig.targetSdk = 34 + testOptions.animationsDisabled = true configureFlavors(this) configureGradleManagedDevices(this) // The resource prefix is derived from the module name,