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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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/62] 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 5403d8054c0db1fb5c2e47d8950b5ecb15d971fd Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 27 Feb 2024 14:59:20 -0800 Subject: [PATCH 23/62] Update Compose versions --- gradle/libs.versions.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d59879623..7c3190f05 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,8 +7,8 @@ androidTools = "31.2.2" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" -androidxComposeBom = "2024.02.00" -androidxComposeCompiler = "1.5.7" +androidxComposeBom = "2024.02.01" +androidxComposeCompiler = "1.5.10" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" @@ -83,6 +83,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } +androidx-compose-compiler = { group = "androidx.compose.compiler", name = "compiler", version.ref = "androidxComposeCompiler" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } From 92981961a10ac69f9b97fa5fe0997fbbc1766af8 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 29 Feb 2024 16:03:47 -0800 Subject: [PATCH 24/62] WIP: Add ktor and retrofit dependencies --- build.gradle.kts | 5 +- core/network/build.gradle.kts | 58 ++++++++++++++----- .../kotlin}/AndroidManifest.xml | 0 .../src/{main => commonMain}/assets/news.json | 0 .../{main => commonMain}/assets/topics.json | 0 .../kotlin/JvmUnitTestFakeAssetManager.kt | 0 .../core/network/NiaNetworkDataSource.kt | 0 .../core/network/di/FlavoredNetworkModule.kt | 2 +- .../core/network/di/NetworkModule.kt | 23 +------- .../core/network/fake/FakeAssetManager.kt | 0 .../network/fake/FakeNiaNetworkDataSource.kt | 0 .../core/network/model/NetworkChangeList.kt | 0 .../core/network/model/NetworkNewsResource.kt | 0 .../core/network/model/NetworkTopic.kt | 0 .../network/retrofit/RetrofitNiaNetwork.kt | 0 gradle/libs.versions.toml | 27 +++++++-- 16 files changed, 72 insertions(+), 43 deletions(-) rename core/network/src/{main => androidMain/kotlin}/AndroidManifest.xml (100%) rename core/network/src/{main => commonMain}/assets/news.json (100%) rename core/network/src/{main => commonMain}/assets/topics.json (100%) rename core/network/src/{main => commonMain}/kotlin/JvmUnitTestFakeAssetManager.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/NiaNetworkDataSource.kt (100%) rename core/network/src/{demo => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt (95%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt (80%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkChangeList.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkTopic.kt (100%) rename core/network/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 094a9eb3d..6e0f9ab17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,7 +46,8 @@ plugins { alias(libs.plugins.roborazzi) apply false alias(libs.plugins.secrets) apply false alias(libs.plugins.room) apply false - alias(libs.plugins.jetbrainsCompose) apply false - alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.jetbrains.compose) apply false + alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.sqldelight.gradle.plugin) apply false + alias(libs.plugins.ktrofit) apply false } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 689a99e73..8edda61fe 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -15,10 +15,12 @@ */ plugins { - alias(libs.plugins.nowinandroid.android.library) + alias(libs.plugins.nowinandroid.kmp.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.kotlin.inject) + alias(libs.plugins.ktrofit) id("kotlinx-serialization") + id("com.google.devtools.ksp") id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") } @@ -38,17 +40,45 @@ secrets { defaultPropertiesFileName = "secrets.defaults.properties" } -dependencies { - api(libs.kotlinx.datetime) - api(projects.core.common) - api(projects.core.model) - - implementation(libs.coil.kt) - implementation(libs.coil.kt.svg) - implementation(libs.kotlinx.serialization.json) - implementation(libs.okhttp.logging) - implementation(libs.retrofit.core) - implementation(libs.retrofit.kotlin.serialization) +kotlin { + sourceSets { + commonMain.dependencies { + api(libs.kotlinx.datetime) + api(projects.core.common) + api(projects.core.model) + implementation(libs.coil.kt) + implementation(libs.coil.kt.svg) + implementation(libs.kotlinx.serialization.json) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.json) + implementation(libs.ktor.client.logging) + implementation(libs.ktor.client.serialization) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktorfit.lib) + implementation(libs.ktorfit.ksp) + } + commonTest.dependencies { + implementation(libs.kotlinx.coroutines.test) + } + androidMain.dependencies { + implementation(libs.ktor.client.android) + } + appleMain.dependencies { + implementation(libs.ktor.client.darwin) + } + jsMain.dependencies { + implementation(libs.ktor.client.js) + } + jvmMain.dependencies { + implementation(libs.ktor.client.java) + } + mingwMain.dependencies { + implementation(libs.ktor.client.winhttp) + } + } +} - testImplementation(libs.kotlinx.coroutines.test) +dependencies { + add("kspCommonMainMetadata", libs.ktorfit.ksp) } diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/androidMain/kotlin/AndroidManifest.xml similarity index 100% rename from core/network/src/main/AndroidManifest.xml rename to core/network/src/androidMain/kotlin/AndroidManifest.xml diff --git a/core/network/src/main/assets/news.json b/core/network/src/commonMain/assets/news.json similarity index 100% rename from core/network/src/main/assets/news.json rename to core/network/src/commonMain/assets/news.json diff --git a/core/network/src/main/assets/topics.json b/core/network/src/commonMain/assets/topics.json similarity index 100% rename from core/network/src/main/assets/topics.json rename to core/network/src/commonMain/assets/topics.json diff --git a/core/network/src/main/kotlin/JvmUnitTestFakeAssetManager.kt b/core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt similarity index 100% rename from core/network/src/main/kotlin/JvmUnitTestFakeAssetManager.kt rename to core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/NiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/NiaNetworkDataSource.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/NiaNetworkDataSource.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/NiaNetworkDataSource.kt diff --git a/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt similarity index 95% rename from core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt index 850b00201..60744eeab 100644 --- a/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt similarity index 80% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index a68683c7c..7a54b0fc9 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -16,36 +16,17 @@ package com.google.samples.apps.nowinandroid.core.network.di -import android.content.Context -import androidx.tracing.trace -import coil.ImageLoader -import coil.decode.SvgDecoder -import coil.util.DebugLogger -import com.google.samples.apps.nowinandroid.core.network.BuildConfig import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent +import de.jensklingenberg.ktorfit.Call import kotlinx.serialization.json.Json -import okhttp3.Call -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import javax.inject.Singleton +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) internal object NetworkModule { - @Provides - @Singleton fun providesNetworkJson(): Json = Json { ignoreUnknownKeys = true } - @Provides - @Singleton fun providesFakeAssetManager( @ApplicationContext context: Context, ): FakeAssetManager = FakeAssetManager(context.assets::open) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkChangeList.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkChangeList.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkChangeList.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkChangeList.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkTopic.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkTopic.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkTopic.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkTopic.kt diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt similarity index 100% rename from core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7c3190f05..c82347cda 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ androidxTracing = "1.3.0-alpha02" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.2.0" androidxWork = "2.9.0" -coil = "2.5.0" +coil = "3.0.0-alpha06" dependencyGuard = "0.4.3" firebaseBom = "32.4.0" firebaseCrashlyticsPlugin = "2.9.9" @@ -73,6 +73,8 @@ junit = "4.13.2" sqldelight = "2.0.1" kotlinInject = '0.6.3' multiplatform-settings = "1.1.1" +ktor = "3.0.0-beta-1" +ktrofit = "1.12.0" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -116,8 +118,9 @@ androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiaut androidx-tracing-ktx = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "androidxTracing" } androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidxWork" } -coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" } -coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } +coil-kt = { group = "io.coil-kt", name = "coil3", version.ref = "coil" } +coil-core = { group = "io.coil-kt", name = "coil-core", version.ref = "coil" } +coil-kt-compose = { group = "io.coil-kt", name = "coil-compose-core", version.ref = "coil" } coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" } firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } @@ -176,6 +179,19 @@ multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settin multiplatform-settings-test = { group = "com.russhwolf", name = "multiplatform-settings-test", version.ref = "multiplatform-settings" } multiplatform-settings-serialization = { group = "com.russhwolf", name = "multiplatform-settings-serialization", version.ref = "multiplatform-settings" } multiplatform-settings-coroutines = { group = "com.russhwolf", name = "multiplatform-settings-coroutines", version.ref = "multiplatform-settings" } +ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } +ktor-client-json = { group = "io.ktor", name = "ktor-client-json", version.ref = "ktor" } +ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } +ktor-client-serialization = { group = "io.ktor", name = "ktor-client-serialization", version.ref = "ktor" } +ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" } +ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" } +ktor-client-darwin = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" } +ktor-client-java = { group = "io.ktor", name = "ktor-client-java", version.ref = "ktor" } +ktor-client-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" } +ktor-client-winhttp = { group = "io.ktor", name = "ktor-client-winhttp", version.ref = "ktor" } +ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktorfit-ksp = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-ksp", version.ref = "ktrofit" } +ktorfit-lib = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-lib", version.ref = "ktrofit" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -204,9 +220,10 @@ protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } -jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } -kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } sqldelight-gradle-plugin = { id = "app.cash.sqldelight", version.ref = "sqldelight" } +ktrofit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktrofit" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From 3f19340c3de4bd11edc147ba2e8130fec03c476e Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 29 Feb 2024 17:30:41 -0800 Subject: [PATCH 25/62] Move news.json and topics.json to the code --- core/network/src/commonMain/assets/news.json | 4581 ---------------- .../network/src/commonMain/assets/topics.json | 154 - .../core/network/assets/SampleData.kt | 4764 +++++++++++++++++ 3 files changed, 4764 insertions(+), 4735 deletions(-) delete mode 100644 core/network/src/commonMain/assets/news.json delete mode 100644 core/network/src/commonMain/assets/topics.json create mode 100644 core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt diff --git a/core/network/src/commonMain/assets/news.json b/core/network/src/commonMain/assets/news.json deleted file mode 100644 index 35df98de3..000000000 --- a/core/network/src/commonMain/assets/news.json +++ /dev/null @@ -1,4581 +0,0 @@ -[ - { - "id": "1", - "title": "Android Dev Summit ’22: Coming to you, online and around the world! ⛰️", - "content": "Android Dev Summit is back, so join us online or in person — for the first time since 2019 — at locations around the world. We’ll be sharing the sessions live on YouTube in three tracks spread across three weeks.", - "url": "https://android-developers.googleblog.com/2022/10/android-dev-summit.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1VWQmqQu6wDswls9f_5NpEQnq4eR57g2NwzWvhKItcKtV6rb_Cyo75XSyL6vvmCIo4tzQn-8taNagEp7QG0KU1L4yMqwbYozNMzBMEFxEN2XintAhy5nLI4RQDaOXr8dgiIFdGOBMdl577Ndelzc0tDBzjI6mz7e4MF8_Tn09KWguZi6I-bS5NbJn/w1200-h630-p-k-no-nu/unnamed%20%2816%29.png", - "publishDate": "2022-10-04T23:00:00.000Z", - "type": "Event 📆", - "topics": [ - "1" - ], - "authors": [ - "64" - ] - }, - { - "id": "2", - "title": "The new Google Pixel Watch is here — start building for Wear OS! ⌚", - "content": "We launched the Google Pixel Watch, powered by Wear OS 3.5, at the Made by Google event — the perfect device to showcase apps built with Compose for Wear OS. With Compose for Wear OS, the Tiles Material library, and the tools in Android Studio Dolphin, it’s now simpler and more efficient than ever to make apps for WearOS.", - "url": "https://android-developers.googleblog.com/2022/10/the-new-google-pixel-watch-is-here.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH63icac2kmydOI8Fs2I09KiuRA3GUo2pfZ1Wpf0M5JIEoVQ8dj9LYSl8jpxSQlmlsUVXoeXbwN4UbHMCf5p0M7FHh_EXzMeFRAJ-6feI9-7eIyhBmtGZSD5o-MItwFLH_ESi15Cxd01AlznWaGy9WDqhK0NWtMQwiWELg3xE1I7hba-_7eVqs747V/w1200-h630-p-k-no-nu/WhasNewinPixelDevices_Social.png", - "publishDate": "2022-10-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1", - "3", - "19" - ], - "authors": [] - }, - { - "id": "3", - "title": "Listen to our major Text to Speech upgrades for 64 bit devices 💬", - "content": "An upgrade to Speech Services by Google is rolling out to 64-bit Android devices via Google Play over the next few weeks, providing clearer, more natural voices. All 421 voices in 67 languages are being upgraded with a new voice model and synthesizer. The post includes more on this update, including demonstrations of some voice upgrades, along with guidance on how to use text to speech in your projects.", - "url": "https://android-developers.googleblog.com/2022/09/listen-to-our-major-text-to-speech-upgrades-for-64-bit-devices.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrHro6d3BTw7ZZ4IXgfb6_8aESB7-SsWfelDSSInZVamiMSnYpBZzGBaZBBrWxWwYgLqOHuOtroGvGjxrwzdUkhjwuIvM1u6chIblGKS1gQ6JVkjXr-Vztyk2zoYb1ylvhNgLgC5q6M-7LaiXT1xnAT96DvkPx89APNb8JEaz-1mnMRcfaOYYBHzQL/w1200-h630-p-k-no-nu/Text%20to%20Speech%20-%20Social%20-%201024x512.png", - "publishDate": "2022-09-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "14" - ], - "authors": [ - "66", - "67" - ] - }, - { - "id": "4", - "title": "MAD Skills Compose: Powerful Toolkit", - "content": "Learn about the powerful toolkit of UI components that ship with Compose enabling you to build rich UIs and interactions.", - "url": "https://medium.com/androiddevelopers/compose-toolkit-8d3651228764", - "headerImageUrl": "https://miro.medium.com/max/1200/1*3FZeNmAPZDYUCmgL0cBXoA.png", - "publishDate": "2022-09-29T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "68" - ] - }, - { - "id": "5", - "title": "MAD Skills Compose: Accelerate development with tooling", - "content": "Learn how to accelerate your Compose development process with a dive into Android Studio tooling support, including live templates, gutter icons for drawables and colors, composable preview functions, multipreview, preview on device, live edits of literals, and the Layout Inspector.", - "url": "https://medium.com/androiddevelopers/compose-tooling-42621bd8719b", - "headerImageUrl": "https://miro.medium.com/max/1200/1*WVUBQsNgePqQxmrHjaID4g.png", - "publishDate": "2022-10-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "68" - ] - }, - { - "id": "6", - "title": "Deep Links Crash Course: Part 3 - Troubleshooting", - "content": "In part 3 of the Deep Links Crash Course you'll learn how to diagnose and debug common issues with deep links using command line tools and the Android Debug Bridge (ADB).", - "url": "https://www.youtube.com/watch?v=OAxJ2kWG4ZI", - "headerImageUrl": "https://i.ytimg.com/vi/OAxJ2kWG4ZI/maxresdefault.jpg", - "publishDate": "2022-09-29T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2", - "5" - ], - "authors": [] - }, - { - "id": "7", - "title": "Deep Links Crash Course: Part 4 - Deep links for your business", - "content": "Part 4 of the Deep Links Crash Course explores Deep links for your business, covering the importance and benefits of implementing deep links for your app, your users, and your business, including success stories and how App Links provide an optimal experience for users.", - "url": "https://www.youtube.com/watch?v=UvMIswgsJF8", - "headerImageUrl": "https://i.ytimg.com/vi/UvMIswgsJF8/maxresdefault.jpg", - "publishDate": "2022-10-05T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2", - "5" - ], - "authors": [] - }, - { - "id": "8", - "title": "Migrating the AOSP QuickSearchBox App to Kotlin", - "content": "This article includes the methodology used in the migration to Kotlin, how manual changes were addressed, and what the impact to APK size and build speed was.", - "url": "https://medium.com/androiddevelopers/migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec", - "headerImageUrl": "https://miro.medium.com/max/720/1*cWnPe-kD4hAVuH3IIcNUcA.png", - "publishDate": "2022-09-22T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "10" - ], - "authors": [ - "69" - ] - }, - { - "id": "9", - "title": "Prepare your Android Project for Android Gradle plugin 8.0 API changes", - "content": "How to prepare your Android Project for Android Gradle plugin 8.0 API changes; this article specifically addresses migrating from the Transform APIs — which slow down builds and will be removed in 8.0 — to the Artifacts API and Instrumentation API.", - "url": "https://android-developers.googleblog.com/2022/10/prepare-your-android-project-for-agp8-changes.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy74acii7wq-Z2pW8TUSga1YGpRKjLZjroOlZlUillRJuTFIlGpUi93PPletKlkcAaz9u6NgF8_LejO9686VYEWNtO2ypawLgpY2QW7JMtrMSVTlPsRGgEDUiQJKUfzEXw2Q_Y7qX1CSUlH9lma8Jjdm3AqMogbEI6PScD3AK1XsWgHmVeqJlVqUiK/w1200-h630-p-k-no-nu/Header-PrepareyourAndroidProjectforAGP8.0Changes%20.png", - "publishDate": "2022-10-05T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5", - "5" - ], - "authors": [ - "70", - "71", - "72" - ] - }, - { - "id": "10", - "title": "Optimize for Android (Go edition) : Lessons from Google apps Part 2", - "content": "Part two of the Optimize for Android Go : Lessons from Google apps blog series, covering strategies Google apps used to improve startup latency and optimize app size — things that will improve the user experience for any app.", - "url": "https://android-developers.googleblog.com/2022/09/optimize-for-android-go-lessons-from-google-apps-part-2.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD7uoBIqlA_WYXwuhyDxKy1Nt2ibaa_GYd9l8ewfQcC7f-f11t0WRCTTS6XhwnkJMqWUxSX-nxAq9DD8oBbk_Om2ik0yNMOV8lHw0sGmRAixLY2T0dxpKtQe0DnsVrzmexNSX1-BYdz9p0cvCtdXoxgDi1Mx6OLixzC5JAnxEEAf1TkHrTQON0fURg/w1200-h630-p-k-no-nu/MAD%20App%20Architecture%20launch%20-%20Mobile%20%281%29%20%281%29.png", - "publishDate": "2022-09-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "73" - ] - }, - { - "id": "11", - "title": "Helping users discover apps for all their devices from their phone", - "content": "Changes in Google Play are helping users discover apps for all their devices from their phone, including homepages for non-phone devices, a device search filter, and the ability to remotely install an app to another device.", - "url": "https://android-developers.googleblog.com/2022/09/helping-users-discover-apps-for-all-their-devices-from-their-phone.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyGaErzRykqFDWOwEmkHWos7vU7OhdETz5GNEjObA7FOhCudnf5DiQ1hAfpxuq102pwxuBf_ZCeifURihNeAwNnLj46pkdoBdbl_JYn8A9plqwaqS8D_0XML6B7Bupt0GhPZuABbfTXB_nkWsVVW6faVQXqpetHIV6QlNQyl1WD6zuojFf-U7wDSHO/w1200-h630-p-k-no-nu/image3.gif", - "publishDate": "2022-09-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [ - "74" - ] - }, - { - "id": "12", - "title": "Deep Links Crash Course : Part 3 Troubleshooting Your Deep Links", - "content": "The Deep Links Crash Course continues with Summers writing an article on troubleshooting deep links. He goes over common issues that can occur with deep links and how to solve them.", - "url": "https://medium.com/androiddevelopers/deep-links-crash-course-part-3-troubleshooting-your-deep-links-61329fecb93", - "headerImageUrl": "https://miro.medium.com/max/1200/1*IsRqP0Fe9I6YLxrJybIG6Q.png", - "publishDate": "2022-09-15T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "6" - ], - "authors": [ - "79" - ] - }, - { - "id": "13", - "title": "Jetpack Compose Composition Tracing", - "content": "Ben covered Compose Composition Tracing, a new feature that allows showing Jetpack Compose composable functions in the Android Studio Flamingo system trace profiler. This feature gives you the low intrusiveness from system tracing, with method tracing levels of detail in composition. This is great for checking your Compose app’s performance and working out why your app may not be performing as you expect. Learn more about this feature in the post!", - "url": "https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535", - "headerImageUrl": "https://miro.medium.com/max/700/1*jPVPY4GjPRK3prnJ2o09cQ.png", - "publishDate": "2022-09-19T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "7" - ], - "authors": [ - "80" - ] - }, - { - "id": "14", - "title": "Android Studio: Dolphin is available", - "content": "Android Studio Dolphin is here! In this post, Takeshi goes over the three key themes: Jetpack Compose, Wear OS, and development productivity. Exciting features include the Compose Animation Inspector, the Wear OS Emulator Pairing Assistant, and Gradle managed virtual devices. Learn about all the new features in the blog post or the video!", - "url": "https://www.youtube.com/watch?v=EQ_btxhpRzU&t=1s", - "headerImageUrl": "https://i.ytimg.com/vi/EQ_btxhpRzU/maxresdefault.jpg", - "publishDate": "2022-09-14T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5", - "5" - ], - "authors": [ - "81" - ] - }, - { - "id": "15", - "title": "Mad Skills: Compose", - "content": "Chris started a brand new MAD Skills series on Compose. This series is a great place to start to learn how to think and start building apps with Compose.", - "url": "https://www.youtube.com/watch?v=4UXJTeb9Khg&t=1s", - "headerImageUrl": "https://i.ytimg.com/vi/4UXJTeb9Khg/maxresdefault.jpg", - "publishDate": "2022-09-12T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3" - ], - "authors": [ - "68" - ] - }, - { - "id": "16", - "title": "Introducing Compose Camp", - "content": "We launched Compose Camp, a series of in-person and virtual sessions where you can learn how to build Android apps with Jetpack Compose alongside your peers. Compose Camp has two tracks: the beginner track caters to complete Android beginners including people that have no coding experience, and the experienced track is for Android developers who want to learn how to migrate to Compose and stop using XML.", - "url": "https://android-developers.googleblog.com/2022/09/learn-jetpack-compose-at-compose-camp-near-you.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn6__UOZ_lipXjY09TrHeXW4HjKodPUdFzmovYRn1tLwdYr8GVKjCN6wfgKpcby5rrJ6JFrUmZozT7aeDkp8x7v46QdH6wtz9ysQFTZAQPwswFfGWQkWcPmKHbeELq_BUUhazt4ppYm9ErYEo7HbFzPCfBid4IQ9qL-hydSgRpJx7w2lNZKh5ylNcb/w1200-h630-p-k-no-nu/Compose%20Camp%203.png", - "publishDate": "2022-09-12T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1", - "3" - ], - "authors": [ - "78" - ] - }, - { - "id": "17", - "title": "Privacy Sandbox developer preview 5 🔐", - "content": "The Privacy Sandbox aims to develop new technologies that improve user privacy and enable effective, personalized advertising experiences for mobile apps. Developer Preview 5 was released, this version is a major milestone that will become the foundation for upcoming Privacy Sandbox Beta releases. Please keep giving us your feedback! See what’s changed in the blog post.", - "url": "https://android-developers.googleblog.com/2022/09/privacy-sandbox-developer-preview-5-is-here.html", - "headerImageUrl": "https://services.google.com/fb/forms/privacysandbox/fb/forms/getlogo/5679849861677056/", - "publishDate": "2022-09-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11" - ], - "authors": [ - "77" - ] - }, - { - "id": "18", - "title": "Guide to app modularization 🧩", - "content": "The team just released new guidance on modularization. Guidance on this topic has been one of the top community requests and here it is! The guide is split into two parts. The overview page gives you a high level, theoretical overview of the matter and its benefits. The common modularization patterns page dives deep into practical examples in the context of modern Android architecture. Take a look at the guide announcement blog post to learn more about this.", - "url": "https://android-developers.googleblog.com/2022/09/announcing-new-guide-to-android-app-modularization.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0ZsQN4PZ_SQ975Hfbc-LDFXMTgOr6RVf42kSUBqNxfv9OcDvc6dTYRZPynsYx0JIlXT7k5TMz_Kjq7bJstYb4dUy2ZX6ilugMH20JudIZISLWEsa19f8sN0hDxA7JWXgS570gDxkXNp3ioHxxH42tvquQ0wUK-qPS6Qv2OeGK06HhumP2vvC0V07V/s1600/Android-AppModularization_4209x1253.png", - "publishDate": "2022-09-05T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "82" - ] - }, - { - "id": "19", - "title": "Build an offline-first app", - "content": "If you need to make your app work offline, we got you covered. The new Build an offline-first app guide helps you design your app to properly handle reads and writes, and deal with synchronization and conflict resolution in a device with no Internet connectivity.", - "url": "https://developer.android.com/topic/architecture/data-layer/offline-first", - "headerImageUrl": "https://developer.android.com/static/images/topic/architecture/data-layer/data-layer.png", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "20", - "title": "State holders and UI state page ", - "content": "Another new guide is the State holders and UI state page in the UI layer docs. Not everything needs to be present in ViewModel classes. This page goes through the different types of state holders you can find in the UI layer and what their responsibilities are.", - "url": "https://developer.android.com/topic/architecture/ui-layer/stateholders", - "headerImageUrl": "https://developer.android.com/static/images/topic/architecture/ui-layer/udf.png", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "4", - "3" - ], - "authors": [] - }, - { - "id": "21", - "title": "Architecture pathway", - "content": "If you want to learn all about Architecture and be up-to-date with our current best practices, check out the Architecture pathway, that got updated with all the videos of the Architecture MAD Skills series we produced early this year and the new docs.", - "url": "https://developer.android.com/courses/pathways/android-architecture", - "headerImageUrl": "https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-overview.png", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Codelab", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "22", - "title": "Mad Skills: Performance ⚡️", - "content": "Ben wrote this blog post that contains a summary of all the videos on MAD Skills: Performance! Don't miss it out!", - "url": "https://medium.com/androiddevelopers/mad-skills-performance-wrap-up-33688abfc51f", - "headerImageUrl": "https://miro.medium.com/max/720/0*qdkZp112bKTGtQIN", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "36" - ] - }, - { - "id": "23", - "title": "AndroidX releases 🚀", - "content": "Since the previous episode, there are some AndroidX releases worth highlighting.Core and core-ktx made it to 1.9.0 stable. This version improves compatibility with Android 13 adding parity between the accessibility framework and compat APIs, and some other additions. Other releases include new in beta Car app 1.3, and new in alpha Navigation 2.6 and Test Ui Automator 2.3.", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "24", - "title": "Precise Improvements: How TikTok Enhanced its Video Social Experience on Android", - "content": "The Developer Relations team wrote about how TikTok Enhanced its Video Social Experience on Android. They were able to significantly improve their overall performance by following Android’s performance guidance, and employing their deep understanding of development tools such as Android Gradle Plugin and Jetpack libraries. Read more here!", - "url": "https://android-developers.googleblog.com/2022/08/precise-improvements-how-tiktok-enhanced-its-social-experience-on-android.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvwJsvRHyECCaiD1FaBBCLGrUr-PoZoDaqm9aUKoswBFPOlWyKNvcC94FhX6M6Ugdo0wVTdZyI48BUmKaiA1xSgOcEE_xOFt9EGmoHd9PDHyJ4mAiKrfjnFHBIEKgjL1JhFeTQWbjWec4DJX-q9lnYAw5b9l0vC7nM09QBKtItv7JmBNxjYosCroQI/s1600/241588700__38488906__148018.png", - "publishDate": "2022-09-13T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [] - }, - { - "id": "25", - "title": "The deep links crash course, Part 1: Introduction to deep links", - "content": "Sabs started a crash course series about deep links. The first part is an introduction to deep links with a blog post and a video. Get to know what a deep link is, go from URIs to app links, and more!", - "url": "https://medium.com/androiddevelopers/the-deep-links-crash-course-part-1-introduction-to-deep-links-2189e509e269", - "headerImageUrl": "https://miro.medium.com/max/720/1*m44rS8zc3W23lmDy1_Vu8g.png", - "publishDate": "2022-09-01T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "83" - ] - }, - { - "id": "26", - "title": "Experimenting with Jetpack Glance", - "content": "Marcel wrote about experimenting with Jetpack Glance that covers a standalone experimental repository to supplement Jetpack Glance with tools that are commonly required for development but not yet available. At the moment, it includes a composable to display RemoteViews inside your app (enabling Live Edit), a debug tool to view and interact with AppWidget snapshots embedded inside the app, and a Material3 Scaffold for AppWidgets.", - "url": "https://medium.com/androiddevelopers/experimenting-with-jetpack-glance-35fbffe520f4", - "headerImageUrl": "https://miro.medium.com/max/720/0*gfm9zG95iVoEX5Gu", - "publishDate": "2022-08-31T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "2" - ], - "authors": [ - "9" - ] - }, - { - "id": "27", - "title": "Jetpack Compose: Debugging Recomposition", - "content": "Ben Trengove wrote about Debugging recomposition in Compose. Check it out because it also contains a screencast of Ben fixing a performance issue in Jetsnack, a Compose sample. For this, Ben also uses the layout inspector in Android Studio where you can see the recomposition and skip counts of composable functions.", - "url": "https://medium.com/androiddevelopers/jetpack-compose-debugging-recomposition-bfcf4a6f8d37", - "headerImageUrl": "https://miro.medium.com/max/720/1*gwdtRcu1bo_PoH8rwh5E4A.png", - "publishDate": "2022-09-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7", - "3", - "5", - "5" - ], - "authors": [ - "80" - ] - }, - { - "id": "28", - "title": "Optimize for Android (Go edition): Lessons from Google apps - Part 1", - "content": "Nikariha started another blog post series about optimizing for Android Go edition. The first part introduces Android Go edition, why you’d want to build for it, and some best practices based on experience building the Gboard and Camera from Google apps.", - "url": "https://android-developers.googleblog.com/2022/09/optimize-for-android-go-lessons-from-google-apps-part-1.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT34hXV07gVlKKi5X9mjpDGRlawITJfAKr7BpE7E02gtIYVqxYW8RoyjX_SPWJo0KS4PcBNy9rqITsAx0UnXeZp0V6zEoldaBCy9FJ9wyyebLEpPoxJgT6BENWxJqpIrihbpcwUsXO45qhcDAJJ3zTldnKkT8Dw_5VGxl2xYTA2trIVGsczYZLJgKj/s1600/MAD%20App%20Architecture%20launch%20-%20header%20%282%29.png", - "publishDate": "2022-09-07T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "73" - ] - }, - { - "id": "29", - "title": "ADB Podcast Episodes🎙: Episode 188 - Android 13", - "content": "In Episode 188: Android 13, Chet, Romain, and Tor talk about some of their favorite new features and changes of the new version of Android, both for users and developers.", - "url": "https://adbackstage.libsyn.com/episode-188-android-13", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/4/6/e/5/46e518b4880184c288c4a68c3ddbc4f2/ADB_188_Android_13.jpg", - "publishDate": "2022-08-31T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "13" - ], - "authors": [ - "31" - ] - }, - { - "id": "30", - "title": "Cross device SDK Developer Preview 📱↔️📱", - "content": "We launched the Cross device SDK for Android developer preview, which allows you to build rich multi-device experiences, abstracting away the intricacies involved with working with device discovery, authentication, and connection protocols.", - "url": "https://android-developers.googleblog.com/2022/07/announcing-cross-device-SDK-Developer-Preview-for-Android.html", - "headerImageUrl": "https://miro.medium.com/max/1280/0*1CDepdsU40_03H5K.png", - "publishDate": "2022-08-25T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8", - "6" - ], - "authors": [ - "84" - ] - }, - { - "id": "31", - "title": "CameraX 1.2 is now in Beta", - "content": "CameraX version 1.2 is now in Beta. It introduces a zero shutter lag capture mode along with MlKitAnalyzer, an implementation of ImageAnalysis.Analyzer that handles much of the ML Kit setup for you. MlKitAnalyzer works with both cameraController and cameraProvider workflows and can even handle coordinate transformations between ML Kit output and your PreviewView. Zero shutter lag greatly reduces image capture lag on supported devices by using a circular buffer of captures to get the frame closest to the actual press of the shutter button.", - "url": "https://android-developers.googleblog.com/2022/08/camerax-12-is-now-in-beta.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS_SSchtaoz90hvgXHZQzwD61bSnd06zOvd7L2sLG-isR8ykrzy7Afk1snnZjCBVkNtMXrmCJIMJfp-gP3X3NMXSbPdVvEgmpqSfTIph-vc_QkBVPDgH8ZQonnMu-XY5Aasi4tp1nmI5jetU2eF4TK_AMOWqA0gLxadk-0dPt2wjpruoDOhxP4PhE_/w681-h202/Android_NewCameraXInBeta_4209x1253.png", - "publishDate": "2022-08-23T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [ - "85" - ] - }, - { - "id": "32", - "title": "Build Tiles fast with the WearOS Material Tiles Library ⌚", - "content": "Tiles are one of the most used surfaces on Wear OS, providing users glanceable access to the information and actions they need to get things done quickly. We launched the Tiles Material library allowing you to use pre-built Material components such as Button, Chip, CompactChip, TitleChip, CircularProgressIndicator, and Text along with layouts such as PrimaryLayout, EdgeContentLayout, MultiButtonLayout, and MultiSlotLayout to create tiles that embrace the latest Material design for Wear OS. Together with the Tiles Design Kit, they help you to easily follow the Tiles Design Guidelines.", - "url": "https://android-developers.googleblog.com/2022/08/wear-os-tiles-material-library-build-tiles-fast.html", - "headerImageUrl": "https://miro.medium.com/max/1278/0*tgTI6u6xZZFHhvSc.png", - "publishDate": "2022-08-23T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "2" - ], - "authors": [ - "16", - "86" - ] - }, - { - "id": "33", - "title": "New deep links monitor in Play Console 🔗", - "content": "Deep links allow you to get your users directly to in-app content by accepting traffic from external sources, including the web. Since answering basic questions like “is this URL deep-linked?” or “why is this deep link not working?” can be difficult to answer, many apps have partial, broken, or no deep links configured. To make it easier for you to keep your deep links in good shape, we’ve introduced a new, dedicated Play Console page that gives you a quick but comprehensive snapshot of your current setup along with tooling to help you identify and troubleshoot issues at a glance.", - "url": "https://android-developers.googleblog.com/2022/08/monitor-your-deep-links-in-one-place.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj42yJ079EEgbm1oAfoXwCKB_LxBw_0iEPsixWd572w6BThCaA5P-O6Ahp7P6SCcCgLZ59rKPtQFkfbpGeVn-f7dk2ef81nSMMqHz3IEw1FL9fAfhiFGgPJZNu5wny2AoWWZ0Ma1PAqGkSGS60eGB59abQHdQ_Hb-_9VdEnS7yg4JLmUIUuW3dNxg0l/w640-h190/Android-DeepLinksWithGooglePlayConsole_4209x1253%20.png", - "publishDate": "2022-08-21T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12", - "2", - "5" - ], - "authors": [ - "87", - "88" - ] - }, - { - "id": "34", - "title": "Celebrating 5 years of Kotlin on Android 🎉", - "content": "Five years ago Android announced official support for the interoperable, mature, production-ready, and open source Kotlin programming language. Since then, JetBrains and Google have been collaborating around the development of Kotlin, and the Kotlin Foundation was co-founded by the two companies; JetBrains developing both the language and tooling has given Kotlin outstanding IDE support. We put together some posts and videos to celebrate the journey and elaborate the milestones of Kotlin on Android with many of the people that helped to make it happen.", - "url": "https://android-developers.googleblog.com/2022/08/celebrating-5-years-of-kotlin-on-android.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRKysS-6n5YNTJAtLz2PkRNV5XsFnSlod6hwTvKRHbUb0W5pE8RszvmTfFK6GNbh2TKa3dbTP1AjB4pI0NB3agCRb1F4MbP5LQb6Q-8oveLb-mDjqFteWaZnIaztK4W1yONSJ5M6ffWAt-qu9CAu04v0PBIg6OIm9kFHMX6kolmf3zkagX2MIDDOtn/w640-h192/Kotlin%20Header.png", - "publishDate": "2022-08-16T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "10" - ], - "authors": [ - "1" - ] - }, - { - "id": "35", - "title": "Mad Skills: Performance 🏎️💨", - "content": "The MAD Skills series on Performance continued with a blog post from \nBen and a video from Tomáš that covers how to use the Macrobenchmark library along with UIAutomator to help generate Baseline Profiles for you. Baseline Profiles help your app to start and run faster by optimizing critical code paths ahead of time, allowing for a smoother user experience.", - "url": "https://medium.com/androiddevelopers/improving-performance-with-baseline-profiles-fdd0db0d8cc6", - "headerImageUrl": "https://miro.medium.com/max/1400/0*Tztd-PrhMpbWTXGC", - "publishDate": "2022-08-22T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "36" - ] - }, - { - "id": "36", - "title": "AndroidX releases 🚀", - "content": "In AndroidX, the Wear Compose Version 1.0.1 release fixed a logic bug in ScalingLazyColumn. As mentioned before, we released Wear Tiles Version 1.1. Webkit Version 1.5 added setAlgorithmicDarkeningAllowed, and added support for setting an allow-list of URLs for the configured proxy.", - "url": "https://developer.android.com/jetpack/androidx/versions", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-10-25T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "8", - "19" - ], - "authors": [] - }, - { - "id": "37", - "title": "A story of App Excellence, featuring Tik Tok", - "content": "Over in video, we covered how TikTok used Android tools to improve app startup and make the user experience more seamless, and how it impacted app usage and Play Store ratings.", - "url": "https://youtu.be/k9Pdgiugleg", - "headerImageUrl": "https://i.ytimg.com/vi/k9Pdgiugleg/maxresdefault.jpg", - "publishDate": "2022-08-31T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5", - "7", - "2" - ], - "authors": [ - "61" - ] - }, - { - "id": "38", - "title": "Design high-quality apps for kids", - "content": "We introduced the Google Play Academy course around designing kids’ apps, ensuring that they’re fun, usable, and appropriate for their target age group. The course covers the framework for rating kids apps on Google Play that teachers across the US use, so you can understand what they’re looking for to help your app stand out.", - "url": "https://youtu.be/-FUmVUPThX8", - "headerImageUrl": "https://i.ytimg.com/vi/-FUmVUPThX8/hqdefault.jpg", - "publishDate": "2022-08-31T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "12", - "2" - ], - "authors": [ - "61" - ] - }, - { - "id": "39", - "title": "MAD about Media", - "content": "Avish, our summer Android DevRel Engineer intern, discussed modern approaches to creating Android media apps leveraging experience in converting the Universal Android Media Player (UAMP) media playback sample app to Compose, updating it to use modern libraries such as Media3.", - "url": "https://medium.com/androiddevelopers/mad-about-media-f536f7d601c", - "headerImageUrl": "https://miro.medium.com/max/1400/1*BKAchEMpYdP3dEyaIAP5xA.png", - "publishDate": "2022-08-25T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [ - "61" - ] - }, - { - "id": "40", - "title": "Top Tips for Adopting Android’s Notification Permission", - "content": "Terence covered tips to improve your app’s user experience with notifications before targeting Android 13, as well as how to test your app’s integration with the permission without flashing different OS versions onto your device.", - "url": "https://medium.com/androiddevelopers/top-tips-for-adopting-androids-notification-permission-bf69afd677b8", - "headerImageUrl": "https://miro.medium.com/max/1400/1*XQmi35H84FdYhY_ONP6ntQ.png", - "publishDate": "2022-08-23T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13", - "6" - ], - "authors": [ - "89" - ] - }, - { - "id": "41", - "title": "Jetpack Compose Accompanist — An FAQ.", - "content": "Ben wrote a FAQ on the Jetpack Compose Accompanist, a labs-like environment for new Compose APIs. Accompanist is used to help fill known gaps in the Compose toolkit, experiment with new APIs and to gather insight into the development experience of building a Compose library. The goal of Accompanist is to eventually upstream libraries into the official toolkit. (at which point they will be deprecated and removed from Accompanist) Current libraries in Accompanist include support for Flow Layouts, Pager, Navigation Transitions, and Swipe Refresh.", - "url": "https://medium.com/androiddevelopers/jetpack-compose-accompanist-an-faq-b55117b02712", - "headerImageUrl": "https://miro.medium.com/max/1400/1*w_MA7M6H9HpwdWb_fx-2IA.png", - "publishDate": "2022-08-18T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "80" - ] - }, - { - "id": "42", - "title": "Introducing the MAD Skills series on Performance", - "content": "It’s time for another series of MAD Skills on Performance! Performance spans every aspect of Android development, and as part of Modern Android Development we’re aiming to make it more approachable and user-friendly. We have also found direct correlations between improved app performance, user satisfaction and business metrics.", - "url": "https://medium.com/androiddevelopers/introducing-the-mad-skills-series-on-performance-7dbb26e8b17f", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkWBi6t47sZvF2EqduUT_a38uamN_jLbjDIoada1oN9PSbkyyduU1f_x6t4H8gX1ghq11Wyt09dBjw-l3efO5EO62AvdrVELnk4qc6Xft96Fk_ViJ8xipsPXirDnvVoYw44tl-gJqUHqOXxrdbPbZjjGwXGmoLL992o_5AMdkpnWyoL0oz8HrAJagH/w1200-h630-p-k-no-nu/unnamed_%281%29.png.jpeg", - "publishDate": "2022-08-09T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7", - "5" - ], - "authors": [ - "36" - ] - }, - { - "id": "43", - "title": "MAD Skills: Important Performance Metrics", - "content": "Before you begin to work with performance effectively, we recommend you make yourself familiar with key performance metrics. By understanding what metrics you should look at, you will make inspecting, improving and monitoring performance easier.", - "url": "https://medium.com/androiddevelopers/important-performance-metrics-c7dacf018eb3", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYdlNQTXP5pTpqN3fQwfF__WHtEmflMVuLS6ErWorUPYM9MwThUmwuBfFhDMVtw5X1jVmchC9z20Bl_yD7M_thVbCmhRJLyZqh3sHZBm6Sryz_xyu4cvusk_xx1kJCh5ANM-TtsvG1WwqMUPllTZegJlstUj3KDesGJ2Xrh6AsLU9HvaFCLT4RLZd7/w1200-h630-p-k-no-nu/resize%20play_10-android_dev.png", - "publishDate": "2022-08-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "36" - ] - }, - { - "id": "44", - "title": "MAD Skills: Inspecting Performance\n", - "content": "This MAD Skills article on inspecting performance introduces you to tools and methods that help when your code’s performance. When you inspect performance you make sure that what’s happening in your app aligns with what you expect to happen.", - "url": "https://medium.com/androiddevelopers/inspecting-performance-95b76477a3d7", - "headerImageUrl": "https://1.bp.blogspot.com/-9MiK78CFMLM/YQFurOq9AII/AAAAAAAAQ1A/lKj5GiDnO_MkPLb72XqgnvD5uxOsHO-eACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-Compose-1.0-header-v2.png", - "publishDate": "2022-08-14T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7", - "5" - ], - "authors": [ - "36" - ] - }, - { - "id": "45", - "title": "Consuming flows safely in Jetpack Compose", - "content": "Collecting flows in a lifecycle-aware manner is the recommended way to collect flows on Android. If you’re building an Android app with Jetpack Compose, use the collectAsStateWithLifecycle API to collect flows in a lifecycle-aware manner from your UI.", - "url": "https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3", - "headerImageUrl": "https://miro.medium.com/max/720/1*LL7FLWzjT4c6bQdGlvdz7w.png", - "publishDate": "2022-08-09T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "8", - "4" - ], - "authors": [ - "23" - ] - }, - { - "id": "46", - "title": "Brushing up on Compose Text coloring", - "content": "Alejandra Stamato’s last article covered compose brush text coloring, and this week she taught us how to take it a step further and add animation to brush text coloring. She covers using the animation APIs in conjunction with the Brush APIs, demonstrating these with a candy cane shimmer effect and a back-and-forth shimmer effect.", - "url": "https://medium.com/androiddevelopers/brushing-up-on-compose-text-coloring-84d7d70dd8fa", - "headerImageUrl": "https://miro.medium.com/max/720/1*PZK1BRIYM22iLQhexPGT1Q.png", - "publishDate": "2022-07-24T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "91" - ] - }, - { - "id": "47", - "title": "Final Android 13 Beta update, official release is next!", - "content": "We’re just a few weeks away from the official release of Android 13! Meanwhile, we published the last Beta for your testing and development. We reached Platform Stability at Beta 3, so all app-facing surfaces are final, including SDK and NDK APIs, app-facing system behaviors, and restrictions on non-SDK interfaces. With these and the latest fixes and optimizations, Beta 4 gives you everything you need to complete your testing.", - "url": "https://android-developers.googleblog.com/2022/07/Final-Android-13-Beta-update-official-release-is-next.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKBGMJx7yKi1RuRF9Q1X-1GOgfCvJ7XVIhNZlsmYgeabTPyljHhEOr2F0iGkF2BM7jeE1HYnL75GXllESyWgpEZEQWm9xfKU5a8kDgrvS5-ZZN0eTq0QaTsCBOAFkJzGX9kBTZxfo_4p6O3DYkXVqsBMRynTq1Mw3EDq3hwEL5OcoCrSQ8rOvFrraK/w1200-h630-p-k-no-nu/Compose%20Blog%20social.jpg", - "publishDate": "2022-07-12T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [ - "62" - ] - }, - { - "id": "48", - "title": "10 years of Google Play", - "content": "In 2012, the team opened the (digital) doors of Google Play. A decade later, more than 2.5 billion people in over 190 countries use Google Play every month to discover apps, games and digital content. And more than 2 million developers work with us to build their businesses and reach people around the globe! Congratulations to the Google Play team for this huge milestone.", - "url": "https://android-developers.googleblog.com/2022/03/celebrating-10-years-of-google-play.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-07-24T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [ - "63" - ] - }, - { - "id": "49", - "title": "Android Basics with Compose Unit 3 available", - "content": "The Unit 3 of Android Basics with Compose course is available already! Unit 3 covers how to build apps that display a list of data and how to make your apps more beautiful with Material Design.", - "url": "https://developer.android.com/courses/android-basics-compose/course", - "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", - "publishDate": "2022-08-02T23:00:00.000Z", - "type": "Codelab", - "topics": [ - "2", - "3", - "10" - ], - "authors": [] - }, - { - "id": "50", - "title": "Jetpack Compose 1.2 is now stable", - "content": "This release contains new features like downloadable fonts, lazy grids, and improvements for tablets and Chrome OS with better focus, mouse, and input handling.", - "url": "https://android-developers.googleblog.com/2022/07/jetpack-compose-1-2-is-now-stable.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1vg5QdkR-Hj7oQ3yueza1VGYFrNOBbuAxRQycRYGO6HLR-Hf2LR9DHT__OxVFecRYFZbVq6cYg6ca15h7kkKO99zzheFMB_x6MdTO74DaJpTR933pmrkJ-pWVq_7fEmN38nYHQv2u1l7-Ukk8RtNPrtGnn-ChdYwcbbx8Q-MnbJ3z3BjSj3U0Q-YX/w1200-h630-p-k-no-nu/header-image-predictive-back-blog%20%281%29.png", - "publishDate": "2022-07-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "65" - ] - }, - { - "id": "51", - "title": "Compose for Wear OS is now 1.0", - "content": "Compose for Wear OS makes building apps for Wear OS easier, faster, and more intuitive by following the declarative approach and offering powerful Kotlin syntax. Moving forward, Compose for Wear OS is our recommended approach for building user interfaces for Wear OS apps.", - "url": "https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsdruRjulgaFFtqwrnp6Z0mzIAhmMzJZIOUjVqugCB3i5noivoLOVecpMNBQGVIsG_kjkzthRTpibL-CEmlsn5nZJUhnSkkdhEe0V2yaNPQt2l-FGh0sQz1JnOZVRHRDZIr72twcPZQL7Q0kdgb-JzxgKJlZSsESJkMLuAkvqCfyXoE7d-XxFQYVoJ/w1200-h630-p-k-no-nu/Google_Android_DeveloperPoweredCTS_4209x1253.jpg", - "publishDate": "2022-07-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "19" - ], - "authors": [ - "75" - ] - }, - { - "id": "52", - "title": "AndroidX releases 🚀", - "content": "Jetpack Compose 1.2, Compose for Wear OS 1.0, Core splashscreen v1.0, and Profile Installer v1.2 went stable. In RC, you can find AppCompat v1.5, Compose Compiler v1.3 (that brings support to Kotlin version 1.7.10), Emoji2 v1.2, and ShareTarget v1.2. Lastly, Wear Tiles v1.1 reached its first beta version!", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirdkVqgyYoZDm0ktFFXjyIATaFKJUCVU0lIzQpTw4dlJjvqruWxLJn5mJ5xHoZijqVQ-poghVIGWGCpZM0Nb_bzx274kr1Lo_nn0PvEzMXcU_DgNEFrKzw5HtuE_vA9zfRVy8RDuiAIgC_aDVhHmGdqSLhzsPK5Pj2m3QNB4lzsf4E0VkbctqiowND/w1200-h630-p-k-no-nu/Android-discountinuing-kotlin-synthetics-for-views-social%20%281%29.png", - "publishDate": "2022-08-02T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "53", - "title": "Make your app large screen ready", - "content": "Learn how to get started with large screen support and why it is so important. Accurately handling orientation changes, aspect ratios, and adaptive layouts may seem challenging, but with new large screen experiences in mind, multiple form factors bring new possibilities to your users.", - "url": "https://medium.com/androiddevelopers/make-your-app-large-screen-ready-baf8fe505ae7", - "headerImageUrl": "https://miro.medium.com/max/1200/0*1hkxEoydoX8GzK9N", - "publishDate": "2022-07-19T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "90" - ] - }, - { - "id": "54", - "title": "Get familiar with Wear OS 3 (without a physical device)", - "content": "You don’t need a physical device to test your Wear apps. Read this article to take a brief look at unique UI surfaces on Wear OS, create a Wear emulator and explore it from a user perspective.", - "url": "https://medium.com/androiddevelopers/get-familiar-with-wear-os-3-without-a-physical-device-e7962c97f02b", - "headerImageUrl": "https://miro.medium.com/max/1200/1*3M48bGiXnBX8y83eYLgFtw.png", - "publishDate": "2022-07-20T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "2" - ], - "authors": [ - "86" - ] - }, - { - "id": "55", - "title": "Jetpack Compose Interop: Using Compose in a RecyclerView", - "content": "What versions of Compose and RecyclerView do you need to use to get the best performance? In addition, you’ll understand how the interop works under the hood.", - "url": "https://medium.com/androiddevelopers/jetpack-compose-interop-using-compose-in-a-recyclerview-569c7ec7a583", - "headerImageUrl": "https://miro.medium.com/max/1200/1*aBNjsK7y35V05OKNQ2oIZg.png", - "publishDate": "2022-07-21T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "2" - ], - "authors": [ - "68" - ] - }, - { - "id": "56", - "title": "Brushing up on Compose Text coloring", - "content": "A very colorful blog post about how to work with Brush API together with TextStyle to achieve complex text coloring like giving a gradient to your text in a simple way.", - "url": "https://medium.com/androiddevelopers/brushing-up-on-compose-text-coloring-84d7d70dd8fa", - "headerImageUrl": "https://miro.medium.com/max/1200/1*PZK1BRIYM22iLQhexPGT1Q.png", - "publishDate": "2022-07-24T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "2" - ], - "authors": [ - "91" - ] - }, - { - "id": "57", - "title": "Animating brush Text coloring in Compose 🖌️", - "content": "Learn how to animate gradients in your text using the Brush API and Compose animations. Go check them out, I can’t stop looking at those animations now!", - "url": "https://medium.com/androiddevelopers/animating-brush-text-coloring-in-compose-%EF%B8%8F-26ae99d9b402", - "headerImageUrl": "https://miro.medium.com/max/1200/1*9fEDrtJES1CQEVlyI7WjgQ.png", - "publishDate": "2022-07-31T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "2" - ], - "authors": [ - "91" - ] - }, - { - "id": "58", - "title": "Prepare your app to support predictive back gestures", - "content": "Predictive back gestures is a feature that will be available in future versions of Android. However, to give you more time to adopt it, we made it available in the developer options of Android 13 Beta 4. Read the blog post for details on how to try out the new gesture and support it in your apps. Spoiler alert: it’s straightforward for most apps!", - "url": "https://android-developers.googleblog.com/2022/07/prepare-your-app-to-support-predictive-back-gestures.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9NXOkaUCvb2KLyrnCp4DWpr2dyryXzHsqvX94Dcrw3r_5znwMZFy6PwmaHJj25D0DKYcZlF8Jac5C0KhM1s2j_mEc0VULf-eiCpT3JGbYgI_jg105SyUEwNG7w2dvF-60npxBgZidqgXqx7A1iWRftv9lOZrM9OAfc4f105met0ZauGQ5hRQC0_wE/w1200-h630-p-k-no-nu/image3.jpg", - "publishDate": "2022-07-28T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13" - ], - "authors": [ - "92", - "93", - "94" - ] - }, - { - "id": "59", - "title": "ADB Podcast Episodes🎙187: System UI: A Retrospective", - "content": "In this episode Tor and Chet meet Dan Sandler and Adam Cohen from the SystemUI team. They dip into a bit of history, talking about where things were at when they joined the team, and how things have developed in the many years since. They also talk about how to expose (or not) gestures and features in a UI system.", - "url": "https://adbackstage.libsyn.com/episode-187-system-ui-a-retrospective", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/9/4/d/b/94dbe077f2f14ee640be95ea3302a6a1/ADB184_Skia_and_AGSL.png", - "publishDate": "2022-07-24T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "2" - ], - "authors": [ - "32", - "31" - ] - }, - { - "id": "60", - "title": "Developer-Powered CTS (CTS-D) 🧪", - "content": "The Compatibility Test Suite (CTS) is a key part of the Android Compatibility Program that helps to ensure that devices provide a stable, consistent environment for your apps. To enhance CTS, we are adding a new test suite called CTS-D that is built and run by developers like you. You can build and contribute test cases to CTS-D to help catch pain points that you are seeing in the field — places where device behavior doesn’t match the Android public APIs and the Android Compatibility Definition Document (CDD).", - "url": "https://android-developers.googleblog.com/2022/06/developer-powered-cts-cts-d.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsdruRjulgaFFtqwrnp6Z0mzIAhmMzJZIOUjVqugCB3i5noivoLOVecpMNBQGVIsG_kjkzthRTpibL-CEmlsn5nZJUhnSkkdhEe0V2yaNPQt2l-FGh0sQz1JnOZVRHRDZIr72twcPZQL7Q0kdgb-JzxgKJlZSsESJkMLuAkvqCfyXoE7d-XxFQYVoJ/w1200-h630-p-k-no-nu/Google_Android_DeveloperPoweredCTS_4209x1253.jpg", - "publishDate": "2022-06-22T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "6", - "13" - ], - "authors": [ - "98" - ] - }, - { - "id": "61", - "title": "Independent versioning of Jetpack Compose libraries ✒️", - "content": "We announced that the various Jetpack Compose libraries will move to independent versioning schemes, making it easier to incrementally upgrade your application and stay up-to-date with the latest Compose features.", - "url": "https://android-developers.googleblog.com/2022/06/independent-versioning-of-Jetpack-Compose-libraries.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjolHnYXFnb81t2qu38Z8BPxU0QNitCVulwRwgZlijGDwCbcSSPETvSVr9apTSV_eDknzPDs1BwccZU_lYr15czYU_ddiXete76bVxWWIhNE29-PfOCxMzvashjOwvGWrzZ4rynym-k4aNQ4c-tmN7v4O5vh0iaRpFZTMuYTFqjFLrNpHNlOwSyZyf6/w1200-h630-p-k-no-nu/unnamed_%281%29.png.jpeg", - "publishDate": "2022-06-28T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "65" - ] - }, - { - "id": "62", - "title": "Notes from Google Play: making Play work for everyone ▶️", - "content": "In the latest edition of Notes from Google Play, we touched on the Play Integrity API, the Data Safety section, the Privacy Sandbox on Android, and the newly-launched Google Play SDK Index, which provides data and insights about over 100 of the most widely used commercial SDKs. We covered new subscription capabilities that allow you to create multiple base plans and offers for each subscription, as well as the option to lower prices starting at the equivalent of 5 US cents to adapt to local purchasing power.", - "url": "https://android-developers.googleblog.com/2022/06/notes-from-google-play-making-play-work.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicboZEaxs6kOlhHSoRE__yCFdTkFvVW7z9ksAfVlkdCVgNQzkG1B1z4RVCV6l3g-Up3ZPGchGjq5idAKV5prrVVy9T2o6MtJh-iXZtUKcKyNY1Cqt39bi5VzwZ2CLy7N3FANcklla-mHIGboZzvNRl3eN5ZMvjl29tjtGGLGubVKIYwIUCh6q2-FhT/w1200-h630-p-k-no-nu/AD%20Blog%20Social.png", - "publishDate": "2022-06-21T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "63", - "title": "Dark theme testing in Pre-Launch Report 🕶️", - "content": "After you upload and publish a test Android App Bundle to Google Play, we install it on a set of Android devices, launch and crawl your app for several minutes, and compile your results into the pre-launch report. We’ve introduced a new test in the Pre-Launch Report that runs accessibility checks on a device switched to dark theme; this can detect color contrast issues that make it difficult to differentiate text and icons from a background. ", - "url": "https://support.google.com/googleplay/thread/170731936/dark-theme-test-now-included-in-pre-launch-report", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJtTKEaaohJa7b5H5mkmCNu1LbQ3FPrPv0hSVWdEacemvtEHRWhk-DCi5aEartYwL0OMg6NOHJ1Vnn1fqeJ5cMc7Bl08SY7JcEBpKp5Vde-y_VDIPoVNlhb5VZbyv4PlauW_xpvnf6iS8yszMOnuo5w0Rw5NmYZ45reEvulY2KgGoPaG9NZ6H8hO2b/w1200-h630-p-k-no-nu/Android_SandboxPreview_V2_1024x512.jpg", - "publishDate": "2022-07-10T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "12", - "6" - ], - "authors": [] - }, - { - "id": "64", - "title": "Performance tips to achieve App Excellence", - "content": "The Performance tips to achieve App Excellence video covered five app performance issues along with the tools that Android Studio and Google Play Console provide to help you diagnose and troubleshoot them.", - "url": "https://www.youtube.com/watch?v=VJItLXb7_V8", - "headerImageUrl": "https://i.ytimg.com/vi/VJItLXb7_V8/maxresdefault.jpg", - "publishDate": "2022-07-05T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "7", - "2" - ], - "authors": [] - }, - { - "id": "65", - "title": "Making Sense of Intent Filters in Android 13", - "content": "Before Android 13, when an app registered an exported component in its manifest and added an , the component could be started by any explicit intent — even those that do not match the intent filter. In some circumstances this can allow other apps to trigger internal-only functionality.\n\nThis behavior has been updated in Android 13. Now intents that specify actions and originate from external apps are delivered to an exported component if and only if the intent matches its declared elements.", - "url": "https://medium.com/androiddevelopers/making-sense-of-intent-filters-in-android-13-8f6656903dde", - "headerImageUrl": "https://miro.medium.com/max/1200/1*PX8VuYcLzaC-AvOtSPgMRw.png", - "publishDate": "2022-07-05T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13", - "2" - ], - "authors": [ - "99" - ] - }, - { - "id": "66", - "title": "Customizing AnimatedContent in Jetpack Compose 🌟", - "content": "Rebecca covers using AnimatedContent to transition between different composables with a smoother and more custom transition effect. Even the default behavior of AnimatedContent can make a big difference to the look and feel of your app, without much effort.", - "url": "https://medium.com/androiddevelopers/customizing-animatedcontent-in-jetpack-compose-629c67b45894", - "headerImageUrl": "https://miro.medium.com/max/1200/1*Yu-W3qMxx1YyPm_AJMcXGg.png", - "publishDate": "2022-06-30T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "96" - ] - }, - { - "id": "67", - "title": "Jetpack Compose Stability Explained", - "content": "Ben does a detailed exploration of how Compose determines the stability of each parameter of your composables to see what can be skipped during recomposition, including using compiler reports to determine what stability is being inferred about your classes. ", - "url": "https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8", - "headerImageUrl": "https://miro.medium.com/max/1200/1*iLEtRB3BpIkD6CgjWFP2RQ.png", - "publishDate": "2022-06-29T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "80" - ] - }, - { - "id": "68", - "title": "Migrating to the new coroutines 1.6 test APIs", - "content": "kotlinx.coroutines 1.6 introduces a set of new testing APIs, and the previous testing APIs are now deprecated.\n \nMarton talked about how we’ve migrated some of our own samples to the new APIs, covering a bunch of the necessary work for most Android projects.", - "url": "https://medium.com/androiddevelopers/migrating-to-the-new-coroutines-1-6-test-apis-b99f7fc47774", - "headerImageUrl": "https://miro.medium.com/max/1200/1*XQmi35H84FdYhY_ONP6ntQ.png", - "publishDate": "2022-06-28T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "10", - "6" - ], - "authors": [ - "1" - ] - }, - { - "id": "69", - "title": "Android 13 beta 3", - "content": "We released the third beta of Android 13! Android 13 has been built on our core themes of privacy and security, developer productivity, and large screen support. ​​Beta 3 takes Android 13 to Platform Stability, which means that the developer APIs and all app-facing behaviors are now final; you can confidently develop and release your updates. Read all about it in the post!\n", - "url": "https://android-developers.googleblog.com/2022/06/android-13-beta-3-platform-stability.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-06-07T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1", - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "70", - "title": "Google Play @ Google I/O - 3 updates you need to know", - "content": "In this video and blog post, Phalene tells us about the top three things to know about Google Play from Google I/O. These include updates on custom store listings, introducing more developers to the LiveOps beta, and new subscription capabilities. Learn more about these three topics in the video or blog post!\n", - "url": "https://android-developers.googleblog.com/2022/06/google-play-google-io-3-updates-you-need-to-know_01537187872.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg40pvJlLB9LP1shkyFOq4pIexSrdI-kSH9uPxMEdhjynUPm2Zdfy4W1sHb6v0d5hZqycnWP9qTVjxHu0DADwL2BrtBwkPrOOIFXA8-H2RC6W70ehcnYUTgKXy8eRvwvWDeu2J-0iVmMgkd4c1lyYUBnZi39mPVTJV5Ke83DvWMBioeLOWPivE0Tpvv/w1200-h630-p-k-no-nu/Android-io-spotlight-modern-android-development-social.png", - "publishDate": "2022-06-12T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1", - "12" - ], - "authors": [ - "76" - ] - }, - { - "id": "71", - "title": "Privacy Sandbox Developer Preview 3", - "content": "The Privacy Sandbox on Android aims to develop new solutions that preserve user privacy while enabling effective, personalized advertising experiences for apps. Now it is in Developer Preview 3, which adds APIs and resources for conversion measurement and remarketing use cases; this allows you to begin testing and evaluating impact on all key APIs for Privacy Sandbox on Android.", - "url": "https://android-developers.googleblog.com/2022/06/privacy-sandbox-developer-preview-3.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg5R2hd0VZp__xKUUgs6-tfKHEoabMPqo2aY6uoGvzre-9E4gUlz6RbGsrE-Txszbrc3OaNL9r2TshsZmzGhEiM3M-_eO8M39K6ljm9NrX2BMHRLHM3HeF04YgJf8l4Z1-kNaP9YV8BCRe3n2zTUTSx3FOvA5IRc4PCjVPiJ7CEw7M7Y7uAJLVd7WQ/w1200-h630-p-k-no-nu/Android-GoogleIO3thingstoknowaboutFormFactors_4209x1253.png", - "publishDate": "2022-06-15T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11" - ], - "authors": [ - "77" - ] - }, - { - "id": "72", - "title": "ADB Podcast Episodes🎙186: Live Edit", - "content": "In this episode, we talk with Alan and Esteban from Android Studio about the new \"Live Edit\" feature recently launched at Google I/O in the Electric Eel canary build. We dive into the technology -- how it works, what the technical challenges are, and its current state.", - "url": "https://adbackstage.libsyn.com/episode-186-live-edit", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2022-06-07T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "5", - "5" - ], - "authors": [ - "31", - "30", - "32" - ] - }, - { - "id": "73", - "title": "AndroidX releases", - "content": "Let’s take a look at what’s been up with AndroidX releases since the last episode of Now in Android. We have a few new features that are stable including Wear Watchface, Games-Activity, Benchmark, and Annotation", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizEliXuy2sTNnhRQtwNAvL46sKpcBLbHiuROrEiOtPDTJ1D0eQlWDDUjspVECqlDw3_sLhFzJO8SCrGJuFaT2QQ7Iezi0xrkhD7yWqpbacVLRC8aX-1bx0aZ-RM1k_S-S0LFTE0PrvX-BlNsmmilGCGMdvRk0v6zhHs8nKwdFv-AluPQIRjAtFx938/w1200-h630-p-k-no-nu/Android-GoogleIOSpotlightPrivacyAndSecurity_1024x512.png", - "publishDate": "2022-06-22T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "74", - "title": "Google I/O recaps ⏱ - Modern Android Development!\n", - "content": "Our goal is to make developing beautiful and engaging Android apps as fast and easy as possible. We want to take on the complex parts of building apps so that you can focus on your app’s features and deliver high quality experiences to your users.\n\nWe call this approach Modern Android Development (or MAD for short!) and deliver it through a suite of tools, libraries and guidance. At Google I/O we announced a number of updates and additions to our MAD offerings; here’s a recap of the three largest announcements.", - "url": "https://android-developers.googleblog.com/2022/05/io22-spotlights-mad.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpSQB-YhRGzOJ4X1hoh1DlnMx9cOxfUKdoriSncXDPuaXZXpiXGYBXpxJUsMVKPhGTbTKkT-Gn3g52Tqcy7Alyv6gkMQEKd7twzAj1JbR2DwdFUZYbIcnMgXD2PeRrkTq9jZw8XId5t0D9im6i3XkVCK-YIk10g3E7ut_pLnVdA9tVnGJU5T7XFLi8/w1200-h630-p-k-no-nu/unnamed.png", - "publishDate": "2022-05-22T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "5" - ], - "authors": [ - "57" - ] - }, - { - "id": "75", - "title": "Google I/O recaps ⏱ - Form factors!", - "content": "With close to half a billion cars, TVs, watches and laptops running on Android, it is more important than ever for apps to work seamlessly across every device. This year at I/O, we renewed our focus on form factors and announced major updates for Wear OS and Large Screens. To help you get to the bottom of what’s new, here are the three things you need to know about Form Factors at Google I/O", - "url": "https://android-developers.googleblog.com/2022/05/form-factors-google-io-22.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7lx3OtPkMXTr0cwNlItkSUwDQYcTAO1cP-fE8n_NLtnZQ5uBnoP-y0MfENfmuB_2HGRUbmrx_ADz4FmDW8VkBmp_WcdISO0uQiO4Dw2yi9XjBUPqwjX2o24j8lUEhvYWJidFi6ra9WrjHxO1sTCjwBZrLyXHhIjgbRZzFQX-oUOKeqvf1dXg4XQ-A/w1200-h630-p-k-no-nu/Android_ImplementingColor_1024x512.jpg", - "publishDate": "2022-05-30T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "2", - "16", - "15" - ], - "authors": [] - }, - { - "id": "76", - "title": "Google I/O recaps ⏱ - Android Privacy, Platform & Security!", - "content": "Amidst the whirlwind of content at Google I/O, we shared huge announcements involving privacy, security, and the Android platform. Read on for the details, and don’t forget to watch the topic playlist on YouTube.", - "url": "https://android-developers.googleblog.com/2022/06/privacy-security-google-io-22.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-06-05T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11", - "13" - ], - "authors": [ - "95" - ] - }, - { - "id": "77", - "title": "Spot your UI jank using CPU profiler in Android Studio", - "content": "Takeshi wrote about spotting your UI jank using the CPU profiler in Android Studio. The article goes through a step by step walkthrough about how to use the new jank detection UI in Android Studio Chipmunk including how to record a trace, and how to inspect janky frames.", - "url": "https://medium.com/androiddevelopers/spot-your-ui-jank-using-cpu-profiler-in-android-studio-9a4c41a54dab", - "headerImageUrl": "https://miro.medium.com/max/1200/1*FkkN0FugRiSDxhfp1TQz-Q.png", - "publishDate": "2022-05-15T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5" - ], - "authors": [ - "81" - ] - }, - { - "id": "78", - "title": "Custom Canvas Animations in Jetpack Compose ✨", - "content": "Rebecca Franks wrote about custom Canvas animations in Jetpack Compose. Using the Animatable states and some side-effects, you’ll be able to achieve custom animations as you were able to do with ValueAnimator in the View system.", - "url": "https://medium.com/androiddevelopers/custom-canvas-animations-in-jetpack-compose-e7767e349339", - "headerImageUrl": "https://miro.medium.com/max/1200/1*16bn5V--jLMAJLCWspst2Q.png", - "publishDate": "2022-05-16T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "96" - ] - }, - { - "id": "79", - "title": "Implementing Dynamic Color: Lessons from the Chrome team", - "content": "If you’re interested in Material You dynamic color, Rebecca Gutteridge talks about how the Chrome team implemented it and the things they kept in mind such as accessibility, custom colors, incognito, and more. It also comes with a really helpful list of recommendations from the designers and developers of the team.", - "url": "https://android-developers.googleblog.com/2022/05/implementing-dynamic-color-lessons-from.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-05-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2", - "3" - ], - "authors": [ - "97" - ] - }, - { - "id": "80", - "title": "ViewModel: One-off event antipatterns", - "content": "We’re very opinionated about what to do with ViewModel events in our UI events Architecture guidance, and this blog post explains why the alternatives to our recommendation might bring higher engineering costs to developers and a worse user experience.", - "url": "https://medium.com/androiddevelopers/viewmodel-one-off-event-antipatterns-16a1da869b95", - "headerImageUrl": "https://miro.medium.com/max/1200/0*ROW1i16idpH-rHO-.png", - "publishDate": "2022-05-31T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "23" - ] - }, - { - "id": "81", - "title": "Diving Into Compose — Lessons Learned While Building Maps Compose", - "content": "If you’re interested in Compose and Google Maps, Chris Arriola wrote about the lessons learned while building Maps Compose. You’ll see how they took advantage of Kotlin features, how to aim for binary compatibility, subcomposition, and more!", - "url": "https://medium.com/androiddevelopers/diving-into-compose-lessons-learned-while-building-maps-compose-d20ef5dfe1bb", - "headerImageUrl": "https://miro.medium.com/max/1200/1*6rFVWLu8FXGXfmASVP3zYQ.jpeg", - "publishDate": "2022-06-02T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "68" - ] - }, - { - "id": "82", - "title": "I/O 22: What's New in Android", - "content": "For a survey of what I/O 22 offers to Android developers in video form check out the What’s New in Android talk.", - "url": "https://youtu.be/Z6iFhczA3NY", - "headerImageUrl": "https://i.ytimg.com/vi/Z6iFhczA3NY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "83", - "title": "I/O 22: What's New for Android Devs", - "content": "Move quickly through some of the top material for Android Devs at IO/22", - "url": "https://www.youtube.com/watch?v=l0iBPh7k_HQ", - "headerImageUrl": "https://i.ytimg.com/vi/l0iBPh7k_HQ/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGUgUShhMA8=&rs=AOn4CLCNz_S_i8TyDdvX_y1-SZGyAfoK3A", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "1", - "5", - "3", - "8" - ], - "authors": [] - }, - { - "id": "84", - "title": "I/O 22: Android Fireside Chat", - "content": "Android Fireside Chat is back! Android leaders answered your questions from the stage.", - "url": "https://www.youtube.com/watch?v=wq3Et-D9P5Y", - "headerImageUrl": "https://i.ytimg.com/vi/wq3Et-D9P5Y/maxresdefault.jpg", - "publishDate": "2022-05-17T23:00:00.000Z", - "type": "", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "85", - "title": "13 Things to know for Android developers at Google I/O!", - "content": "There aren’t many platforms where you can build something and instantly reach billions of people around the world, not only on their phones—but their TVs, cars, tablets, watches, and more. Today, at Google I/O, we covered a number of ways Android helps you make the most of this opportunity, and how Modern Android Development brings as much commonality as possible, to make it faster and easier for you to create experiences that tailor to all the different screens we use in our daily lives.", - "url": "https://android-developers.googleblog.com/2022/05/13-things-to-know-for-android-developers-at-google-io.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC2X0sIY_AGvgi6jD8Eh_u8rOdZXKA6PP18tnJdA6jQxR-n4bF6vsIVI2D4FTOnHAlqSY5hJShEjHcRQr7P8QM-YyP3sM3Su_KxFRdBXhg8WUIoXr74luWfFvtgYGJHWdDe_gPnwpCsLR4YhE0U88QcSqrYs3LLjp7dGqQul_pRoerJr__-mD8lUPA/w1200-h630-p-k-no-nu/Android-IO22AndroidDevRecap_Social.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1", - "3", - "2", - "5", - "19" - ], - "authors": [ - "62" - ] - }, - { - "id": "86", - "title": "I/O 22: Now in Android, the App ⏱️", - "content": "After being available on this blog, our YouTube series, and a podcast, starting today, you can check out the alpha version of the Now in Android app on GitHub that was featured in the Google I/O 2022 Developer Keynote 🎉\n\nThe app showcases best practices, opinionated designs, and solutions to complex real-world problems. ", - "url": "https://android-developers.googleblog.com/2022/05/now-in-android-sample-app-alpha.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCfJQnd9fqg_J5d_j4lWDbJQ-u-sHd_Z_z8srVPoEuO3_CWY3eVZBulaRTPxqQV3VNkA_1qqkleLVYBI7tRtSIZsOsIDzOKKstOLehI8a1RAUwFgHpXY-3kEmoEPujjQZU1VUk08DesedqDdiA1ZOxUxR-XJIMb66G3gruUq3cxqHwokSQSWycIRPl/w1200-h630-p-k-no-nu/Now-in-Android%28Splash%29.jpg", - "publishDate": "2022-05-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "2" - ], - "authors": [ - "42" - ] - }, - { - "id": "87", - "title": "I/O 22: Jetpack, Compose, and Tooling 🚀", - "content": "What’s new in Jetpack covered additions and updates to the over 120 libraries we’ve created to address common pain points and make development easier.", - "url": "https://android-developers.googleblog.com/2022/05/whats-new-in-jetpack-compose.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQyCrAWdIb8-moiYuP7EdpznRLOLaKoZWJ04MLzMi1wkJrMfLKQshwXhB_ODNz3T6_aoOwQ0YccVpSbLO2K9qkpx-HTklvNm3ZR_spOINLr861_PgDXDnh6LgpptIyzR5Nv-UjlQ-5FyeLpHwOCb4NjZ8darLIomTVjHM2VvDv7YZdzO-FS6zMKEhlCQ/w1200-h630-p-k-no-nu/Android-JetpackCompose1.2-Social.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "8" - ], - "authors": [ - "65", - "52" - ] - }, - { - "id": "88", - "title": "I/O 22: Lazy layouts in Compose", - "content": "Compose brings a simple and performant way of creating scrolling lists, with fewer lines of code than RecyclerView. Learn how lazy layouts enable adding content on demand, how to use Lazy composables, how these work under the hood, and optimization tips for better performance.", - "url": "https://youtu.be/1ANt65eoNhQ", - "headerImageUrl": "https://i.ytimg.com/vi/1ANt65eoNhQ/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "5" - ], - "authors": [] - }, - { - "id": "89", - "title": "I/O 22: Fragments: The good (non-deprecated) parts", - "content": "Fragments have been in constant motion over the past couple of years as we move towards a world where all the behavior is defined. Some of these moves have resulted in new APIs designed to do a specific function and replace old, unreliable ones. Learn about the changes in menus, fragment communication, the new strict mode, single lifecycle, and more.", - "url": "https://www.youtube.com/watch?v=OE-tDh3d1F4", - "headerImageUrl": "https://i.ytimg.com/vi/OE-tDh3d1F4/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "5" - ], - "authors": [] - }, - { - "id": "90", - "title": "I/O 22: Performance best practices for Jetpack Compose", - "content": "Jetpack Compose can feel like magic, but what do you do when the magic isn't as performant as you want? Discover best practices in Jetpack Compose with regards to performance, common mistakes, and how to avoid them.", - "url": "https://www.youtube.com/watch?v=EOQB8PTLkpY", - "headerImageUrl": "https://i.ytimg.com/vi/EOQB8PTLkpY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "7" - ], - "authors": [] - }, - { - "id": "91", - "title": "I/O 22: Workshop: Basic layouts in Compose", - "content": "Learn how to implement a realistic and complex UI using Compose Layouts.", - "url": "https://www.youtube.com/watch?v=kyH01Lg4G1E", - "headerImageUrl": "https://i.ytimg.com/vi/kyH01Lg4G1E/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "2" - ], - "authors": [] - }, - { - "id": "92", - "title": "I/O 22: Workshop: State in Jetpack Compose", - "content": "Discover the core concepts of using state in Jetpack Compose by building a wellness app. Learn how the app's state determines what is displayed in the UI, how Compose keeps the UI updated when state changes, how to optimize the structure of your composable functions, and work with ViewModels in a Compose app.", - "url": "https://www.youtube.com/watch?v=PMMY23F0CFg", - "headerImageUrl": "https://i.ytimg.com/vi/PMMY23F0CFg/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "2" - ], - "authors": [] - }, - { - "id": "93", - "title": "I/O 22: Airbnb uses Jetpack Compose to empower devs to do their best work", - "content": "Jetpack Compose, Android’s modern UI-building toolkit, directly supports both of Airbnb’s development principles. Compose provided a solid foundation for adaptable, quality engineering and reduced boilerplate code, so developers could focus on delivering a great user experience — and advance their two-fold pursuit of engineering excellence.", - "url": "https://android-developers.googleblog.com/2022/05/airbnb-uses-jetpack-compose.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1-9FyHvhui7rTgRvUNvyQE8Mmrx5vQ1ZHnuYs0DMdkwFeBK7DuGIP2VL1sgxiQgBtrGvKF4j0QcKInGgSWXCx4bszhPM4VwmuUHm1VIBjmkJqBSWPYk4D9fPmDVhK3asVTNqmkxRjTidzZpzaUzQYn0JmQzjwblhp3el20qowfr00yTpPdKyhefcT/w1200-h630-p-k-no-nu/image1.gif", - "publishDate": "2022-05-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [] - }, - { - "id": "94", - "title": "I/O 22: What’s new in Android development tools ", - "content": "Get an overview of what's new in Android Studio for Android app developers, including demos and a presentation of relevant features that can accelerate developers' workflow on the latest Android APIs.", - "url": "https://www.youtube.com/watch?v=RFv8GkLd5OY", - "headerImageUrl": "https://i.ytimg.com/vi/RFv8GkLd5OY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5", - "2" - ], - "authors": [] - }, - { - "id": "95", - "title": "I/O 22: Designing apps for large screens", - "content": "Explore key concepts and strategies for adapting mobile apps to large screen devices, such as tablets and foldables. Dig into the challenges of optimizing design and finding ways to meet the changing expectations of your users, in order to offer the highest quality large screen app experience.", - "url": "https://www.youtube.com/watch?v=pvdqeIM6mh0&t", - "headerImageUrl": "https://i.ytimg.com/vi/pvdqeIM6mh0/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "96", - "title": "I/O 22: Learn how to update your app for the larger screen", - "content": "You already have an app you’ve been working on for years, with a set, and hard to change, architecture. Discover the many options to bring your existing app forward, and build optimized large screen experiences without starting from scratch.", - "url": "https://www.youtube.com/watch?v=1ZOQ_-XPSv8", - "headerImageUrl": "https://i.ytimg.com/vi/1ZOQ_-XPSv8/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "97", - "title": "I/O 22: Implementing Android apps for all screen sizes ", - "content": "Get a better understanding of how to build your app to fill all screen formats. Explore development best practices to optimize applications for all devices with an emphasis on Jetpack Compose, navigation, managing state, and testing.", - "url": "https://www.youtube.com/watch?v=MPwf5DklKp0&t", - "headerImageUrl": "https://i.ytimg.com/vi/MPwf5DklKp0/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "98", - "title": "I/O 22: Input for all screens", - "content": "Learn about some straight-forward best practices to support input methods like keyboard, mouse, and stylus. These simple changes can elevate your app experience and grow user engagement.", - "url": "https://www.youtube.com/watch?v=XtImpP23uhE", - "headerImageUrl": "https://i.ytimg.com/vi/XtImpP23uhE/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "99", - "title": "I/O 22: Building an adaptive layout with SlidingPaneLayout", - "content": "Learn how to add a list and detail layout to a View-based app with SlidingPaneLayout. Explore how to reconfigure your fragments, open and close the detail pane, handle custom back navigation, and disable gesture navigation.", - "url": "https://www.youtube.com/watch?v=2rtLdF9UFqg", - "headerImageUrl": "https://i.ytimg.com/vi/2rtLdF9UFqg/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "100", - "title": "I/O 22: Android Developer Story: eBay gets a 4.7 Google Play rating", - "content": "Matthew Mossman, Android engineer for eBay, shares how he and his team optimized the eBay app for large screens and discusses the impact they saw in their customer’s engagement and experience on the tablets.", - "url": "https://www.youtube.com/watch?v=8gGXwOxHQjk", - "headerImageUrl": "https://i.ytimg.com/vi/8gGXwOxHQjk/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "101", - "title": "I/O 22: Tablet moments, built by you!", - "content": "Android developers around the world are building some amazing experiences for tablets and large screen devices. You can see how Facebook, TikTok, HBO Max, Zoom and Google Slides are all enhancing their applications.", - "url": "https://www.youtube.com/watch?v=IRiEcVfJJko", - "headerImageUrl": "https://i.ytimg.com/vi/IRiEcVfJJko/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "102", - "title": "I/O 22: Second Beta of Android 13", - "content": "The Android 13 Beta is available to test on a range of devices from Asus, Lenovo, Nokia, OnePlus, Oppo, Realme, Sharp, TECNO, Vivo, Xiaomi, and ZTE.", - "url": "https://android-developers.googleblog.com/2022/05/second-beta-of-android-13.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjV9RykVMNPi7wNhkdMXSTn14sT_GE3-0m7iHPi6zfEIMlLjUr9I8icC7vKh7u0bTKqpU6PKnMKGufNHfE7QJJjvwU6PcTygM0Umd0sEh3C1lYKkAxyeJfOCJblem10kjPCZWlwpUT6E-ITy1F3AglIvqQAoA6mxcHCUAmjNzXfNXw2lCOLUQvjTHj/w1200-h630-p-k-no-nu/Android13_dpp.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "103", - "title": "I/O 22: Developing privacy user-centric apps", - "content": "Keeping users in control of their privacy and safeguarding everything they do online is more important than ever, particularly when it comes to the mobile operating system.", - "url": "https://www.youtube.com/watch?v=opGkUl8C-HM&t", - "headerImageUrl": "https://i.ytimg.com/vi/opGkUl8C-HM/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13", - "11" - ], - "authors": [] - }, - { - "id": "104", - "title": "I/O 22: Building the Privacy Sandbox", - "content": "The Privacy Sandbox initiative is an industry-wide effort to make Android and the web private. It introduces a set of privacy-preserving APIs that give both developers and entrepreneurs the tools they need to build thriving digital businesses and protect people's privacy online. Hear from a panel of Privacy Sandbox team members to hear answers to popular questions.", - "url": "https://www.youtube.com/watch?v=NKz5oT6kXI4&t", - "headerImageUrl": "https://i.ytimg.com/vi/NKz5oT6kXI4/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13", - "11" - ], - "authors": [] - }, - { - "id": "105", - "title": "I/O 22: Overview of the Privacy Sandbox on Android", - "content": "The Privacy Sandbox on Android is intended to fundamentally advance privacy for the ecosystem, without sacrificing key ads functionality and without putting access to free ad-supported apps at risk. Listen to an overview of the key changes and technical considerations for developers.", - "url": "https://www.youtube.com/watch?v=pQdzFbmlvOo", - "headerImageUrl": "https://i.ytimg.com/vi/pQdzFbmlvOo/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13", - "11" - ], - "authors": [] - }, - { - "id": "106", - "title": "I/O 22: Best practices for running background work on Android", - "content": "Learn about several changes to how and when apps can run tasks in the background. Discover why the changes were made and some best practices for developers to run work in the background.", - "url": "https://www.youtube.com/watch?v=t1_8WSEguDY", - "headerImageUrl": "https://i.ytimg.com/vi/t1_8WSEguDY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13", - "7" - ], - "authors": [] - }, - { - "id": "107", - "title": "I/O 22: What’s new in Android machine learning", - "content": "Learn about the latest APIs and early access programs for machine learning (ML) on Android.", - "url": "https://www.youtube.com/watch?v=tG6hiQNMLmE", - "headerImageUrl": "https://i.ytimg.com/vi/tG6hiQNMLmE/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "108", - "title": "I/O 22: What’s new in Android Camera", - "content": "A camera is one of the top reasons consumers purchase phones and devices. Explore the latest in Android Camera and upcoming launches. Learn best practices when using Android Camera and how to bring delightful experiences to users.", - "url": "https://www.youtube.com/watch?v=n8mubjwEVxQ", - "headerImageUrl": "https://i.ytimg.com/vi/n8mubjwEVxQ/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "18" - ], - "authors": [] - }, - { - "id": "109", - "title": "I/O 22: What’s new in Android media", - "content": "Get a high level overview of everything that's new in Android media, including media features and changes in Android 12+, as well as new features in ExoPlayer and the Jetpack media libraries. Hear about key announcements in Spatial audio, HDR video, video transcoding, editing APIs, AV1 decode, and Performance Class 13.", - "url": "https://www.youtube.com/watch?v=Ba70zmFZgk0", - "headerImageUrl": "https://i.ytimg.com/vi/Ba70zmFZgk0/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "18" - ], - "authors": [] - }, - { - "id": "110", - "title": "I/O 22: Workshop: How to optimize media streaming with ExoPlayer\n", - "content": "This workshop guides you through using the new Jetpack Media3 APIs to build a simple media app using ExoPlayer for progressive and adaptive streaming playback. Learn about the advantages of using ExoPlayer and the features it offers.", - "url": "https://www.youtube.com/watch?v=Hw0Jeq42FNU", - "headerImageUrl": "https://i.ytimg.com/vi/Hw0Jeq42FNU/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "18" - ], - "authors": [] - }, - { - "id": "111", - "title": "I/O 22: What’s new in Accessibility for developers\n", - "content": "Making applications accessible ensures equal access to roughly one billion people in the world with disabilities, and it can also benefit people without disabilities by providing a better user experience in general. Learn about new developments in Android Studio, a new API that improves the video consuming experience, and advancements in Jetpack Compose that can help you build more accessible apps.", - "url": "https://www.youtube.com/watch?v=6LsaP6oKxMY", - "headerImageUrl": "https://i.ytimg.com/vi/6LsaP6oKxMY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [] - }, - { - "id": "112", - "title": "I/O 22: What's new in app performance", - "content": "Users expect apps to launch quickly and scroll smoothly, even on low-end devices. That's why performance enhancements should be available on devices with older versions of Android. Learn how to write, maintain, and monitor apps that perform to users' high standards.", - "url": "https://www.youtube.com/watch?v=DYdHLqLVspY", - "headerImageUrl": "https://i.ytimg.com/vi/DYdHLqLVspY/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "7" - ], - "authors": [] - }, - { - "id": "113", - "title": "I/O 22: Introducing Google Wallet and developer API features", - "content": "Learn more about how to use the Google Wallet APIs to digitize tickets, loyalty cards, and much more. Discover the new Android SDK and additional developer tools to simplify your integration.", - "url": "https://www.youtube.com/watch?v=2gTCghy-dU4", - "headerImageUrl": "https://i.ytimg.com/vi/2gTCghy-dU4/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "114", - "title": "I/O 22: Android solutions for seamless sign-in across devices", - "content": "Discover Android solutions that enable seamless and secure login experiences so users can enjoy your app across devices.", - "url": "https://www.youtube.com/watch?v=xghjqgj4peA", - "headerImageUrl": "https://i.ytimg.com/vi/xghjqgj4peA/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "115", - "title": "I/O 22: Build power, multi-device experiences\n", - "content": "Developers play a critical role in Google's multi-device ecosystem. Hear about new tools available to you so that you can build your own powerful, multi-device experiences that span platforms and form factors. Learn about Cast, BlockStore, and the new abstraction layer D2DI.", - "url": "https://www.youtube.com/watch?v=H6UxTnghkMw", - "headerImageUrl": "https://i.ytimg.com/vi/H6UxTnghkMw/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "116", - "title": "I/O 22: Announcing Compose for Wear OS Beta!", - "content": "Today we’re launching the Beta release of Compose for Wear OS, our modern declarative UI toolkit designed to help developers create beautiful user experiences for Wear OS.", - "url": "https://android-developers.googleblog.com/2022/05/announcing-compose-for-wear-os-beta.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrfM5DyOkb4yfuiuJEOelmS5x4sTioxYCRPdnoSI1h64j-xWkWq9Wk0mZ61ljUw_tkO7NXxKsDUb5TbYMHLxLxhcY24rzNnnDhjzFOOClkM_WH--2bTLJFq93HODS7PHebGl00oluu0Sg5p0MTeNAfusLgHvorSxTDS26YwnQXkTJsDq2HJC36m7Jl/w1200-h630-p-k-no-nu/image4.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "3", - "8", - "13" - ], - "authors": [ - "75" - ] - }, - { - "id": "117", - "title": "I/O 22: Introducing Health Connect", - "content": "Introducint Health Connect, a new API for Android app developers to securely access user health dataAs Android developers, connecting and sharing health and fitness data between apps can help you provide more meaningful experiences and insights for your users", - "url": "https://android-developers.googleblog.com/2022/05/introducing-health-connect.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioB9TK2fLuYAv8nIzhQMGsmSQJFhcWTsEl4ZUkR1XXbEkmR4JR3ZBP2N3YLYU143Lo02Qsx3iXE2VBobBBDJ0fr9V_2_epxOtnDLRA9S2XpkUdAWO-OyBejhkrf53wv4sIqnaqmjRB8iu8XzeFhCgM00gxgln1M-ipVeww9WG5bduNTBcpxRpASMha/w1200-h630-p-k-no-nu/231491533__37260715__148018.png", - "publishDate": "2022-05-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "118", - "title": "I/O 22: What’s new with Google TV & Android TV OS", - "content": "Since last year’s I/O, we’ve continued our commitment to enable you to build better and more engaging experiences on Android TV OS. In addition to platform updates, new features, like expanded integrations with the Live tab, offer opportunities for users to better engage with your content.", - "url": "https://android-developers.googleblog.com/2022/05/whats-new-with-google-tv-android-tv-os.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkqwL9j8jLb9ItKoISX9Yo0d3r_IdPdNMje6cca3obZpFDuNmmfKsL6Qj-E3agkDK7E312kuVjVLw3Ez2dF-vVj9UeRSUUPuOuc2T3T9d-HqEaZ6g76NQXEvqwIik0rAqFRZPiE6NH2PfgFmaDDQh6hb81HRgQrzmWGT6WJyuyK-yxnrrCfV4YHYnh/w1200-h630-p-k-no-nu/Android_WhatsNewInAndroidTVGoogleTV_1024x512.png", - "publishDate": "2022-05-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "16", - "13" - ], - "authors": [ - "27" - ] - }, - { - "id": "119", - "title": "I/O 22: What’s new with Android for Cars", - "content": "We’re excited to share some of our combined accomplishments from this past year, and introduce new updates that will make it easier for you to provide users with an even better experience in the car.", - "url": "https://android-developers.googleblog.com/2022/05/whats-new-with-android-for-cars.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNnogZQP4IGFjoSw7QWt1vr7MphwOi1OtRAAfCAVaNDTdnD5_CAaKhsf11nzxS_XrQ3ERet5yhpK7bs0e5YXdarv6o8iuzNYRqJ25fZrRL8TPfyBGpADg3oOrGM364foSvEdNhSTqDOF_2_TTBkRq-rElETpNAC6pOIHioH7ug3s8z8iJ_f3vWL5pTuQ/w1200-h630-p-k-no-nu/Android-whats-new-with-android-for-cars-io22-social%20%281%29.png", - "publishDate": "2022-05-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "15" - ], - "authors": [] - }, - { - "id": "120", - "title": "I/O 22: What's new in Google Play", - "content": "At this year’s Google I/O, we focused on three major ways we can help you continue growing your business on Google Play: Privacy and security initiatives ; Tools to help you improve your app quality; New ways to help you acquire users and engage with existing ones", - "url": "https://android-developers.googleblog.com/2022/05/whats-new-in-google-play.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugYmi_1-WFvfxVe8BJ5GCiZjAp1R_B42dvrxu-fHkL1WkswlvjZpAVImVJIJdgzEERdFyzF9QzNZYPmoAJDEe2lfwdOnpSr2zHiQy0od18YP2ozVpC_fR7WQUpeB9kJyqTLx7udivLZn1w3trWfVeT4ejl8e9bZqVJfUyH05k0SgODQpYsUb4Junn/w1200-h630-p-k-no-nu/Play-new-google-play-io-01.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12", - "8", - "11" - ], - "authors": [] - }, - { - "id": "121", - "title": "I/O 22: New Google Play SDK Index", - "content": "New Google Play SDK Index helps you choose the right SDKs for your app. Helping developers, like you, make informed decisions about SDKs is part of keeping Google Play a safe, trusted space for billions of people.", - "url": "https://android-developers.googleblog.com/2022/05/new-google-play-sdk-index.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEBzXvqydpodashPiKchgLE9NA6WXVUYbTFBuooFn8_XwK6E4cMEbM7hyiTRPZ-H3pwTvyHY8143pGB5zgUt_zgUuzsjAIkRSQsngYBd4_dusLSXF94j6KZ0r1UiZC3vQFrabOw9vXdA0Wzcm3SDm_LvlCFdxW67-FplcMJLpZYLQ02I2EUrvORXHl/w1200-h630-p-k-no-nu/Play-new-google-play-sdk-index-social-V2.png", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [ - "87" - ] - }, - { - "id": "122", - "title": "I/O 22: How to integrate Android widgets with Google Assistant", - "content": "Explore intermediate-level concepts for integrating Android widgets with Google Assistant. Provide quick answers and interactive app experiences on Assistant enabled surfaces like Android and Android Auto to their users.", - "url": "https://www.youtube.com/watch?v=6vXZcg9g_Mo", - "headerImageUrl": "https://i.ytimg.com/vi/6vXZcg9g_Mo/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "123", - "title": "I/O 22: Integrate Google Assistant into Android for cars", - "content": "Learn how to integrate voice into apps built for Android Auto. Learn to add voice to Widgets for Auto, and explore some of the other voice-first features coming to the platform.", - "url": "https://www.youtube.com/watch?v=hhdVMLG5Y10", - "headerImageUrl": "https://i.ytimg.com/vi/hhdVMLG5Y10/maxresdefault.jpg", - "publishDate": "2022-05-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2", - "15" - ], - "authors": [] - }, - { - "id": "124", - "title": "I/O 22: ADB Podcast Episode 185 : Play Store🎙", - "content": "In this episode Tor, Chet, and Romain chat with Jon and Andrew from the Play team about the Play Store app, which recently went through a major refactoring.", - "url": "https://adbackstage.libsyn.com/episode-185-play-store", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2022-05-17T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "125", - "title": "Android Basics with Compose", - "content": "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. ", - "url": "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html", - "headerImageUrl": "https://developer.android.com/images/hero-assets/android-basics-compose.svg", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "Codelab", - "topics": [ - "2", - "3", - "10" - ], - "authors": [ - "25" - ] - }, - { - "id": "126", - "title": "Android 13 Beta 1", - "content": "Beta 1 includes the latest updates to photo picker, themed app icons, improved localization and language support, and the new notification permission which requires apps targeting Android 13 to request the notification permission from the user before posting notifications. Check out the beta by visiting the Android 13 developer site.", - "url": "https://android-developers.googleblog.com/2022/04/android-13-beta-1-blog.html", - "headerImageUrl": "https://developer.android.com/about/versions/13/images/android-13-hero_1440.png", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "127", - "title": "Architecture: Entities - MAD Skills", - "content": "In this episode, Garima from GoDaddy Studio talks about entities and more specifically how creating separate entities per layer per project leads to clean and scalable model architecture.", - "url": "https://www.youtube.com/watch?v=cfak1jDKM_4", - "headerImageUrl": "https://i3.ytimg.com/vi/cfak1jDKM_4/maxresdefault.jpg", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "128", - "title": "Architecture: Live Q&A - MAD Skills", - "content": "Manuel , Yigit , TJ , and Milosz hosted a very special Architecture Q&A and answered questions from the community. Find out the answers to: “Is LiveData deprecated?”, “MVVM or MVI for Compose”, “Should we use flow in DataSources” and more.", - "url": "https://www.youtube.com/watch?v=_2BtE1P6MPE", - "headerImageUrl": "https://i3.ytimg.com/vi/_2BtE1P6MPE/maxresdefault.jpg", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [ - "23", - "34", - "38" - ] - }, - { - "id": "129", - "title": "MAD Skills: Architecture", - "content": "To wrap up the Architecture Android MAD skills series, \nManuel wrote a blog post summarizing each episode of the series! Check it out to get caught up on all things Android Architecture.", - "url": "https://android-developers.googleblog.com/2022/04/architecture-mad-skills-series-wrap-up.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwpLUhIDR7IVIgPnCayAMbm0cYAC0ktSWLT9_vWJ1au0oZK_0un_IlXfu4HixEtc4G_AOi4BkWATw6BsyFCTPtCiu2wSvnfL3OVqWVNdblp6neIuFh9N3KH02SYDBgr6hIpAU7A9KjX9mT3oFJI6uuasaYqqMg_GZgptg0aooIqLywmeTp_PrpTAOj/s1600/1_J2NKRQ4qedvMVWoxL_4ZLA.jpeg", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "23" - ] - }, - { - "id": "130", - "title": "The first developer preview of Privacy Sandbox on Android", - "content": "Privacy Sandbox is a program to help you conduct initial testing of the proposed APIs and evaluate how you might adopt them for your solutions. The Developer Preview provides additional platform APIs and services on top of the Android 13 Developer Beta release, SDK, system images, Preview APIs, API reference, and support references. See the release notes for more details on what’s included in the release.", - "url": "https://android-developers.googleblog.com/2022/04/first-preview-privacy-sandbox-android.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimeEtKqNjaaRY2oxecefVbcytzULjln30fNMxfJQfWOu6Tqy9XYQKAVkwLTeGRiPh50RBIxyA6HD86_Qm_Vpiit7eEO1ZmeZttgdsH187-cL8YE-w6NOvqYDwcn-MzIPmk0yiJy-4_kbsZ4_k3CngfP36F-U5g-PQmunzFpPAHuWtBNCsHcbP80flJ/s1600/Android_PrivacySandboxonAndroidDeveloper_4209x1253.png", - "publishDate": "2022-05-04T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "11", - "13" - ], - "authors": [ - "77" - ] - }, - { - "id": "131", - "title": "Expanding Play’s Target Level API Requirements 🎯", - "content": "Starting on November 1, 2022, apps that don’t target an API level within two years of the latest major Android release version will not be available on Google Play to new users with devices running Android OS versions newer than your app’s target API level. For example, as of this November, existing apps need to target at least API level 30, Android 11, to be available to new users on Android 12 and 13 devices.", - "url": "https://android-developers.googleblog.com/2022/04/expanding-plays-target-level-api-requirements-to-strengthen-user-security.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifh6osWctzfS76FGmd91DArGexlVFw7BNh0ZCqgSuU5aO1AU2pt2T554nkpGy8AzeY_oIOY-TWc0YsS_DwMR9yp3aV_TSrgh7-XPNAg8jSDe_8ySG4ae6D6OqVUMzPmwEoPDXvEhA09um5qahSO1cfSjWIk03bq7vUVDvDHnvt-EubXLKw_Dz2uoUI/s1600/Android-New-policy-update-to-strengthen-Google-Play-social.png", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "12" - ], - "authors": [ - "10" - ] - }, - { - "id": "132", - "title": "Google Play PolicyBytes - April 2022 policy updates", - "content": "Users who have previously installed your app from Google Play will continue to be able to discover, re-install, and use your app, even if they move to a new Android device. App updates still also need to target an API level within a year of the latest major Android release version. Expanding our target level API requirements will protect users from installing older apps that may not have the privacy and security protections in place that newer Android releases offer.", - "url": "https://www.youtube.com/watch?v=O0UwUF2DgQc", - "headerImageUrl": "https://i3.ytimg.com/vi/O0UwUF2DgQc/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "133", - "title": "Upgrading Android Attestation: Remote Provisioning 🔐", - "content": "Attestation for device integrity has been mandated since Android 8.0, and is used in a variety of services such as SafetyNet. Android 12 added the option of Remote Key Provisioning for device manufacturers, and it will be mandated in Android 13. If you’re leveraging attestation in your app, watch out for a longer certificate chain structure, a new root of trust, the deprecation of RSA attestation, and short-lived certificates/attestation keys.", - "url": "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGH6tNkY1UkXgIQluciMoaSR9hZNKAoKcRyv_UxyHbEuPRvTVfWT4A_3BQEb_HCMUALR5bScXZsIEzHiRJwrFgm9fhouknFkP5H5ngCUtdf7uiGpTuCOm5dF5rtDrjR5Vm0r9NNU4J7lzN3k0sdMQumgan-NPp2nPSgXypTqj-yqn6BBS9URGrh1F/s1600/Android-KeyAttestation-Header.png", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "134", - "title": "Architecture: Handling UI events - MAD Skills", - "content": "With this episode of MAD skills we continue with our architecture series of videos. In this video you'll learn about UI events. Developer Relations Engineer Manuel Vivo covers the different types of UI events, the best practices for handling them, and more!", - "url": "https://www.youtube.com/watch?v=lwGtp0Yr0PE", - "headerImageUrl": "https://i3.ytimg.com/vi/lwGtp0Yr0PE/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [ - "23" - ] - }, - { - "id": "135", - "title": "Architecture: The Domain Layer - MAD Skills", - "content": "In this episode of MAD skills you'll learn about the Domain Layer - an optional layer which sits between the UI and Data layers. Developer Relations Engineer Don Turner will explain how the domain layer can simplify your app architecture, making it easier to understand and test.", - "url": "https://www.youtube.com/watch?v=gIhjCh3U88I", - "headerImageUrl": "https://i3.ytimg.com/vi/gIhjCh3U88I/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [ - "42" - ] - }, - { - "id": "136", - "title": "Architecture: Organizing modules - MAD Skills", - "content": "In this episode of Architecture for Modern Android Development Skills, Emily Kager shares a tip around organizing modules in Android apps.", - "url": "https://www.youtube.com/watch?v=HB_r9wn49Gc", - "headerImageUrl": "https://i3.ytimg.com/vi/HB_r9wn49Gc/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "137", - "title": "App Excellence: Android Architecture", - "content": "How do you build a successful app? Our advice: think like a building architect. If you need help getting started, we have the perfect blueprint for success when building on Android. Check out our updated guide to Android App Architecture, and build something that your users will love.", - "url": "https://www.youtube.com/watch?v=fodD6UHjLmw", - "headerImageUrl": "https://i3.ytimg.com/vi/fodD6UHjLmw/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "138", - "title": "Accessibility on TV - Integrate with Android TV & Google TV", - "content": "Thinking about accessibility is critical when developing a quality app on Android TV OS. Ian will be covering the most common issues, the solution to these issues, and some more complex scenarios. ", - "url": "https://www.youtube.com/watch?v=GyglHvJ6LMY", - "headerImageUrl": "https://i3.ytimg.com/vi/GyglHvJ6LMY/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "16", - "14" - ], - "authors": [] - }, - { - "id": "139", - "title": "Google Play Billing - Integrate with Android TV & Google TV", - "content": "In this episode of Integrate with Android TV & Google TV, Thomas will discuss how you can monetize your Android TV app using Google Play Billing. ", - "url": "https://www.youtube.com/watch?v=gb55CjH7NHY", - "headerImageUrl": "https://i3.ytimg.com/vi/gb55CjH7NHY/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "16" - ], - "authors": [] - }, - { - "id": "140", - "title": "Android for Cars 🚗", - "content": "Android for cars has introduced media recommendations powered by Google Assistant, a progress bar for long form content, and per-item content styles to allow browsable/playable items to be individually assigned to a list or grid. Head on over to the developer documentation to learn about all of these changes.", - "url": "https://developer.android.com/cars", - "headerImageUrl": "https://developers.google.com/cars/design/images/designforcars_1920.png", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "16" - ], - "authors": [] - }, - { - "id": "141", - "title": "Google Play Academy🎓 - Go Global: Japan\n", - "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Japan, one of the largest mobile app and gaming markets, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", - "url": "https://www.youtube.com/watch?v=hY1HH-9efkg", - "headerImageUrl": "https://i3.ytimg.com/vi/hY1HH-9efkg/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "142", - "title": "Google Play Academy🎓 - Go Global: Southeast Asia", - "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Southeast Asia, a fast-growing market that spends as much as 1.5x more time on the mobile internet than any other region, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", - "url": "https://www.youtube.com/watch?v=j9VRzvDhTO0", - "headerImageUrl": "https://i3.ytimg.com/vi/j9VRzvDhTO0/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "143", - "title": "Google Play Academy🎓 - Design for All Users", - "content": "Learn how to optimize for onboarding, build accessible apps, and reduce app size to reach more users.", - "url": "https://www.youtube.com/watch?v=07NUULjEJ5A", - "headerImageUrl": "https://i3.ytimg.com/vi/07NUULjEJ5A/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "144", - "title": "Game development 🎮", - "content": "We covered how to help you monitor your game’s stability using Android vitals on Google Play Console, how to best optimize your game to improve your customer engagement during the month of Ramadan, and announced that the Indie Games Accelerator & Indie Games Festival 2022 from Google Play is coming soon, offering a way to get notified when submissions open.", - "url": "https://www.youtube.com/watch?v=m2gTnT2kCRQ", - "headerImageUrl": "https://i3.ytimg.com/vi/m2gTnT2kCRQ/maxresdefault.jpg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "17" - ], - "authors": [] - }, - { - "id": "145", - "title": "Migrating Architecture Blueprints to Jetpack Compose", - "content": "Manuel wrote about how and why we’ve Migrated our Architecture Blueprints to Jetpack Compose, and some issues we faced in doing so.", - "url": "https://medium.com/androiddevelopers/migrating-architecture-blueprints-to-jetpack-compose-8ffa6615ede3", - "headerImageUrl": "https://miro.medium.com/max/1400/1*J2NKRQ4qedvMVWoxL_4ZLA.jpeg", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4", - "3" - ], - "authors": [ - "23" - ] - }, - { - "id": "146", - "title": "The curious case of FLAG_ACTIVITY_LAUNCH_ADJACENT", - "content": "Pietro wrote about how to enable split screen use cases using the Android 7.0 FLAG_ACTIVITY_LAUNCH_ADJACENT flag to open your Activity in a new adjacent window on Android 12L. (and some supported Android 11+ devices)", - "url": "https://medium.com/androiddevelopers/the-curious-case-of-flag-activity-launch-adjacent-f1212f37a8e0", - "headerImageUrl": "https://miro.medium.com/max/1400/1*YWg6uZkqSakAb5vW6uc-gg.png", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "12" - ] - }, - { - "id": "147", - "title": "AndroidX releases 🚀", - "content": "AppCompat AppCompat-Resources Version 1.5.0-alpha01 contains a bunch of bugfixes, as well as updated nullability to match Android 13 DP2 and a few small compatibility features involving TextView, AppCompatDialog, SearchView, and SwitchCompat.\n\nNavigation Version 2.4.2 has been released with all the new bugfixes backported from the 2.5 alpha releases.", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhxtjouMCgk8sv8wkvC5Aip4muxMo4TLnfSVtZ3Hw7ZpuqXQmk-EkV9qk9PKim0yVFVFlpjEJG-vqh6gCLFkQPuf2dQk6qEdQZM_9brvuxBA0dtOUlvUh7tMIQsF11RnSnSPWOPKDIzeiixfapL2ax4YgMahJppgG_a5rjs_4QBjzzgzqsDs9Wc-Ldx=w1200-h630-p-k-no-nu", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "148", - "title": "ADB Podcast Episodes🎙184: Skia and AGSL - Shaders of Things to Come", - "content": "In this episode Tor, Chet, and Romain chat with Derek and Brian from the Skia team about Skia (the graphics layer that backs the Android Canvas APIs), pixel shaders, and the new “AGSL” API that lets you provide pixel shaders for advanced graphics effects, which was recently added as part of the Android T preview release. If you’re interested in graphics technology, this is the episode for you.", - "url": "https://adbackstage.libsyn.com/episode-184-skia-and-agsl-shaders-of-things-to-come", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/9/4/d/b/94dbe077f2f14ee640be95ea3302a6a1/ADB184_Skia_and_AGSL.png", - "publishDate": "2022-04-13T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "2", - "8" - ], - "authors": [ - "32", - "30", - "31" - ] - }, - { - "id": "149", - "title": "Android 13 DP 2 😍", - "content": "Recently we shared Android 13 Developer Preview 2 with more new features and changes for you to try in your apps! Some notable features include Developer downgradable permissions which allows your app to protect user privacy by downgrading previously granted runtime permissions, and Bluetooth LE Audio which helps users receive high fidelity audio without sacrificing battery life; it can also seamlessly switch between different use cases that were not possible with Bluetooth Classic. Check out all the new features in the post!", - "url": "https://android-developers.googleblog.com/2022/03/second-preview-android-13.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjnrShXcFkBmErmhgdmx82vJbaKBIxU6p2Yz2Vr1V7AlFkD2tGwRmx_a7tWcInPmiUh8VpPmEEqXut-EjP23lFYG9wiMO4sKBDEwbZ3MNppZOy_HW54OXO4SkdQVH08cWdi7QnTMMwGELFoPq_r7_cyaGU8fx2InJG2R-NfkqF1IRt7rKOfA8M1GhUy", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "150", - "title": "Architecture: The data layer - MAD Skills", - "content": "Jose goes over the data layer and its two components: repositories and data sources. You will dive deeper into what the roles of these two are and understand their differences. You will also learn about data immutability, error handling, threading testing and more!", - "url": "https://www.youtube.com/watch?v=r5AseKQh2ZE", - "headerImageUrl": "https://i3.ytimg.com/vi/r5AseKQh2ZE/maxresdefault.jpg", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "151", - "title": "Architecture: The UI layer - MAD Skills", - "content": "TJ covers the UI layer in this episode of MAD skills using the JetNews sample app as a case study You will learn UI Layer pipeline, UI state exposure, UI state consumption and more!", - "url": "https://www.youtube.com/watch?v=p9VR8KbmzEE", - "headerImageUrl": "https://i3.ytimg.com/vi/p9VR8KbmzEE/maxresdefault.jpg", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [ - "38" - ] - }, - { - "id": "152", - "title": "Account Linking - Integrate with Android TV & Google TV", - "content": "Miguel and Adekunle discuss account linking. ​​Google Account Linking lets you safely link a user’s Google Account with their account on your platform, granting Google applications and devices access to your services and is needed for many integrations on Android TV & Google TV. They discuss the basics of OAuth like implementing your authorization, token exchange, and revocation endpoints. You will also learn the difference between the Web OAuth, Streamlined, and App Flip linking flows.", - "url": "https://www.youtube.com/watch?v=-Fa99hpUsdk", - "headerImageUrl": "https://i3.ytimg.com/vi/-Fa99hpUsdk/maxresdefault.jpg", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "16" - ], - "authors": [] - }, - { - "id": "153", - "title": "Modern media playback on Android - Integrate with Android TV & Google TV", - "content": "Paul explores best practices for integrating and validating media sessions, the unified way for Android apps to interact with media content. MediaSessions allows different devices like smart speakers, watches, peripherals and accessories to surface and interact with playback and content metadata.", - "url": "https://www.youtube.com/watch?v=OYy41ceW59s", - "headerImageUrl": "https://i3.ytimg.com/vi/OYy41ceW59s/maxresdefault.jpg", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "16" - ], - "authors": [ - "27" - ] - }, - { - "id": "154", - "title": "FHIR SDK for Android Developers 🏥", - "content": "Community health workers in low-and-middle-income countries use mobile devices as critical tools for doing community outreach and providing crucial health services. Unfortunately, the lack of data interoperability means that patient records are fragmented and caregivers may only receive incomplete information. Last year, Google introduced a collaboration with the World Health Organization to build an open source software developer kit designed to help developers build mobile solutions using the Fast Healthcare Interoperability Resources (FHIR) global standard for healthcare data. Read the article to learn more about how this SDK can help you create apps to aid community health workers in low-and-middle-income countries.", - "url": "https://medium.com/androiddevelopers/our-fhir-sdk-for-android-developers-9f8455e0b42f", - "headerImageUrl": "https://miro.medium.com/max/1400/1*azSHuKma0fz1RxcPcqiusg.png", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "155", - "title": "Helping Users Discover Quality Apps on Large Screens 🔎", - "content": "Adoption of large screens is growing rapidly and now there are over 250M active Android tablets, foldables, and ChromeOS devices. To help people get the most from their devices, we’re making three big changes in Google Play to enable users to discover and engage with high quality apps and games: ranking and promotability changes, alerts for low quality apps, and device-specific ratings and reviews. Read all about it in the post!", - "url": "https://android-developers.googleblog.com/2022/03/helping-users-discover-quality-apps-on.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEiWXMaly6_CP_gSHmxE92yVBXUQ1X5EcTA6pdKwo_NsAtO1Ouv_RhHxG1HqtbStufdnylV51VbHI0FmmPV8lvmLAOqNzhcD2znU3vWVajQXfOlFw_kP-01jxSvrzVIXZG7SCQMiw58yUaWgmqzO-dsaso5DOeYVKnwQm3Vdu9lFmogfCkQT5u7H0sVt", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "2" - ], - "authors": [ - "61" - ] - }, - { - "id": "156", - "title": "Access Android vitals data through the new Play Developer Reporting API", - "content": "In this article Lauren talks about Android vitals which are a great way to track how your app is performing in Google Play Console. Now there are new use cases for Android vitals which include building internal dashboards, joining with other datasets for deeper analysis and automation troubleshooting and releases. Learn more about the API and how to use it in this post!", - "url": "https://android-developers.googleblog.com/2022/03/play-developer-reporting-API.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhnvMF36lJv9wDDHWLQb7AfVBajueyEuocw_9ne1jgKJAO5dgXWcAyrKa92f4miTcFoSH5usz_Jha2C1gJwJNSr6et8sZGSCnkZTgtdaKPemEfwaHJDjiurWaPtqFF3qI0aX7aRB7B9WUW1VXT_Wgkyyq8nYK7RrOy9zW4a7gROkzd3H5m9T36Bc7Ww", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "12" - ], - "authors": [] - }, - { - "id": "157", - "title": "Using performance class to optimize your user experience", - "content": "The Jetpack Core Performance library in alpha has launched! This library enables you to easily identify what a device is capable of, and tailor your user experience accordingly. As developer, this means you can reliably group devices with the same level of performance and tailor your app’s behavior to those different groups. This enables you to deliver an optimal experience to users with both more and less capable devices.", - "url": "https://android-developers.googleblog.com/2022/03/using-performance-class-to-optimize.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYa28aPEBLCSzLkir02bVSWusH5BBIiAcq_CzCx5DD3iQu5WyDLC1sZe1y5OInomon5KHJKemMCa5q6XAtfkMhljMoePuebLGDz6yRDU3cjkwMo7sV5WKe20KNzWhP1ktdOn7OxOxeiXqzeDrPwLcpoVaStm8840phqHOqDptiQ0twMsGTD2u3o0Xf/s1600/Android-using-performance-class-to-optimize-user-experience-header%20%281%29.png", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "7" - ], - "authors": [ - "42" - ] - }, - { - "id": "158", - "title": "AndroidX releases 🚀", - "content": "We have a few libraries in alpha-01 including Activity Version 1.6.0-alpha01, CustomView-Poolingcontainer Version 1.0.0-alpha01, and Junit-Gtest Version 1.0.0-alpha01.\n\nCar App Version 1.2.0-rc01 and Mediarouter Version 1.3.0-rc01 are also in rc-01.", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEi6y_NUo9gnpHYdRc7lwnbVnraBtUSIZTnIoAcHXkbq8Z0AFHBUHDI_s7HwwP2h2nTwo571RnRuXN-sUWdgJ7qkNb2MSslYiXWP3tteXooTdwAS_YzbZMTux25eLZk0kgdLtXmWTRLdolft-ZcsgGjCyJnH-CjzHsZXGy8vNVxB5oFZkBExOpBwvoDL=w1200-h630-p-k-no-nu", - "publishDate": "2022-03-29T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "159", - "title": "Discontinuing Kotlin synthetics for views", - "content": "Synthetic properties to access views were created as a way to eliminate the common boilerplate of findViewById calls. These synthetics are provided by JetBrains in the Kotlin Android Extensions Gradle plugin (not to be confused with Android KTX).", - "url": "https://android-developers.googleblog.com/2022/02/discontinuing-kotlin-synthetics-for-views.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhAba1fyHq6ddgUfT09YxU3XkAhnolKyLnXE2GmcJABVT-y8PWLKUiC7LmesY7Txak65fc6nW8T7yar9_4Uz4ezcBA_MDZ-yqBR2cj4ipSN-5l_E57exa3m9qt2MHFo_RdLWc_YDX7J7AOMkyzs43ylbGtwl6Z8GSf1zgs71Te36cQ-9Z_qgMgFroLq=w1200-h630-p-k-no-nu", - "publishDate": "2022-02-18T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2", - "10" - ], - "authors": [ - "1" - ] - }, - { - "id": "160", - "title": "Things to know from the 2022 Google for Games Developer Summit", - "content": "This week marked the 2022 Google for Games Developer Summit, Google’s biggest event of the year centered around game development. The Android team shared information around the next generation of services, tools and features to help you develop and deliver high quality games. ", - "url": "https://android-developers.googleblog.com/2022/03/GGDS-recap-blog.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhW4RL-UKUurgM2bVJRepqjKehVETjf9bqdXllyspPaWTTt8s86MGvfxlxLkDyJAnnkGr7vDpDTPx6bQbgkThYXMSaW1GQvXw9V57xybA8Y89vIE45JDElGxSNFHwOAndATPYrGmc200fkyBTRSNi7w53hTbS1ao-TSoEBFs8jvTgz6ud5Tcb1qitkt", - "publishDate": "2022-03-15T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "17" - ], - "authors": [ - "2" - ] - }, - { - "id": "161", - "title": "MAD Skills: DataStore and Introduction to Architecture💡", - "content": "Now that our MAD Skills series on Jetpack DataStore is complete, let’s do a quick wrap up of all the things we’ve covered in each episode.", - "url": "https://android-developers.googleblog.com/2022/03/jetpack-datastore-wrap-up.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgo2-I1LhMjWd1zzpIQXzjMCPoZeUZc35n43UosKDuLMyP7rIDe8cGfs23tmkSAed6Wxw9EoNTIpvvWCljermK_lCu0etlrCnONx3WeXMCGe-s8I45hYhuVo6w_Q2UTNATMTA70t2o9MS5p2pBdPFz5Ye4b2ajOJjNlW9rELtqWcEW4O1Rkzy4lfqRO", - "publishDate": "2022-03-14T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "162", - "title": "Play Time with Jetpack Compose", - "content": "Learn about Google Play Store’s strategy for adopting Jetpack Compose, how they overcame specific performance challenges, and improved developer productivity and happiness.", - "url": "https://android-developers.googleblog.com/2022/03/play-time-with-jetpack-compose.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-03-10T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3", - "12" - ], - "authors": [ - "4", - "5" - ] - }, - { - "id": "163", - "title": "App Excellence Summit 2022 ⭐", - "content": "Did you know that 54% of users who left a 1-star review in the Play Store mentioned app stability and bugs? *\n\nTo help product managers and business decision makers understand how high quality app experiences drive business growth and what tools they can use to make sound business and technical decisions, we are hosting our first Android App Excellence Summit in just a few weeks!", - "url": "https://android-developers.googleblog.com/2022/03/app-excellence-summit-2022.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEh4Vck7mqle-tLweEgrIc1WT0ycY6O6zBxv9mC1Dt1xCnJN5COTGFxDSQlIM1rbbMKIMZHPtjzXgENMGk80oxb5Mn8kTn6qO7kgUXC_N5YSB0dWxcXvQOIPHEEgNJze9g8eZrY1xgA9_oBls71NLItDJKTYeoJGEXxIBiAE_c6SkXv2jSELZEoFfqVq", - "publishDate": "2022-03-10T00:00:00.000Z", - "type": "Event 📆", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "164", - "title": "#TheAndroidShow: Tablets, Jetpack Compose, and Android 13 📹", - "content": "Last week, Florina and Huyen hosted #TheAndroidShow, where we went Behind the scenes with animations & Jetpack Compose, asked whether now is the moment to think tablet first, and covered Android 13 along with other key themes for Android this year.", - "url": "https://www.youtube.com/watch?v=WL9h46CymlU", - "headerImageUrl": "https://i.ytimg.com/vi/WL9h46CymlU/maxresdefault.jpg", - "publishDate": "2022-03-09T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "3", - "2", - "13", - "1" - ], - "authors": [ - "6" - ] - }, - { - "id": "165", - "title": "Freeing up 60% of storage for apps 💾", - "content": "App archiving will allow users to reclaim ~60% of app storage temporarily by removing parts of the app rather than uninstalling the app completely.", - "url": "https://android-developers.googleblog.com/2022/03/freeing-up-60-of-storage-for-apps.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-03-08T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "7", - "8" - ] - }, - { - "id": "166", - "title": "Demystifying Jetpack Glance for app widgets", - "content": "We recently announced the first Alpha version of Glance, initially with support for AppWidgets and now for Tiles for Wear OS. This new framework is built on top of the Jetpack Compose runtime and designed to make it faster and easier to build “glanceables” such as app widgets without having to handle a lot of boilerplate code or lifecycle events to connect different components.", - "url": "https://medium.com/androiddevelopers/demystifying-jetpack-glance-for-app-widgets-8fbc7041955c", - "headerImageUrl": "https://miro.medium.com/max/1400/1*mlswR3fyxaIG-C1OUifYVw.jpeg", - "publishDate": "2022-03-07T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2", - "19" - ], - "authors": [ - "9" - ] - }, - { - "id": "167", - "title": "Keeping Google Play safe with our key 2022 initiatives 🔒", - "content": "We shared information about what’s ahead in 2022 for Google Play’s privacy and safety initiatives to give you time to prepare.", - "url": "https://android-developers.googleblog.com/2022/03/privacy-and-security-direction.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhh3FMLL-etD7iDzhSI6CoYbuwgB9ZADjXa6A9C4aM3W-eRqj1FGfP8dyMY4i5RlMtQJD8Sx1y1NHFuaCae10iZkAs_cETaCAllzCDU075awpkAc1pkhld7uxwjTmwNdihGhB-FtySiSsf9aknd1ZULz0zkRtybX4gRUp8JCbPh2n3pPEhjK0mTjNWS", - "publishDate": "2022-03-03T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11" - ], - "authors": [ - "10" - ] - }, - { - "id": "168", - "title": "Games-Activity Version 1.1.0", - "content": "adds WindowInsets listening/querying for notch and IME response along with key and motion event filters.", - "url": "https://developer.android.com/jetpack/androidx/releases/games#1.1.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-02-23T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "17" - ], - "authors": [] - }, - { - "id": "169", - "title": "Room Version 2.5.0-alpha01", - "content": "Converted room-common, room-migration, and paging related files in room-runtime from Java to Kotlin along with a new API for multi-process lock to protect multi-process 1st time database creation and migrations", - "url": "https://developer.android.com/jetpack/androidx/releases/room#2.5.0-alpha01", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-02-23T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "9" - ], - "authors": [] - }, - { - "id": "170", - "title": "Media Version 1.6.0-alpha 01", - "content": "Adds the extras necessary to setup a signin/settings page using CarAppLibrary.", - "url": "https://developer.android.com/jetpack/androidx/releases/media#media-1.6.0-alpha01", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjo_CL5arSn2zb_YKP8hKjaMG3nqXXPQ_zn05X9FQ0XLdE2Ii6WeGG0eD_miObCRv2iz3hJ2T0lIIn3iDFyT3yN8B7NFET_fE5nhcw6MHQmOKK4G4R5XgXTkEIyqY4kjz2F5hpPscvQgsz0aRkVqSLynp-6x9HqkoldNYwDSp7kbttmh2JCW1cwUXhG=w1200-h630-p-k-no-nu", - "publishDate": "2022-02-23T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "18" - ], - "authors": [] - }, - { - "id": "171", - "title": "AppCompat-Resources Version 1.6.0-alpha01", - "content": "Adds support for customizing locales, providing backwards compatibility for the Android 13 per-language preferences API", - "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.6.0-alpha01", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhxsx2mynFgTjtKCAxJiWvIYuJF1sNFfRCPnEbBWSmQLATP_Z6Bmz81sr9WmS2CWVUqIzW4uYyRyW2wQSLR73i9WXLUzGc-LbMS-QEcQQZI5qoymfRf3pyrMnOeGuFAKsfLaAEtquvNyqA2KaO28BnF3plt0jr6kVYIyl0tkpWhxHa47CPuNvhEehQ1=w1200-h630-p-k-no-nu", - "publishDate": "2022-02-23T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "172", - "title": "Recording Video with CameraX VideoCapture API", - "content": "A picture is worth a thousand words, and CameraX ImageCapture has already made it much easier to tell your story through still images on Android. Now with the new VideoCapture API, CameraX can help you create thousands of continuous pictures to tell an even better and more engaging story!", - "url": "https://medium.com/androiddevelopers/recording-video-with-camerax-videocapture-api-a36cfd8a48c8", - "headerImageUrl": "https://miro.medium.com/max/1400/1*GZmhCFMCrG4L_mOtwSb0zA.png", - "publishDate": "2022-02-23T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [ - "11" - ] - }, - { - "id": "173", - "title": "Unbundling the stable WindowManager", - "content": "The 1.0.0 stable release of Jetpack WindowManager, the foundation for great experiences on all types of large screen devices.", - "url": "https://medium.com/androiddevelopers/unbundling-the-stable-windowmanager-a5471ff2907", - "headerImageUrl": "https://miro.medium.com/max/1400/0*dIXjHF8_-47CvYTb.png", - "publishDate": "2022-02-17T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "12" - ] - }, - { - "id": "174", - "title": "Jetpack Compose 1.1 is now stable!", - "content": "Last week we released version 1.1 of Jetpack Compose and Florina Muntenescu wrote an article giving us all the information! This release contains new features like improved focus handling, touch target sizing, ImageVector caching and support for Android 12 stretch overscroll. This also means that previously experimental APIs are now stable. Check out our recently updated samples, codelabs, and the Accompanist library!", - "url": "https://android-developers.googleblog.com/2022/02/jetpack-compose-11-now-stable.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEiEIiQOoFF-f-sDcbYOMINZw5-2R9aQjrREfiXFMGsRYODVfaz1sgdCS2C3UjgeJjCII5oyE4y97kbvQIUsl9wIx8RqTSZPSdIoCywW89lvmAJ5a15bkFOwoR9UacCEUb4CjOMy0omVMfC0CQhUfz9VMTZR4iyjDGagEZfNuMid8BT0lvarns9Tp6PC", - "publishDate": "2022-02-09T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "2" - ], - "authors": [ - "6" - ] - }, - { - "id": "175", - "title": "MAD Skills: DataStore", - "content": "The DataStore MAD Skills series rolls on! In the sixth episode, Simona Stojanovic covered DataStore: Best Practices part 2 covering DataStore-to-DataStore migration. This is used when you make significant changes to your dataset like renaming your data model values or changing their type. ", - "url": "https://medium.com/androiddevelopers/datastore-and-data-migration-fdca806eb1aa", - "headerImageUrl": "https://miro.medium.com/max/1400/0*8wsdb7Z7QxT1d4lM", - "publishDate": "2022-02-15T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "176", - "title": "DataStore and Testing", - "content": "For the final part of the DataStore series, Simona covered DataStore and testing and teaches you how to fully test your DataStore.", - "url": "https://medium.com/androiddevelopers/datastore-and-testing-edf7ae8df3d8", - "headerImageUrl": "https://miro.medium.com/max/1400/1*5_yt1M6_QEMN0OgGU8VaZw.png", - "publishDate": "2022-02-16T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "177", - "title": "Material You: Coming to more Android Devices near you", - "content": "Material You will soon be available on more Android 12 phones globally including devices by Samsung, Oppo, OnePlus and more! Material You has made the Android experience more fluid and personal than ever. Our OEM partners continue to work with us to ensure that key design APIs work consistently across the Android ecosystem so developers can benefit from a cohesive experience.", - "url": "https://android-developers.googleblog.com/2022/02/material-you-coming-to-more-android.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhDOIPFoqZ8uvg7VmH5EuY3ocfxvKZXawUQ9NczUCEtOdpw3v42vSTrpUSvHjbph5KmTlDH-XtnmGeXmCFTMaHDnRS9ibzLUHBip_XnVHUL7xv-3UrVL6plimErj_oK_KyW5ULpmj6orVTaTq9r56K0V3npQFdIrBPE7_caRWb_QA5E9FljpREWVB7Y", - "publishDate": "2022-02-10T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2", - "13" - ], - "authors": [ - "13" - ] - }, - { - "id": "178", - "title": "The first developer preview of Android 13", - "content": "We’re sharing a first look at the next release of Android, with the Android 13 Developer Preview 1. With Android 13 we’re continuing some important themes: privacy and security, as well as developer productivity. We’ll also build on some of the newer updates we made in 12L to help you take advantage of the 250+ million large screen Android devices currently running.", - "url": "https://android-developers.googleblog.com/2022/02/first-preview-android-13.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjnrShXcFkBmErmhgdmx82vJbaKBIxU6p2Yz2Vr1V7AlFkD2tGwRmx_a7tWcInPmiUh8VpPmEEqXut-EjP23lFYG9wiMO4sKBDEwbZ3MNppZOy_HW54OXO4SkdQVH08cWdi7QnTMMwGELFoPq_r7_cyaGU8fx2InJG2R-NfkqF1IRt7rKOfA8M1GhUy", - "publishDate": "2022-02-10T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "179", - "title": "AndroidX releases 🚀", - "content": "Since Compose just went stable, the Animation, Compiler, Foundation, Material, Runtime and UI Versions also went stable! Games-Text-Input and ProfileInstaller also went stable! \n\nThere are a bunch of new APIs in alpha including new Testing APIs (Test Runner, Test Monitor, Test Services and Test Orchestrator), Metrics Version and Startup Version.", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#february_9_2022", - "headerImageUrl": "https://miro.medium.com/max/1400/0*bux1xKYcB3A9pBFx", - "publishDate": "2022-02-09T00:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "3", - "8" - ], - "authors": [ - "15" - ] - }, - { - "id": "180", - "title": "DataStore best practices part 1", - "content": "learn about performing synchronous work and how to make it work with Kotlin data class serialization and Hilt.", - "url": "https://www.youtube.com/watch?v=S10ci36lBJ4", - "headerImageUrl": "https://i.ytimg.com/vi/S10ci36lBJ4/maxresdefault.jpg", - "publishDate": "2022-02-07T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "181", - "title": "All about Proto DataStore", - "content": "In this post, we will learn about Proto DataStore, one of two DataStore implementations. We will discuss how to create it, read and write data and how to handle exceptions, to better understand the scenarios that make Proto a great choice.", - "url": "https://medium.com/androiddevelopers/all-about-proto-datastore-1b1af6cd2879", - "headerImageUrl": "https://miro.medium.com/max/1400/1*UtNu7pmbt3WEA213SW9p9Q.png", - "publishDate": "2022-01-31T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "182", - "title": "Glance: Tiles for Wear OS made simple ⌚️", - "content": "Last year we announced the Wear Tiles API. To complement that Java API, we are excited to announce that support for Wear OS Tiles has been added to Glance, a new framework built on top of Jetpack Compose designed to make it easier to build for surfaces outside your Android app. As this library is in alpha, we’d love to get your feedback.", - "url": "https://android-developers.googleblog.com/2022/01/announcing-glance-tiles-for-wear-os.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-01-26T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19" - ], - "authors": [ - "16" - ] - }, - { - "id": "183", - "title": "Android Studio Bumblebee 🐝 stable", - "content": "Android Studio Bumblebee (2021.1.1) is now stable. We’ve since patched it to address some launch issues — so make sure to upgrade! It improves functionality across the typical developer workflow: Build and Deploy, Profiling and Inspection, and Design.", - "url": "https://android-developers.googleblog.com/2022/01/android-studio-bumblebee-202111-stable.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhQ7R2ySipHb8y5jNJeiIj3pE8dZfWAV7EF0wQZ4rQ65lB4MsZroAT4R_7rSfznMZ30xBMLx9_dwnt05V6I0Du0EfI7mvLicK6LwdkuZsF_Gc3sPqrZGxkojTJpHCXFI3Kvr3bLyoSjElldtt1NUpGSBzHgG3O1pvS9BR02L9R2_FYTUgPLfUoNLWYQ", - "publishDate": "2022-01-25T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5" - ], - "authors": [ - "17" - ] - }, - { - "id": "184", - "title": "All about Preferences DataStore", - "content": "In this post, we will take a look at Preferences DataStore, one of two DataStore implementations. We will go over how to create it, read and write data, and how to handle exceptions, all of which should, hopefully, provide you with enough information to decide if it’s the right choice for your app.", - "url": "https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334", - "headerImageUrl": "https://miro.medium.com/max/1400/1*UtNu7pmbt3WEA213SW9p9Q.png", - "publishDate": "2022-01-24T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "185", - "title": "Building apps for Android Automotive OS 🚘", - "content": "The Car App Library version 1.2 is already in beta, enabling app developers to start building their navigation, parking, and charging apps for Android Automotive OS. Now, developers can begin building and testing apps for these categories using the Automotive OS emulator across both Android Automotive OS and Android Auto.", - "url": "https://android-developers.googleblog.com/2022/01/building-apps-for-android-automotive-os.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEicCVDoRaflBAdKr9_Zh2cAGUB8pphAj9m0w1iN7VLizNZ6L2iNGNSnt7tvD6MP72BW8eqobZpU751t32aF47bpNDv2walZ6zzsXxyuAjCyhBl0b4o06X1j3bPi0AAU0EedqYjp5FSXMQHHzvxBedjsST3MIIFvalX3tZpgiFZgEdqbB2f_H741Irrb=w1200-h630-p-k-no-nu", - "publishDate": "2022-01-27T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "15" - ], - "authors": [ - "18" - ] - }, - { - "id": "186", - "title": "Navigation 2.4 is stable ", - "content": "It’s been rewritten in Kotlin, with two pane integration, Navigation routes + Kotlin DSL improvements, Navigation Compose’s first stable release, and multiple back stack support.", - "url": "https://developer.android.com/jetpack/androidx/releases/navigation#2.4.0", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhsJgTKhUlIb-1X_G1rWQLCxpd0KMmGTUgqUfSNr4__CsxxjiOdJgJHCtgO9dG8mZdwzAHat9HyIcMsvA-fS0o6T0-_ut_Ej74hKfn09AJUPNc3YscwfGG6hqFS-W_oTczgtd1aGNzpCdDDo4b4lrUM3n8OsFKjvslqE6pHRY3w0aZSTHsaYytSnQSA=w1200-h630-p-k-no-nu", - "publishDate": "2022-01-26T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "187", - "title": "Google Maps with Jetpack Compose", - "content": "A project which contains Jetpack Compose components for the Google Maps SDK for Android.\n\n", - "url": "https://github.com/googlemaps/android-maps-compose", - "headerImageUrl": "https://opengraph.githubassets.com/0952eadfbb07f5ce9f631fd0312d87e8f0e2557df01bac3b587311ca864cf836/googlemaps/android-maps-compose", - "publishDate": "2022-02-11T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "3" - ], - "authors": [] - }, - { - "id": "188", - "title": "Improving App Performance with Baseline Profiles", - "content": "In this blog post we’ll discuss Baseline Profiles and how they improve app and library performance, including startup time by up to 40%. While this blogpost focuses on startup, baseline profiles also significantly improve jank as well.", - "url": "https://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-01-28T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [ - "19", - "20", - "21" - ] - }, - { - "id": "189", - "title": "Smule Adopts Google’s Oboe to Improve Recording Quality & Completion Rates", - "content": "As the most downloaded singing app of all time, Smule Inc. has been investing on Android to improve the overall audio quality and, more specifically, to reduce latency, i.e. allowing singers to hear their voices in the headset as they perform. The teams specialized in Audio and Video allocated a significant part of 2021 into making the necessary changes to convert the Smule application used by over ten million Android users from using the OpenSL audio API to the Oboe audio library, enabling roughly a 10%+ increase in recording completion rate.", - "url": "https://android-developers.googleblog.com/2022/02/smule-adopts-googles-oboe-to-improve.html", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-02-02T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [] - }, - { - "id": "190", - "title": "Guide to background work", - "content": "Do you use coroutines or WorkManager for background work? The team updated the guide to background work to help you choose which library is best for your use case. It depends on whether or not the work is persistent, and if it needs to run immediately, it’s long running, or deferrable.", - "url": "https://developer.android.com/guide/background", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-02-11T00:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "4" - ], - "authors": [] - }, - { - "id": "191", - "title": "Accessibility best practices", - "content": "If you work on Android TV, you should be aware of the accessibility best practices that the team created. It provides recommendations for both native and non-native apps. Get to know why accessibility is important for your TV app, how to evaluate your apps when TalkBack is used, how to adopt system caption settings, and more!", - "url": "https://developer.android.com/training/tv/accessibility", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2022-02-11T00:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "16", - "14" - ], - "authors": [] - }, - { - "id": "192", - "title": "TalkBack - the Google screen reader", - "content": "Next up in the Accessibility series is TalkBack, the Google screen reader! In this video, learn what TalkBack is, how to set it up, how to navigate through your app with it, and how you can use it to improve the Accessibility of your app.", - "url": "https://www.youtube.com/watch?v=_1yRVwhEv5I", - "headerImageUrl": "https://i.ytimg.com/vi/_1yRVwhEv5I/maxresdefault.jpg", - "publishDate": "2022-01-21T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [] - }, - { - "id": "193", - "title": "ADB Podcast 182: Large screens are a big deal", - "content": "Clara, Florina and Daniel join your usual hosts to talk about large screens, what they are and what they mean for app developers. You will also learn about the resources at your disposal to build high quality experiences on large screen devices: from samples and guidance to canonical layouts and new APIs such as window size classes. Disclaimer: Florina is very excited about this, don’t miss the epic Large screens! Large screens! Large screens! intro!", - "url": "https://adbackstage.libsyn.com/episode-182-large-screens-are-a-big-deal", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2022-02-01T00:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "194", - "title": "Jetpack Alpha for Glance Widgets 🔍", - "content": "We made the first release of Jetpack Glance available, a new framework designed to make it faster and easier to build app widgets for the home screen and other surfaces. Glance offers similar modern, declarative Kotlin APIs that you are used to with Jetpack Compose, helping you build beautiful, responsive app widgets with way less code. Glance provides a base-set of its own Composables to help build “glanceable” experiences — starting today with app widget components but with more coming. Using the Jetpack Compose runtime, Glance translates these Composables into RemoteViews that can be displayed in an app widget", - "url": "https://android-developers.googleblog.com/2021/12/announcing-jetpack-glance-alpha-for-app.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgol-A5cMCZY79MH5v0axcekWIVJ--ymPUe0U5Q4BLsC0BA1LTbWIlZ76XWi2cHjxHVu-kbpv0o2QJWBjNAda_93Ah7AW_PcAgz9o082cd6zyTJZAM8HjQnrZ69A6CaKQaCFuf2LLi4p6xRvS_WUn9tVA2K2wmV3_qB6JDKnFNhO3Guvn5tPc_SuoaY", - "publishDate": "2021-12-15T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8" - ], - "authors": [ - "9" - ] - }, - { - "id": "195", - "title": "Jetpack Watch Face Library ⌚", - "content": "We launched the Jetpack Watch Face library written from the ground up in Kotlin, including all functionality from the Wearable Support Library along with many new features such as: Watch face styling which persists across both the watch and phone (with no need for your own database or companion app); Support for a WYSIWYG watch face configuration UI on the phone; Smaller, separate libraries (that only include what you need); Battery improvements through promoting good battery usage patterns out of the box, such as automatically reducing the interactive frame rate when the battery is low; New screenshot APIs so users can see previews of their watch face changes in real time on both the watch and phone.\n\nIf you are still using the Wearable Support Library, we strongly encourage migrating to the new Jetpack libraries to take advantage of the new APIs and upcoming features and bug fixes.", - "url": "https://android-developers.googleblog.com/2021/12/develop-watch-faces-with-stable-jetpack.html", - "headerImageUrl": "https://1.bp.blogspot.com/-P4S1eEhqouE/YaaFy_bGD1I/AAAAAAAARNA/-w5O05Mppo8pe0hoeMC1yDNRWiX_mnTOgCLcBGAsYHQ/s0/image1.png", - "publishDate": "2021-12-01T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "3", - "8" - ], - "authors": [ - "22" - ] - }, - { - "id": "196", - "title": "Rebuilding our Guide to App Architecture 📐", - "content": "We launched a revamped guide to app architecture which includes best practices. As Android apps grow in size, it’s important to design the code with an architecture in place that allows the app to scale, improves quality and robustness, and makes testing easier. The guide contains pages for UI, domain, and data layers including deep dives into more complex topics, such as how to handle UI events. We also have a learning pathway to walk you through it.", - "url": "https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgnJ0CCtKClhEOE_BDOoWiXGr2eA6LWjn-RPvFjFx8Va97f_1_xCmpF3uI_bUILoQPqJUDlXUbIRVPjvi3oCiFtRVZlcAAkHBa1cJlufG5OvmeovQeiHgH9bLhxREufi-fw7FnxIcmxGmzWuW0DmYUZolsM6rywTSZIm3KtI6yx9jSIeRpuYzRZubke", - "publishDate": "2021-12-14T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "23" - ] - }, - { - "id": "197", - "title": "Google Play Games on PC Beta 🎮", - "content": "We announced that we’re opening sign-ups for Google Play Games on PC as a beta in Korea, Taiwan, and Hong Kong, allowing users participating in the beta to play a catalog of Google Play games on their PC via a standalone application built by Google. The developer site has a form to express interest, along with information about bringing your Android game to PCs. It involves many of the same updates that you do to optimize your game for Chrome OS devices, such as support for Mouse and Keyboard controls.", - "url": "https://developers.googleblog.com/2022/01/googleplaygames.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgsNv-PVLNLlX2SYd2p5DwTN2Jxwb54Rc7Ekbm0LgcFuwHBrF_5Y-DiUblL9oTjmeJ1Y44nPRMMkH5K-xlC0OApgUGxqBpUcfuV1LYPVvKsI67BKTpc_gNhaHsNda6Q1Uk1UvTznmMydqNHtXSqTgSJbjpQCoTGZM_ZLXlkGwMoBFfnMQkAIdl2zjsC", - "publishDate": "2022-01-19T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "17" - ], - "authors": [ - "24" - ] - }, - { - "id": "198", - "title": "MAD Skills: Gradle 🐘", - "content": "Murat covered building custom plugins in more depth, including the Artifact API in addition to the Variant API covered previously. It demonstrates building a plugin which automatically updates the version code specified in the app manifest with the git version. With the AGP 7.0 release, you can use these APIs to control build inputs, read, modify, or even replace intermediate and final artifacts.", - "url": "https://medium.com/androiddevelopers/gradle-and-agp-build-apis-taking-your-plugin-to-the-next-step-95e7bd1cd4c9", - "headerImageUrl": "https://miro.medium.com/max/1400/0*WkRft2aAKv19MoIm.jpeg", - "publishDate": "2021-12-01T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "199", - "title": "Gradle and AGP Build APIs: Community tip - MAD Skills", - "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Alex Saveau walks you through manipulating Android build artifacts with the Android Gradle Plugin (AGP) and Gradle APIs.", - "url": "https://www.youtube.com/watch?v=8SFfffaB0CU", - "headerImageUrl": "https://i3.ytimg.com/vi/8SFfffaB0CU/maxresdefault.jpg", - "publishDate": "2021-12-15T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "26" - ] - }, - { - "id": "200", - "title": "Gradle and AGP Build APIs: Taking your plugin to the next step - MAD Skills", - "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Murat will discuss Gradle tasks, providers, properties, and basics of task inputs and outputs. Next, you will be able to take your plugin a step further and learn how to get access to various build artifacts using the new Artifact API. ", - "url": "https://www.youtube.com/watch?v=SB4QlngQQW0", - "headerImageUrl": "https://i3.ytimg.com/vi/SB4QlngQQW0/maxresdefault.jpg", - "publishDate": "2021-11-29T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "201", - "title": "MAD Skills Gradle and AGP build APIs Wrap Up!", - "content": "This wrap-up post summarizes the whole MAD Skills Gradle series", - "url": "https://android-developers.googleblog.com/2021/12/mad-skills-gradle-and-agp-build-apis.html", - "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgo1Fw61B9qtQESKdVJzcNXOG0RzhA2k85zkDMDNidBiQY7B6uguHXQ9t9IPB9BiHS0WTB1b4fwIgeN5zEIJrmznF9pt5lu9186wvXxJ3IKfLi8Fci8LyMDbQKGYc7nnijJ9_lhrNHtRQamaF2GTSXyJq5_lQk7we3cSfSviOxhgKN9TscMJaGgdMZJ", - "publishDate": "2021-12-16T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "202", - "title": "MAD Skills: DataStore 🗄️", - "content": "Simona began MAD Skills: DataStore. DataStore is a thread-safe, non-blocking library in Android Jetpack that provides a safe and consistent way to store small amounts of data, such as preferences or application state, replacing SharedPreferences. It provides an implementation that stores typed objects backed by protocol buffers (Proto DataStore) and an implementation that stores key-value pairs (Preferences DataStore).", - "url": "https://www.youtube.com/watch?v=9ws-cJzlJkU", - "headerImageUrl": "https://i3.ytimg.com/vi/9ws-cJzlJkU/maxresdefault.jpg", - "publishDate": "2022-01-18T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "9" - ], - "authors": [ - "3" - ] - }, - { - "id": "203", - "title": "AndroidX releases 🚀", - "content": "Since the last Now in Android episode, a lot of libraries were promoted to stable! Compose ConstraintLayout brings support for ConstraintLayout syntax to Compose. We also released CoordinatorLayout 1.2, Car App 1.1.0, Room 2.4.0, Sqlite 2.2.0, Collection 1.2.0, and Wear Watchface 1.0.0.\n\nOur first alpha of Jetpack Compose 1.2 was released, along with alphas for Glance 1.0.0, Core-Ktx 1.8.0, WorkManager 2.8.0, Mediarouter 1.3.0, Emoji2 1.1.0, Annotation 1.4.0, Core-RemoteViews, Core-Peformance, and more.", - "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#december_1_2021", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-12-01T00:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "8" - ], - "authors": [] - }, - { - "id": "204", - "title": "Jetnews for every screen", - "content": "Alex wrote about the recent updates to Jetnews that improves its behavior across big and small mobile devices. It describes our design and development process so that you can learn our philosophy and associated implementation steps for building an application optimized for all screens with Jetpack Compose, including how to build a list/detail layout.", - "url": "https://medium.com/androiddevelopers/jetnews-for-every-screen-4d8e7927752", - "headerImageUrl": "https://miro.medium.com/max/1400/1*678DlYtu4G7wFrq30FQ7Mw.png", - "publishDate": "2022-01-18T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "22" - ] - }, - { - "id": "205", - "title": "Simplifying drag and drop", - "content": "Paul wrote about drag & drop, and how the Android Jetpack DragAndDrop library alpha makes it easier to handle data dropped into your app.", - "url": "https://medium.com/androiddevelopers/simplifying-drag-and-drop-3713d6ef526e", - "headerImageUrl": "https://miro.medium.com/max/1400/1*pUe4RBLe7FVlISDtAqeQ4Q.png", - "publishDate": "2021-12-15T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8" - ], - "authors": [ - "27" - ] - }, - { - "id": "206", - "title": "Accessibility series 🌐: Handling content that times out - Accessibility on Android", - "content": "The accessibility series continues on, beginning with an episode on how to properly implement UI elements that disappear after a set amount of time.", - "url": "https://www.youtube.com/watch?v=X97P6Y8WHl0", - "headerImageUrl": "https://i3.ytimg.com/vi/X97P6Y8WHl0/maxresdefault.jpg", - "publishDate": "2021-12-03T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "28" - ] - }, - { - "id": "207", - "title": "Accessibility series 🌐: Acessibility Scanner", - "content": "We also cover how Accessibility Scanner can help you improve your app for all users by suggesting improvements in areas of accessibility.", - "url": "https://www.youtube.com/watch?v=i1gMzQv0hWU", - "headerImageUrl": "https://i3.ytimg.com/vi/i1gMzQv0hWU/maxresdefault.jpg", - "publishDate": "2021-12-10T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "28" - ] - }, - { - "id": "208", - "title": "Accessibility series 🌐: Accessibility test framework and Espresso - Accessibility on Android", - "content": "We investigate how Espresso and the Accessibility Test Framework can help you create automated accessibility tests.", - "url": "https://www.youtube.com/watch?v=DLN2s16HwcE", - "headerImageUrl": "https://i3.ytimg.com/vi/DLN2s16HwcE/maxresdefault.jpg", - "publishDate": "2021-12-22T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "28" - ] - }, - { - "id": "209", - "title": "Android TV & Google TV 📺", - "content": "Mayuri covered best practices for the Watch Next API on Android TV & Google TV, which increases engagement with your app by allowing your content to show up in the Watch Next row.", - "url": "https://www.youtube.com/watch?v=QFMIP5GOo70", - "headerImageUrl": "https://i3.ytimg.com/vi/QFMIP5GOo70/maxresdefault.jpg", - "publishDate": "2022-01-14T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "16" - ], - "authors": [ - "29" - ] - }, - { - "id": "210", - "title": "ADB Podcast 179: Flibberty Widget", - "content": "In this episode, Chet and Romain talked with Nicole McWilliams and Petr Čermák from the London engineering office about their work on App Widgets and Digital Wellbeing.", - "url": "https://adbackstage.libsyn.com/flibberty-widget", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/4/0/e/c/40ec1fb11096bffed959afa2a1bf1c87/adb-180-flibberty-widget.png", - "publishDate": "2021-11-30T00:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "13" - ], - "authors": [ - "30", - "31", - "32", - "33" - ] - }, - { - "id": "211", - "title": "ADB Podcast 180: Kotlin Magic Platform", - "content": "In this episode, we chat with Yigit Boyar from the Android Toolkit Team about Kotlin multi platform, while Romain provides light background music on his piano.", - "url": "https://adbackstage.libsyn.com/episode-180-kotlin-magic-platform", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/2/6/2/5/262599d4ce76d20fa04421dee9605cbd/adb-181-kmp.png", - "publishDate": "2021-12-16T00:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "10" - ], - "authors": [ - "30", - "31", - "32", - "34" - ] - }, - { - "id": "212", - "title": "ADB Podcast 181: Architecture → Fewer bugs at the end", - "content": "In this episode, we chat with Yigit Boyar (again!) from the Android Toolkit Team and Manuel Vivo from the Developer Relations team about application architecture. The team has released new architecture guidance, and we talk about that guidance here, as well as how our architecture recommendations apply in the new Jetpack Compose world.", - "url": "https://adbackstage.libsyn.com/episode-181-architecture-fewer-bugs-at-the-end", - "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/8/d/1/3/8d137b65f392a68c27a2322813b393ee/ADB_181_Architecture.png", - "publishDate": "2022-01-11T00:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "4" - ], - "authors": [ - "31", - "31", - "32", - "23" - ] - }, - { - "id": "213", - "title": "Android 12", - "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). We introduced a new design language called Material You. We reduced the CPU time used by core system services, added performance class device capabilities, and added new features to improve performance. Users have more control of their privacy with the Privacy Dashboard and other new security and privacy features. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", - "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", - "headerImageUrl": "https://1.bp.blogspot.com/-mGlzRmn42Rs/YVstltyrboI/AAAAAAAAK3A/44QpoNJDeuoHhlgrRJSbk0L_ZopgFDLFACLcBGAsYHQ/s0/Android%2B12%2Blogo.png", - "publishDate": "2021-10-03T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "214", - "title": "Compose", - "content": "Jetpack Compose, Android’s modern, native UI toolkit became stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system. You can learn more about working with Compose in the Compose learning path and see where we’re going in future Compose releases in the Compose roadmap.", - "url": "https://developer.android.com/jetpack/compose", - "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", - "publishDate": "2021-12-07T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "3" - ], - "authors": [] - }, - { - "id": "215", - "title": "Training", - "content": "This year, the Android Training Team released the final four new units of Android Basics in Kotlin.", - "url": "https://developer.android.com/courses/android-basics-kotlin/course", - "headerImageUrl": "https://developer.android.com/images/hero-assets/android-basics-kotlin.svg", - "publishDate": "2021-12-07T00:00:00.000Z", - "type": "Codelab", - "topics": [ - "10" - ], - "authors": [] - }, - { - "id": "216", - "title": "Introduction to Kotlin and Jetpack ", - "content": "Learn the basics of Jetpack KTX libraries, how to simplify callbacks with coroutines and Flow, and how to use and test Room/WorkManager APIs.", - "url": "https://youtu.be/nw7nnlHDkHw?list=PLWz5rJ2EKKc98e0f5ZbsgB63MdjZTFgsy", - "headerImageUrl": "https://i3.ytimg.com/vi/nw7nnlHDkHw/maxresdefault.jpg", - "publishDate": "2021-12-14T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "10", - "8" - ], - "authors": [ - "6" - ] - }, - { - "id": "217", - "title": "Introduction to Motion Layout", - "content": "Learn how to use MotionLayout and its design tool to create rich, animated experiences.", - "url": "https://www.youtube.com/watch?v=M1jE3W3_NTQ&list=PLWz5rJ2EKKc_PEOEHNBEyy6tPX1EgtUw2", - "headerImageUrl": "https://i3.ytimg.com/vi/M1jE3W3_NTQ/maxresdefault.jpg", - "publishDate": "2022-01-19T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "10", - "2", - "8" - ], - "authors": [ - "35" - ] - }, - { - "id": "218", - "title": "Introduction to WorkManager", - "content": "Learn how to schedule critical background work with WorkManager: from basic usage, threading, custom configuration and more.", - "url": "https://www.youtube.com/watch?v=NtpgWjiXEfg&list=PLWz5rJ2EKKc_J88-h0PhCO_aV0HIAs9Qk", - "headerImageUrl": "https://i3.ytimg.com/vi/NtpgWjiXEfg/maxresdefault.jpg", - "publishDate": "2022-03-01T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8" - ], - "authors": [ - "36" - ] - }, - { - "id": "219", - "title": "Introduction to Navigation", - "content": "Learn the basics of the Navigation component, specific features of the tool and the APIs to create and navigate to destinations.", - "url": "https://www.youtube.com/watch?list=PLWz5rJ2EKKc9VpBMZUS9geQtc5RJ2RsUd&v=fiQiMy0HzsY&feature=emb_title", - "headerImageUrl": "https://i3.ytimg.com/vi/fiQiMy0HzsY/maxresdefault.jpg", - "publishDate": "2022-03-25T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8" - ], - "authors": [ - "31" - ] - }, - { - "id": "220", - "title": "Introduction to Performance", - "content": "Learn about using system tracing and sampling profiling to debug performance issues in apps.", - "url": "https://www.youtube.com/watch?v=_5LgIrd4O5g&list=PLWz5rJ2EKKc-xjSI-rWn9SViXivBhQUnp", - "headerImageUrl": "https://i3.ytimg.com/vi/_5LgIrd4O5g/maxresdefault.jpg", - "publishDate": "2021-07-18T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "7" - ], - "authors": [ - "37" - ] - }, - { - "id": "221", - "title": "Introduction to Hilt", - "content": "Learn how to add and use Hilt for dependency injection in your Android app, best practices for testing with Hilt, and more advanced content.", - "url": "https://www.youtube.com/watch?v=mnMCgjuMJPA&list=PLWz5rJ2EKKc_9Qo-RBRYhVmME1iR4oeTK", - "headerImageUrl": "https://i3.ytimg.com/vi/mnMCgjuMJPA/maxresdefault.jpg", - "publishDate": "2021-08-22T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8" - ], - "authors": [ - "23" - ] - }, - { - "id": "222", - "title": "Paging", - "content": "Learn the basics of paging, from the core types to binding them to your UI elements.", - "url": "https://www.youtube.com/watch?v=Pw-jhS-ucYA&list=PLWz5rJ2EKKc9L-fmWJLhyXrdPi1YKmvqS", - "headerImageUrl": "https://i3.ytimg.com/vi/Pw-jhS-ucYA/maxresdefault.jpg", - "publishDate": "2021-09-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8" - ], - "authors": [ - "38" - ] - }, - { - "id": "223", - "title": "Introduction to Gradle and AGP Build APIs\n", - "content": "Learn how to configure your build, customize the build process to your needs and how to write your own plugins to extend your build even further.", - "url": "https://www.youtube.com/watch?v=mk0XBWenod8&list=PLWz5rJ2EKKc8fyNmwKXYvA2CqxMhXqKXX", - "headerImageUrl": "https://i3.ytimg.com/vi/mk0XBWenod8/maxresdefault.jpg", - "publishDate": "2021-11-15T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5", - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "224", - "title": "Google I/O", - "content": "At I/O we released updates in Jetpack, Compose, Android Studio tooling, Large screens, Wear OS, Testing, and more! Get caught up on all the Android videos from I/O!", - "url": "https://www.youtube.com/watch?v=D_mVOAXcrtc", - "headerImageUrl": "https://i3.ytimg.com/vi/D_mVOAXcrtc/maxresdefault.jpg", - "publishDate": "2021-05-17T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "225", - "title": "Android Dev Summit", - "content": "At Android Dev Summit we released updates on privacy and security, large screens, Android 12, Google Play & Games, Building across screens, Jetpack Compose, Modern Android Development and more. Check out all the videos from ADS!", - "url": "https://www.youtube.com/watch?v=WZgR5Yf1iq8", - "headerImageUrl": "https://i3.ytimg.com/vi/WZgR5Yf1iq8/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "226", - "title": "Conveying state for Accessibility", - "content": "In this episode of the Accessibility series, you can learn more about the StateDescription API, when to use stateDescription and contentDescription, and how to represent error states to the end user.", - "url": "https://youtu.be/JvWM2PjLJls", - "headerImageUrl": "https://i.ytimg.com/vi/JvWM2PjLJls/maxresdefault.jpg", - "publishDate": "2021-11-30T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [ - "39" - ] - }, - { - "id": "227", - "title": "Take your Gradle plugin to the next step", - "content": "This third and last episode of the Gradle MAD Skills series teaches you how to get access to various build artifacts using the new Artifact API.", - "url": "https://youtu.be/SB4QlngQQW0", - "headerImageUrl": "https://i.ytimg.com/vi/SB4QlngQQW0/maxresdefault.jpg", - "publishDate": "2021-11-29T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "228", - "title": "How to write a Gradle plugin", - "content": "In this second episode of the Gradle MAD Skills series, Murat explains how to write your own custom Gradle plugin.", - "url": "https://youtu.be/LPzBVtwGxlo", - "headerImageUrl": "https://i.ytimg.com/vi/LPzBVtwGxlo/maxresdefault.jpg", - "publishDate": "2021-11-22T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "229", - "title": "Convert YUV to RGB for CameraX Image Analysis", - "content": "Learn about a new feature in CameraX to convert YUV, the format that CameraX produces, to RGB used for image analysis capabilities available in TensorFlow Lite, for example. Read the blog post for more information about these formats and how to use the new conversion feature.", - "url": "https://medium.com/androiddevelopers/convert-yuv-to-rgb-for-camerax-imageanalysis-6c627f3a0292", - "headerImageUrl": "https://miro.medium.com/max/1400/1*cuOorbZgMbRvkSSGuDGccw.png", - "publishDate": "2021-11-19T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8", - "18" - ], - "authors": [ - "40" - ] - }, - { - "id": "230", - "title": "AppCompat, Activity, and Fragment to support multiple back stacks", - "content": "The 1.4.0 release of these libraries brings stable support for multiple back stacks.", - "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.4.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8", - "2" - ], - "authors": [] - }, - { - "id": "231", - "title": "Emoji2 adds support for modern emojis", - "content": "The 1.0 stable release of Emoji2 allows you to use modern emojis in your app.", - "url": "https://developer.android.com/jetpack/androidx/releases/emoji2#1.0.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8", - "2" - ], - "authors": [] - }, - { - "id": "232", - "title": "Lifecycle introduces lifecycle-aware coroutine APIs", - "content": "The new 2.4 release of Lifecycle introduces repeatOnLifecycle and flowWithLifecycle.", - "url": "https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8", - "4" - ], - "authors": [] - }, - { - "id": "233", - "title": "Paging release brings changes to LoadState", - "content": "The new 3.1 release of Paging changes the behavior of LoadState.", - "url": "https://developer.android.com/jetpack/androidx/releases/paging#3.1.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8", - "2" - ], - "authors": [] - }, - { - "id": "234", - "title": "Wear tiles released as 1.0 stable", - "content": "The library that you use to build custom tiles for Wear OS devices is now stable.", - "url": "https://developer.android.com/jetpack/androidx/releases/wear-tiles#1.0.0", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "8", - "19" - ], - "authors": [] - }, - { - "id": "235", - "title": "About Custom Accessibility Actions", - "content": "The accessibility series continues on with more information on how to create custom accessibility actions to make your apps more accessible. You can provide a custom action to the accessibility services and implement logic related to the action. For more information, check out the following episode!", - "url": "https://youtu.be/wWDYIGk0Kdo", - "headerImageUrl": "https://i.ytimg.com/vi/wWDYIGk0Kdo/maxresdefault.jpg", - "publishDate": "2021-11-17T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [ - "39" - ] - }, - { - "id": "236", - "title": "Improving App Startup: Lessons from the Facebook App", - "content": "Improving app startup time is not a trivial task and requires a deep understanding of things that affect it. This year, the Android team and the Facebook app team have been working together on metrics and sharing approaches to improve app startup. Read more about the findings in this blog post.", - "url": "https://android-developers.googleblog.com/2021/11/improving-app-startup-facebook-app.html", - "headerImageUrl": "https://1.bp.blogspot.com/-5VyrQpFJufM/YaVKxf_DanI/AAAAAAAALS4/ybeza_emDKoKP0gjiNkqfDS_ltwo0075ACLcBGAsYHQ/w1200-h630-p-k-no-nu/AppExcellence_Editorial_LessonsFromFBApp_4209x1253-01%2B%25281%2529%2B%25281%2529.png", - "publishDate": "2021-11-16T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "7" - ], - "authors": [] - }, - { - "id": "237", - "title": "Gradle series kicks off", - "content": "Murat introduces the Gradle series and everything you'll learn in it.", - "url": "https://youtu.be/mk0XBWenod8", - "headerImageUrl": "https://i.ytimg.com/vi/mk0XBWenod8/maxresdefault.jpg", - "publishDate": "2021-11-15T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "238", - "title": "Intro to Gradle and AGP", - "content": "In the first episode of the Gradle MAD Skills series, Murat explains how the Android build system works, and how to configure your build.", - "url": "https://youtu.be/GjPS4xDMmQY", - "headerImageUrl": "https://i.ytimg.com/vi/GjPS4xDMmQY/maxresdefault.jpg", - "publishDate": "2021-11-15T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "5" - ], - "authors": [ - "25" - ] - }, - { - "id": "239", - "title": "ADB Podcast episode 179 Hosts 3, Guests 0", - "content": "Chet, Romain and Tor sit down to chat about the Android Developer Summit, and in particular all the new features arriving in Android Studio, along with a few other topics like Chet’s new jank stats library, the Android 12L release, and more.", - "url": "https://adbackstage.libsyn.com/episode-178-hosts-3-guests-0", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2021-11-15T00:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "5", - "7", - "13" - ], - "authors": [ - "31" - ] - }, - { - "id": "240", - "title": "The problem with emojis and how emoji2 can help out", - "content": "Meghan wrote about the new emoji2 library that just became stable.", - "url": "https://medium.com/androiddevelopers/support-modern-emoji-99f6dea8e57f", - "headerImageUrl": "https://miro.medium.com/max/1400/1*yAOOlpXKKUl5nWWsPkNb7g.png", - "publishDate": "2021-11-12T00:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8", - "2" - ], - "authors": [ - "15" - ] - }, - { - "id": "241", - "title": "Paging Q&A", - "content": "In this live session, TJ and Dustin answered your questions in the usual live Q&A format.", - "url": "https://youtu.be/8i6vrlbIVCc", - "headerImageUrl": "https://i.ytimg.com/vi/8i6vrlbIVCc/maxresdefault.jpg", - "publishDate": "2021-11-11T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "242", - "title": "Thanks for helping us reach 1M YouTube Subscribers", - "content": "Thank you everyone for following the Now in Android series and everything the Android Developers YouTube channel has to offer. During the Android Developer Summit, our YouTube channel reached 1 million subscribers! Here’s a small video to thank you all.", - "url": "https://youtu.be/-fJ6poHQrjM", - "headerImageUrl": "https://i.ytimg.com/vi/-fJ6poHQrjM/maxresdefault.jpg", - "publishDate": "2021-11-09T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "243", - "title": "Community tip on Paging", - "content": "Tips for using the Paging library from the developer community", - "url": "https://youtu.be/r5JgIyS3t3s", - "headerImageUrl": "https://i.ytimg.com/vi/r5JgIyS3t3s/maxresdefault.jpg", - "publishDate": "2021-11-08T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "244", - "title": "Transformations and customisations in the Paging Library", - "content": "A demonstration of different operations that can be performed with Paging. Transformations like inserting separators, when to create a new pager, and customisation options for consuming PagingData.", - "url": "https://youtu.be/ZARz0pjm5YM", - "headerImageUrl": "https://i.ytimg.com/vi/ZARz0pjm5YM/maxresdefault.jpg", - "publishDate": "2021-11-01T00:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [ - "38" - ] - }, - { - "id": "245", - "title": "New Compose for Wear OS codelab", - "content": "In this codelab, you can learn how Wear OS can work with Compose, what Wear OS specific composables are available, and more!", - "url": "https://developer.android.com/codelabs/compose-for-wear-os", - "headerImageUrl": "https://developer.android.com/codelabs/compose-for-wear-os/img/4d28d16f3f514083.png", - "publishDate": "2021-10-27T23:00:00.000Z", - "type": "Codelab", - "topics": [ - "3", - "19" - ], - "authors": [ - "41" - ] - }, - { - "id": "246", - "title": "Building apps which are private by design", - "content": "Sara N-Marandi, product manager, and Yacine Rezgui, developer relations engineer, provided guidelines and best practices on how to build apps that are private by design, covered new privacy features in Android 12 and previewed upcoming Android concepts.", - "url": "https://youtu.be/hBVwr2ErQCw", - "headerImageUrl": "https://i.ytimg.com/vi/hBVwr2ErQCw/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "11" - ], - "authors": [] - }, - { - "id": "247", - "title": "Memory Safety Tools", - "content": "Serban Constantinescu, product manager, talked about the Memory Safety Tools that became available starting in Android 11 and have continued to evolve in Android 12. These tools can help address memory bugs and improve the quality and security of your application.", - "url": "https://youtu.be/JqLcTFpXreg", - "headerImageUrl": "https://i.ytimg.com/vi/JqLcTFpXreg/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "11" - ], - "authors": [] - }, - { - "id": "248", - "title": "Increasing User Transparency with Privacy Dashboard", - "content": "Android is ever evolving in its quest to protect users’ privacy. In Android 12, the platform increases transparency by introducing Privacy Dashboard, which gives users a simple and clear timeline view of the apps that have accessed location, microphone and camera within the past 24 hours. ", - "url": "https://medium.com/androiddevelopers/increasing-user-transparency-with-privacy-dashboard-23064f2d7ff6", - "headerImageUrl": "https://miro.medium.com/max/1400/1*cgaSAY9AvPWlndLimzIIzQ.png", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11" - ], - "authors": [ - "15" - ] - }, - { - "id": "249", - "title": "The most unusual and interesting security issues addressed last year", - "content": "Lilian Young, software engineer, presented a selection of the most unusual, intricate, and interesting security issues addressed in the last year. Developers and researchers are able to contribute to the security of the Android platform by submitting to the Android Vulnerability Rewards Program.", - "url": "https://medium.com/androiddevelopers/now-in-android-50-ads-special-9934422f8dd1", - "headerImageUrl": "https://miro.medium.com/max/1400/0*6h0XYdyki_1jfImJ", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11" - ], - "authors": [ - "43" - ] - }, - { - "id": "250", - "title": "New Data Safety section in the Play Console", - "content": "The new Data safety section will give you a simple way to showcase your app’s overall safety. It gives you a place to give users deeper insight into your app’s privacy and security practices, and explain the data your app may collect and why — all before users install.", - "url": "https://youtu.be/J7TM0Yy0aTQ", - "headerImageUrl": "https://i.ytimg.com/vi/J7TM0Yy0aTQ/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "11", - "12" - ], - "authors": [] - }, - { - "id": "251", - "title": "Building Android UIs for any screen size", - "content": "Clara Bayarri, engineering manager and Daniel Jacobson, product manager, talked about the state of the ecosystem, focusing on new design guidance, APIs, and tools to help you make the most of your UI on different screen sizes.", - "url": "https://youtu.be/ir3LztqbeRI", - "headerImageUrl": "https://i.ytimg.com/vi/ir3LztqbeRI/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "252", - "title": "What's new for large screens & foldables", - "content": "Emilie Roberts, Chrome OS developer advocate and Andrii Kulian, Android software engineer, introduced new features focused specifically on making apps look great on large screens, foldables, and Chrome OS. ", - "url": "https://youtu.be/6-925K3hMHU", - "headerImageUrl": "https://i.ytimg.com/vi/6-925K3hMHU/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "253", - "title": "Enable great input support for all devices", - "content": "Users expect seamless experiences when using keyboards, mice, and stylus. Emilie Roberts taught us how to handle common keyboard and mouse input events and how to get started with more advanced support like keyboard shortcuts, low-latency styluses, MIDI, and more.", - "url": "https://youtu.be/piLEZYTc_4g", - "headerImageUrl": "https://i.ytimg.com/vi/piLEZYTc_4g/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "254", - "title": "Best practices for video apps on foldable devices", - "content": "Francesco Romano, developer advocate, and Will Chan, product manager at Zoom explored new user experiences made possible by the foldable form factor, focusing on video conferencing and media applications. ", - "url": "https://youtu.be/DBAek_P0nEw", - "headerImageUrl": "https://i.ytimg.com/vi/DBAek_P0nEw/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2", - "18" - ], - "authors": [] - }, - { - "id": "255", - "title": "Design beautiful apps on foldables and large screens", - "content": "Liam Spradlin, design advocate, and Jonathan Koren, developer relations engineer, talked about how to design and test Android applications that look and feel great across device types and screen sizes, from tablets to foldables to Chrome OS.", - "url": "https://youtu.be/DJeJIJKOUbI", - "headerImageUrl": "https://i.ytimg.com/vi/DJeJIJKOUbI/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "256", - "title": "12L and new Android APIs and tools for large screens", - "content": "Dave Burke, vice president of engineering, wrote a post covering the developer preview of 12L, an upcoming feature drop that makes Android 12 even better on large screens. ", - "url": "https://android-developers.googleblog.com/2021/10/12L-preview-large-screens.html", - "headerImageUrl": "https://1.bp.blogspot.com/-sjT5kFGiQtg/YXlpg0uByLI/AAAAAAAARJk/XHO_uo5bRJcMeQVm0Fn1wN-qe54FGI7MgCLcBGAsYHQ/w1200-h630-p-k-no-nu/12L-devices-hero.png", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2", - "13" - ], - "authors": [] - }, - { - "id": "257", - "title": "New features in ML Kit: Text Recognition V2 & Pose Detections", - "content": "Zongmin Sun, software engineer, and Valentin Bazarevsky, MediaPipe Engineer, talked about Text Recognition V2 & Pose Detection, recently-released features in ML Kit. ", - "url": "https://youtu.be/9EKQ0UC04S8", - "headerImageUrl": "https://i.ytimg.com/vi/9EKQ0UC04S8/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "8", - "2" - ], - "authors": [] - }, - { - "id": "258", - "title": "How to retain users with Android backup and restore", - "content": "In this talk, Martin Millmore, engineering manager, and Ruslan Tkhakokhov, software engineer, explored the benefits of transferring users’ data to a new device, using Backup and Restore to achieve that in a simple and secure way.", - "url": "https://youtu.be/bg2drEhz1_s", - "headerImageUrl": "https://i.ytimg.com/vi/bg2drEhz1_s/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "259", - "title": "Compatibility changes in Android 12", - "content": "Developer relations engineers Kseniia Shumelchyk and Slava Panasenko talked about new Android 12 features and changes. They shared tools and techniques to ensure that apps are compatible with the next Android release and users can take advantage of new features, along with app developer success stories.", - "url": "https://youtu.be/fCMJmV6nqGo", - "headerImageUrl": "https://i.ytimg.com/vi/fCMJmV6nqGo/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [] - }, - { - "id": "260", - "title": "Building great experiences for Novice Internet Users", - "content": "Learn the principles to help craft great experiences for the novice Internet user segment from Mrinal Sharma, UX manager, and Amrit Sanjeev, developer relations engineer. They highlight the gap between nascent and tech savvy user segments and suggest strategies in areas to improve the overall user experience. Factors like low functional literacy, being multilingual by default, being less digitally confident, and having no prior internet experience requires that we rethink the way we build apps for these users.", - "url": "https://youtu.be/Sf_TauUY4LE", - "headerImageUrl": "https://i.ytimg.com/vi/Sf_TauUY4LE/maxresdefault.jpg", - "publishDate": "2021-10-26T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "261", - "title": "Android Basics in Kotlin course 🧑‍💻", - "content": "Android Basics in Kotlin teaches people with no programming experience how to build simple Android apps. Since the first learning units were released in 2020, over 100,000 beginners have completed it! Today, we’re excited to share that the final unit has been released, and the full Android Basics in Kotlin course is now available.", - "url": "https://android-developers.googleblog.com/2021/10/announcing-android-basics-in-kotlin.html", - "headerImageUrl": "https://1.bp.blogspot.com/-BmlW7k8RhME/YWRvsOes9aI/AAAAAAAAQ_g/FpFS6_new9Y7vdzP7P4RPs_x4WHVi4yxQCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-announcing-android-basics-in-Kotlin-course-16x9.png", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "10" - ], - "authors": [ - "25" - ] - }, - { - "id": "262", - "title": "Updated Widget docs", - "content": "Widgets can make a huge impact on your user’s home screen! We updated the App Widgets documentation with the recent changes in the latest OS versions. New pages about how to create a simple widget, an advanced widget, and how to provide flexible widget layouts.", - "url": "https://developer.android.com/guide/topics/appwidgets", - "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "2" - ], - "authors": [] - }, - { - "id": "263", - "title": "Extend AGP by creating your own plugins", - "content": "The Android Gradle Plugin (AGP) contains extension points for plugins to control build inputs and extend its functionality. Starting in version 7.0, AGP has a set of official, stable APIs that you can rely on. We also have a new documentation page that walks you through this and explains how to create your own plugins.", - "url": "https://developer.android.com/studio/build/extend-agp", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "5", - "5" - ], - "authors": [] - }, - { - "id": "264", - "title": "Revamped Compose Basics Codelab", - "content": "If you’re planning to start learning Jetpack Compose, our modern toolkit for building native Android UI, it’s your lucky day! We just revamped the Basics Jetpack Compose codelab to help you learn the core concepts of Compose, and only with this, you’ll see how much it improves building Android UIs.", - "url": "https://developer.android.com/codelabs/jetpack-compose-basics", - "headerImageUrl": "https://i.ytimg.com/vi/k3jvNqj4m08/maxresdefault.jpg", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Codelab", - "topics": [ - "3" - ], - "authors": [] - }, - { - "id": "265", - "title": "Material components in Compose", - "content": "We added a new Material Components and layouts page that goes over the different Material components in Compose such as backdrop, app bars, modal drawers, etc.!", - "url": "https://developer.android.com/jetpack/compose/layouts/material", - "headerImageUrl": "https://developer.android.com/images/jetpack/compose/layouts/material/material_components.png", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "3", - "2" - ], - "authors": [] - }, - { - "id": "266", - "title": "How to implement a custom design system", - "content": "How to implement a custom design system in Compose", - "url": "https://developer.android.com/jetpack/compose/themes/custom", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "3", - "2" - ], - "authors": [] - }, - { - "id": "267", - "title": "The anatomy of a theme", - "content": "Understanding the anatomy of a Compose theme", - "url": "https://developer.android.com/jetpack/compose/themes/anatomy", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-10-20T23:00:00.000Z", - "type": "Docs 📑", - "topics": [ - "3" - ], - "authors": [] - }, - { - "id": "268", - "title": "Paging 📑 Displaying data and its loading state", - "content": "In the third episode of the Paging video series, TJ adds a local cache to pull from and refresh only when necessary, making use of Room . The local cache acts as the single source of truth for paging data.", - "url": "https://www.youtube.com/watch?v=OHH_FPbrjtA", - "headerImageUrl": "https://i.ytimg.com/vi/OHH_FPbrjtA/maxresdefault.jpg", - "publishDate": "2021-10-17T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "9", - "2" - ], - "authors": [ - "38" - ] - }, - { - "id": "269", - "title": "Data safety in the Play Console 🔒", - "content": "Google Play is rolling out the Data safety form in the Google Play Console. With the new Data safety section, developers will now have a transparent way to show users if and how they collect, share, and protect user data, before users install an app.\nRead the blog post to learn more about how to submit your app information in Play Console, how to get prepared, and what your users will see in your app’s store listing starting February.", - "url": "https://android-developers.googleblog.com/2021/10/launching-data-safety-in-play-console.html", - "headerImageUrl": "https://1.bp.blogspot.com/-Zde9ioLE3SY/YWh7qiquXKI/AAAAAAAARCU/m6D-qJJe6QowYPcDWUtb3-YzFGn9xIaUwCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-get-ready-to-sumbit-your-data-safety-secton-social.png", - "publishDate": "2021-10-17T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "11", - "12" - ], - "authors": [ - "10" - ] - }, - { - "id": "270", - "title": "Honor every photo - How cameras capture images", - "content": "Episode 177: Honor every photon. In this episode, Chet, Roman, and Tor have a chat with Bart Wronski from the Google Research team, discussing the camera pipeline that powers the Pixel phones. How cameras capture images, how the algorithms responsible for Pixel’s beautiful images, HDR+ or Night Sight mode works, and more!", - "url": "https://adbackstage.libsyn.com/episode-177-honor-every-photon", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2021-10-17T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "18" - ], - "authors": [ - "31" - ] - }, - { - "id": "271", - "title": "Accessibility series 🌐 - Touch targets", - "content": "The accessibility series continues on with more information on how to follow basic accessibility principles to make sure that your app can be used by as many users as possible.\nIn general, you should ensure that interactive elements have a width and height of at least 48dp! In the touch targets episode, you’ll learn about a few ways in which you can make this happen.", - "url": "https://www.youtube.com/watch?v=Dqqbe8IFBA4", - "headerImageUrl": "https://i.ytimg.com/vi/Dqqbe8IFBA4/maxresdefault.jpg", - "publishDate": "2021-10-16T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "39" - ] - }, - { - "id": "272", - "title": "Using the CameraX Exposure Compensation API", - "content": "This blog post by Wenhung Teng talks about how to use the CameraX Exposure Compensation that makes it much simpler to quickly take images with exceptional quality.", - "url": "https://medium.com/androiddevelopers/using-camerax-exposure-compensation-api-11fd75785bf", - "headerImageUrl": "https://miro.medium.com/max/1400/1*zinEvf1keSZYuZojr31ehQ.png", - "publishDate": "2021-10-12T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [ - "44" - ] - }, - { - "id": "273", - "title": "Compose for Wear OS in Developer preview ⌚", - "content": "We’re bringing the best of Compose to Wear OS as well, with built-in support for Material You to help you create beautiful apps with less code. Read the following article to review the main composables for Wear OS we’ve built and point you towards resources to get started using them.", - "url": "https://android-developers.googleblog.com/2021/10/compose-for-wear-os-now-in-developer.html", - "headerImageUrl": "https://1.bp.blogspot.com/-RkL3Yokn3XE/YWWmbuX8E7I/AAAAAAAAQ_o/CEmNJ5_mfq0kScxkFGoMpf1BlU5-uBHjACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-compose-for-wear-os-now-in-dev-review-header-dark.png", - "publishDate": "2021-10-11T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "19", - "3" - ], - "authors": [ - "41" - ] - }, - { - "id": "274", - "title": "Paging 📑 How to fetch data and bind the PagingData to the UI", - "content": "The series on Paging continues on with more content! In the second episode, TJ shows how to fetch data and bind the PagingData to the UI, including headers and footers.", - "url": "https://www.youtube.com/watch?v=C0H54K63Lww", - "headerImageUrl": "https://i.ytimg.com/vi/C0H54K63Lww/maxresdefault.jpg", - "publishDate": "2021-10-10T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [ - "38" - ] - }, - { - "id": "275", - "title": "Room adds support for Kotlin Symbol Processing", - "content": "Yigit Boyar wrote the story about how Room added support for Kotlin Symbol Processing (KSP). Spoiler: it wasn’t easy, but it was definitely worth it.", - "url": "https://medium.com/androiddevelopers/room-kotlin-symbol-processing-24808528a28e", - "headerImageUrl": "https://miro.medium.com/max/1400/1*yM7Lf4dC_hwse6YmoCO4uQ.png", - "publishDate": "2021-10-09T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9" - ], - "authors": [ - "34" - ] - }, - { - "id": "276", - "title": "Apply special effects to images with the CameraX Extensions API", - "content": "Have you ever wanted to apply special effects such as HDR or Night mode when taking pictures from your app? CameraX is here to help you! In this article by Charcoal Chen, learn how to do that using the new ExtensionsManager available in the camera-extensions Jetpack library. ", - "url": "https://medium.com/androiddevelopers/apply-special-effects-to-images-with-the-camerax-extensions-api-d1a169b803d3", - "headerImageUrl": "https://miro.medium.com/max/1400/1*GZmhCFMCrG4L_mOtwSb0zA.png", - "publishDate": "2021-10-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "18" - ], - "authors": [ - "45" - ] - }, - { - "id": "277", - "title": "Wear OS Jetpack libraries now in stable", - "content": "The Wear OS Jetpack libraries are now in stable.", - "url": "https://android-developers.googleblog.com/2021/09/wear-os-jetpack-libraries-now-in-stable.html", - "headerImageUrl": "https://1.bp.blogspot.com/-9zeEGNCG_As/YUD1UO_3kkI/AAAAAAAAQ8k/tCFBpTCwU4MEQHQNB9XzTOXSf6hd9TkQQCLcBGAsYHQ/w1200-h630-p-k-no-nu/image1.png", - "publishDate": "2021-09-14T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8", - "19" - ], - "authors": [ - "41" - ] - }, - { - "id": "278", - "title": "Android Dev Summit returns on October 27-28, 2021! 📆", - "content": "Join us October 27–28 for Android Dev Summit 2021! The show kicks off at 10 AM PST on October 27 with The Android Show: a technical keynote where you’ll hear all the latest developer news and updates. From there, we have over 30 sessions on a range of technical Android development topics, and we’ll be answering your #AskAndroid questions live.", - "url": "https://developer.android.com/dev-summit", - "headerImageUrl": "https://developer.android.com/dev-summit/images/android-dev-summit-2021.png", - "publishDate": "2021-10-05T23:00:00.000Z", - "type": "Event 📆", - "topics": [ - "1" - ], - "authors": [] - }, - { - "id": "279", - "title": "Android 12 is live in AOSP! 🤖", - "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). It will be coming to devices later on this year. Thank you for your feedback during the beta.\nAndroid 12 introduces a new design language called Material You along with redesigned widgets, notification UI updates, stretch overscroll, and app launch splash screens. We reduced the CPU time used by core system services, added performance class device capabilities, made ML accelerator drivers updatable outside of platform releases, and prevented apps from launching foreground services from the background and using notification trampolines to improve performance. The new Privacy Dashboard, approximate location, microphone and camera indicators/toggles, and nearby device permissions give users more insight into and control over privacy. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", - "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", - "headerImageUrl": "https://1.bp.blogspot.com/-7dVmEfR3mJs/YVst2TdY16I/AAAAAAAAK3I/pLnt0r5S-pIaJwcSNsNBqT8w2Y4Ej0yaQCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android%2B12.jpeg", - "publishDate": "2021-10-03T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "280", - "title": "Improved Google Play Console user management 🧑‍💼", - "content": "The user and permission tools in Play Console have a new, decluttered interface and new team management features, making it easier to make sure every team member has the right set of permissions to fulfill their responsibilities without overexposing unrelated business data.\nWe’ve rewritten permission names and descriptions, clarified differentiation between account and app-level permissions, added new search, filtering, and batch-editing capabilities, and added the ability to export this information to a CSV file. In addition, Play Console users can request access to actions with a justification, and we’ve introduced permission groups to make it easier to assign multiple permissions at once to users that share the same or similar roles.", - "url": "https://android-developers.googleblog.com/2021/09/improved-google-play-console-user.html", - "headerImageUrl": "https://1.bp.blogspot.com/-vw3eaKdwzVU/YUjvyJ6zy2I/AAAAAAAAQ9s/m39byf56P8Icog5e5TgCbu9et0VCZh1iACLcBGAsYHQ/w1200-h630-p-k-no-nu/PlayConsole-revamped-user-management-01.png", - "publishDate": "2021-09-20T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [ - "46" - ] - }, - { - "id": "281", - "title": "Making Permissions auto-reset available to billions more devices 🔐", - "content": "Android 11 introduced permission auto-reset, automatically resetting an app’s runtime permissions when it isn’t used for a few months. In December 2021, we are starting to roll this feature out to devices with Google Play services running Android 6.0 (API level 23) or higher for apps targeting Android 11 (API level 30) or higher. Users can manually enable permission auto-reset for apps targeting API levels 23 to 29.\nSome apps and permissions are automatically exempted from revocation, like active Device Administrator apps used by enterprises, and permissions fixed by enterprise policy. If your app is expected to work primarily in the background without user interaction, you can ask the user to prevent the system from resetting your app’s permissions.", - "url": "https://android-developers.googleblog.com/2021/09/making-permissions-auto-reset-available.html", - "headerImageUrl": "https://1.bp.blogspot.com/-W3UAh-gyf3Y/YUJehjKWQjI/AAAAAAAAQ84/zkURLgqMRa4VZK3Is3ENNYG_OjXJxx2pgCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-making-permissions-auto-reset-social-v2.png", - "publishDate": "2021-09-16T23:00:00.000Z", - "type": "DAC - Android version features", - "topics": [ - "11" - ], - "authors": [ - "47" - ] - }, - { - "id": "282", - "title": "Migrating from Dagger to Hilt", - "content": "While you will eventually want to migrate all your existing Dagger modules over to Hilt’s built in components, you can start by migrating application-wide components to Hilt’s singleton component. This episode explains how.", - "url": "https://www.youtube.com/watch?v=Xt1_3Nq4lD0&t=15s", - "headerImageUrl": "https://i.ytimg.com/vi/Xt1_3Nq4lD0/hqdefault.jpg", - "publishDate": "2021-09-19T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "4" - ], - "authors": [ - "48" - ] - }, - { - "id": "283", - "title": "ADB Podcast Episode 175: Creating delightful user experiences with Lottie animations", - "content": "In this episode, Chet, Romain and Tor have a chat with Gabriel Peal from Tonal, well known for his contributions to the Android community on projects such as Mavericks and Lottie. They talked about Lottie and how it helps designers and developers deliver more delightful user experiences by taking complex animations designed in specialized authoring tools such as After Effects, and rendering them efficiently on mobile devices. They also explored the challenges of designing and implementing a rendering engine such as Lottie.", - "url": "http://adbackstage.libsyn.com/episode-175-lottie", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2021-09-13T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "2" - ], - "authors": [ - "31" - ] - }, - { - "id": "284", - "title": "Hilt extensions", - "content": "This episode explains how to write your own Hilt Extensions. Hilt Extensions allow you to extend Hilt support to new libraries. Extensions can be created for common patterns in projects, to support non-standard member injection, mirroring bindings, and more.", - "url": "https://medium.com/androiddevelopers/hilt-extensions-in-the-mad-skills-series-f2ed6fcba5fe", - "headerImageUrl": "https://miro.medium.com/max/1400/1*a_ZJwMHs17SmEFr3uEbxDg.png", - "publishDate": "2021-09-12T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "49" - ] - }, - { - "id": "285", - "title": "Labeling images for Accessibility", - "content": "This Accessibilities series episode covers labeling images for accessibility, such as content descriptions for ImageViews and ImageButtons.", - "url": "https://youtu.be/O2DeSITnzFk", - "headerImageUrl": "https://i.ytimg.com/vi/O2DeSITnzFk/maxresdefault.jpg", - "publishDate": "2021-09-09T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "28" - ] - }, - { - "id": "286", - "title": "ADB Podcast Episode 174: Compose in Android Studio", - "content": "In this episode, Tor and Nick are joined by Chris Sinco, Diego Perez and Nicolas Roard to discuss the features added to Android Studio for Jetpack Compose. Tune in as they discuss the Compose preview, interactive preview, animation inspector, and additions to the Layout inspector along with their approach to creating tooling to support Compose’s code-centric system.", - "url": "http://adbackstage.libsyn.com/episode-174-compose-tooling", - "headerImageUrl": "http://assets.libsyn.com/content/110962067?height=250&width=250&overlay=true", - "publishDate": "2021-09-08T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "5", - "3" - ], - "authors": [ - "32" - ] - }, - { - "id": "287", - "title": "Hilt under the hood", - "content": "This episode dives into how the Hilt annotation processors generate code, and how the Hilt Gradle plugin works behind the scenes to improve the overall experience when using Hilt with Gradle.", - "url": "https://medium.com/androiddevelopers/mad-skills-series-hilt-under-the-hood-9d89ee227059", - "headerImageUrl": "https://miro.medium.com/max/1400/1*a_ZJwMHs17SmEFr3uEbxDg.png", - "publishDate": "2021-09-07T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "50" - ] - }, - { - "id": "288", - "title": "Trackr comes to the Big Screen", - "content": "A blog post on Trackr, a sample task management app where we showcase Modern Android Development best practices. This post takes you through how applying Material Design and responsive patterns produced a more refined and intuitive user experience on large screen devices.", - "url": "https://medium.com/androiddevelopers/trackr-comes-to-the-big-screen-9f13c6f927bf", - "headerImageUrl": "https://miro.medium.com/max/1400/1*678DlYtu4G7wFrq30FQ7Mw.png", - "publishDate": "2021-09-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "51" - ] - }, - { - "id": "289", - "title": "Accessibility services and the Android Accessibility model", - "content": "This Accessibilities series episode covers accessibility services like TalkBack, Switch Access and Voice Access and how they help users interact with your apps. Android’s accessibility framework allows you to write one app and the framework takes care of providing the information needed by different accessibility services.", - "url": "https://youtu.be/LxKat_m7mHk", - "headerImageUrl": "https://i.ytimg.com/vi/LxKat_m7mHk/maxresdefault.jpg", - "publishDate": "2021-09-02T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "14" - ], - "authors": [ - "39" - ] - }, - { - "id": "290", - "title": "New Accessibility Pathway", - "content": "Want even more accessibility? You are in luck, check out this entire new learning pathway aimed at teaching you how to make your app more accessible.", - "url": "https://developer.android.com/courses/pathways/make-your-android-app-accessible", - "headerImageUrl": "https://developers.google.com/profile/badges/playlists/make-your-android-app-accessible/badge.svg", - "publishDate": "2021-08-31T23:00:00.000Z", - "type": "", - "topics": [ - "14" - ], - "authors": [] - }, - { - "id": "291", - "title": "Jetpack Compose 1.0 stable is released", - "content": "Jetpack Compose, Android’s modern, native UI toolkit is now stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system.", - "url": "https://android-developers.googleblog.com/2021/07/jetpack-compose-announcement.html", - "headerImageUrl": "https://1.bp.blogspot.com/-9MiK78CFMLM/YQFurOq9AII/AAAAAAAAQ1A/lKj5GiDnO_MkPLb72XqgnvD5uxOsHO-eACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-Compose-1.0-header-v2.png", - "publishDate": "2021-07-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "3" - ], - "authors": [ - "52" - ] - }, - { - "id": "292", - "title": "Android Studio Artic Fox stable is released", - "content": "Android Studio Arctic Fox is now available in the stable release channel. Arctic Fox brings Jetpack Compose to life with Compose Preview, Deploy Preview, Compose support in the Layout Inspector, and Live Editing of literals. Compose Preview works with the @Preview annotation to let you instantly see the impact of changes across multiple themes, screen sizes, font sizes, and more. Deploy Preview deploys snippets of your Compose code to a device or emulator for quick testing. Layout inspector now works with apps written fully in Compose as well as apps that have Compose alongside Views, allowing you to explore your layouts and troubleshoot. With Live Edit of literals, you can edit literals such as strings, numbers, booleans, etc. and see the immediate results change in previews, the emulator, or on a physical device — all without having to compile.\n", - "url": "https://android-developers.googleblog.com/2021/07/android-studio-arctic-fox-202031-stable.html", - "headerImageUrl": "https://1.bp.blogspot.com/-cmcRT5BGOTY/YQBKC6asA0I/AAAAAAAAQzg/hZrde9Sgx881Wdf-c__VMkTvsKoVjOwsACLcBGAsYHQ/w1200-h630-p-k-no-nu/Arctic_Fox_Splash_2x%2B%25281%2529.png", - "publishDate": "2021-07-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "5", - "3" - ], - "authors": [ - "53" - ] - }, - { - "id": "293", - "title": "User control, privacy, security, and safety", - "content": "Play announced new updates to bolster user control, privacy, and security. The post covered advertising ID updates, including zeroing out the advertising ID when users opt out of interest-based advertising or ads personalization, the developer preview of the app set ID, enhanced protection for kids, and policy updates around dormant accounts and users of the AccessibilityService API.", - "url": "https://android-developers.googleblog.com/2021/07/announcing-policy-updates-to-bolster.html", - "headerImageUrl": "https://1.bp.blogspot.com/-pWCVY7BR-z8/YQAzb9zCZsI/AAAAAAAAQzY/2-OetxLvjOUYhHlTFJNw5JSm_BVjkI0VwCLcBGAsYHQ/s0/Untitled.png", - "publishDate": "2021-07-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12" - ], - "authors": [ - "10" - ] - }, - { - "id": "294", - "title": "Identify performance bottlenecks using system trace", - "content": "System trace profiling within Android Studio with a detailed walkthrough of app startup performance.", - "url": "https://www.youtube.com/watch?v=aUrqx9AnDUg", - "headerImageUrl": "https://i.ytimg.com/vi/aUrqx9AnDUg/hqdefault.jpg", - "publishDate": "2021-07-25T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "7", - "5" - ], - "authors": [ - "37" - ] - }, - { - "id": "295", - "title": "Testing in Compose", - "content": "ADB released episode #171, part of our continuing series on Jetpack Compose. In this episode, Nick and Romain are joined by Filip Pavlis, Jelle Fresen & Jose Alcérreca to talk about Testing in Compose. They discuss how Compose’s testing APIs were developed hand-in-hand with the UI toolkit, making them more deterministic and opening up new possibilities like manipulating time. They go on to discuss the semantics tree, interop testing, screenshot testing and the possibilities for host-side testing.", - "url": "https://adbackstage.libsyn.com/episode-171-compose-testing", - "headerImageUrl": "http://assets.libsyn.com/content/108505820?height=250&width=250&overlay=true", - "publishDate": "2021-06-29T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "3", - "6" - ], - "authors": [ - "54" - ] - }, - { - "id": "296", - "title": "DataStore reached release candidate status", - "content": "DataStore has reached release candidate status meaning the 1.0 stable release is right around the corner!", - "url": "https://developer.android.com/topic/libraries/architecture/datastore", - "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", - "publishDate": "2021-06-29T23:00:00.000Z", - "type": "Jetpack release 🚀", - "topics": [ - "9" - ], - "authors": [] - }, - { - "id": "297", - "title": "Scope Storage Myths", - "content": "Apps will be required to update their targetSdkVersion to API 30 in the second half of the year. That means your app will be required to work with Scoped Storage. In this blog post, Nicole Borrelli busts some Scope storage myths in a Q&A format.", - "url": "https://medium.com/androiddevelopers/scope-storage-myths-ca6a97d7ff37", - "headerImageUrl": "https://miro.medium.com/max/1200/1*csWzYUmYq_1HQsqBWk3OTA.jpeg", - "publishDate": "2021-06-27T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "9", - "11" - ], - "authors": [ - "55" - ] - }, - { - "id": "298", - "title": "Navigation with Multiple back stacks", - "content": "As part of the rercommended Material pattern for bottom-navigation, the Jetpack Navigation library makes it easy to implement navigation with multiple back-stacks", - "url": "https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f", - "headerImageUrl": "https://miro.medium.com/max/1400/1*v7S7LKg4TlrMRlneeP224Q.jpeg", - "publishDate": "2021-06-14T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "25" - ] - }, - { - "id": "299", - "title": "Build sophisticated search features with AppSearch", - "content": "AppSearch is an on-device search library which provides high performance and feature-rich full-text search functionality. Learn how to use the new Jetpack AppSearch library for doing high-performance on-device full text searches.", - "url": "https://android-developers.googleblog.com/2021/06/sophisticated-search-with-appsearch-in-jetpack.html", - "headerImageUrl": "https://1.bp.blogspot.com/-PmN4MS50wvo/YMj-HmY4N2I/AAAAAAAAQoQ/5eCx8CU1HgAlFQnQ55IOb_CCVRhe8eGewCLcBGAsYHQ/w1200-h630-p-k-no-nu/AppSearch.jpg", - "publishDate": "2021-06-13T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "8", - "2" - ], - "authors": [ - "56" - ] - }, - { - "id": "300", - "title": "ADB Podcast Episode 167: Jetpack Compose Layout", - "content": "In this second episode of our mini-series on Jetpack Compose (AD/BC), Nick and Romain are joined by Anastasia Soboleva, George Mount and Mihai Popa to talk about Compose’s layout system. They explain how the Compose layout model works and its benefits, introduce common layout composables, discuss how writing your own layout is far simpler than Views, and how you can even animate layout.", - "url": "https://adbackstage.libsyn.com/episode-167-jetpack-compose-layout", - "headerImageUrl": "http://assets.libsyn.com/content/105399023?height=250&width=250&overlay=true", - "publishDate": "2021-06-13T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "3" - ], - "authors": [ - "57" - ] - }, - { - "id": "301", - "title": "Create an application CoroutineScope using Hilt", - "content": "Learn how to create an applicatioon-scoped CoroutineScope using Hilt, and how to inject it as a dependency.", - "url": "https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528", - "headerImageUrl": "https://miro.medium.com/max/1400/1*MgDtM-AJmc2m2hg5chkflg.png", - "publishDate": "2021-06-09T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "4" - ], - "authors": [ - "23" - ] - }, - { - "id": "302", - "title": "Android 12 Beta 2 Update", - "content": "The second Beta of Android 12 has just been released for you to try. Beta 2 adds new privacy features like the Privacy Dashboard and continues our work of refining the release.", - "url": "https://android-developers.googleblog.com/2021/06/android-12-beta-2-update.html", - "headerImageUrl": "https://1.bp.blogspot.com/-tLt-TVPqpjA/YKMRwRPMfjI/AAAAAAAAQik/JNtMesFZ2i87RyBACHAVEC14CvcU7G__wCLcBGAsYHQ/w1200-h630-p-k-no-nu/Screen%2BShot%2B2021-05-17%2Bat%2B9.00.30%2BPM.png", - "publishDate": "2021-06-08T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13" - ], - "authors": [ - "14" - ] - }, - { - "id": "303", - "title": "Top 3 things in Android 12 | Android @ Google I/O '21", - "content": "Did you miss the latest in Android 12 at Google I/O 2021? Android Software Engineer Chet Haase will recap the top three themes in Android 12 from this year’s Google I/O!", - "url": "https://www.youtube.com/watch?v=tvf1wmD5H0M", - "headerImageUrl": "https://i.ytimg.com/vi/tvf1wmD5H0M/maxresdefault.jpg", - "publishDate": "2021-06-08T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "13" - ], - "authors": [ - "31" - ] - }, - { - "id": "304", - "title": "ADB Podcast Episode 166: Security Deposit", - "content": "In this episode, Chad and Jeff from the Android Security team join Tor and Romain to talk about… security. They explain what the platform does to help preserve user trust and device integrity, why it sometimes means restricting existing APIs, and touch on what apps can do or should worry about.", - "url": "http://adbackstage.libsyn.com/episode-166-security-deposit", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2021-06-07T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "11" - ], - "authors": [ - "32" - ] - }, - { - "id": "305", - "title": "Multiple Back Stacks", - "content": "A deep dive into multiple back stacks and some of the work it took to make this feature happen in Fragments and Navigation", - "url": "https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134", - "headerImageUrl": "https://miro.medium.com/max/1400/1*5-lbc-YBJlZnxVFPvNMPAQ.png", - "publishDate": "2021-06-06T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "58" - ] - }, - { - "id": "306", - "title": "Building across devices | Android @ Google I/O '21", - "content": "Did you miss the latest in Building across screens at Google I/O 2021? Product Manager Diana Wong will recap the top three announcements from this year’s Google I/O!", - "url": "https://www.youtube.com/watch?v=O5oRiIUk_F4", - "headerImageUrl": "https://i.ytimg.com/vi/O5oRiIUk_F4/maxresdefault.jpg", - "publishDate": "2021-06-02T23:00:00.000Z", - "type": "Video 📺", - "topics": [ - "2" - ], - "authors": [ - "59" - ] - }, - { - "id": "307", - "title": "Navigation in Feature Modules", - "content": "Feature modules delivered with Play Feature delivery at not downloadedd at install time, but only when the app requestss them. Learn how to use the dynamic features navigation library to include the graph from the feature module.", - "url": "https://medium.com/androiddevelopers/navigation-in-feature-modules-322ac3d79334", - "headerImageUrl": "https://miro.medium.com/max/1400/1*v7S7LKg4TlrMRlneeP224Q.jpeg", - "publishDate": "2021-06-01T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "2" - ], - "authors": [ - "25" - ] - }, - { - "id": "308", - "title": "ADB Podcast Episode 165: Material Witnesses", - "content": "In this episode, Chet and Romain chattedd with Hunter and Nick from the Material Design team about recent additions and improvements to the Material Design Component libraries: transitions, motion theming, Compose, large screens support and guidance, etc.", - "url": "http://adbackstage.libsyn.com/episode-165-material-witnesses", - "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", - "publishDate": "2021-06-01T23:00:00.000Z", - "type": "Podcast 🎙", - "topics": [ - "2" - ], - "authors": [ - "31" - ] - }, - { - "id": "309", - "title": "Grow Your Indie Game with Help From Google Play", - "content": "Google Play is opening submissions for two of our annual developer programs - the Indie Games Accelerator and the Indie Games Festival. These programs are designed to help small games studios grow on Google Play, no matter what stage they are in", - "url": "https://developers.googleblog.com/2021/06/grow-your-indie-game-with-help-from-google-play.html", - "headerImageUrl": "https://1.bp.blogspot.com/-MNEblg7_8fA/YK7lludSxJI/AAAAAAAAKQM/_YIT15giTk42oPXWIhK6l2FBVt5PCFKTwCLcBGAsYHQ/w1200-h630-p-k-no-nu/Joint_Announcement_Android%2BDevelopers%2BBlog_Header_1200x600%2B%25282%2529.png", - "publishDate": "2021-05-31T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "12", - "17" - ], - "authors": [ - "60" - ] - }, - { - "id": "310", - "title": "Untrusted Touch Events in Android", - "content": "Android 12 prevents touch events from being deliverred if these touches first pass through a window from a different app to ensure users can see what they are interacting with. Learn about alternatives, to see if your app will be affected and how you can test to see if your app will be impacted.", - "url": "https://medium.com/androiddevelopers/untrusted-touch-events-2c0e0b9c374c", - "headerImageUrl": "https://miro.medium.com/max/1400/1*lvwe7v_bcNsNXI_7ltFkJA.jpeg", - "publishDate": "2021-05-25T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "13" - ], - "authors": [ - "15" - ] - }, - { - "id": "311", - "title": "Android @ Google I/O: 3 things to know in Modern Android Development", - "content": "This year’s Google I/O brought lots of updates for Modern Android Development. Learn about the top 3 things you should know.", - "url": "https://android-developers.googleblog.com/2021/05/mad-spotlight.html", - "headerImageUrl": "https://1.bp.blogspot.com/-8cqMFObMeko/YK5RbJ7Yr_I/AAAAAAAAQkw/Iw4_hRZwa7QD1CmVGnZUZ4NjYowXZadTgCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android_PostIO_blog-MAD.png", - "publishDate": "2021-05-24T23:00:00.000Z", - "type": "Article 📚", - "topics": [ - "1" - ], - "authors": [ - "61" - ] - } -] \ No newline at end of file diff --git a/core/network/src/commonMain/assets/topics.json b/core/network/src/commonMain/assets/topics.json deleted file mode 100644 index 9157274c8..000000000 --- a/core/network/src/commonMain/assets/topics.json +++ /dev/null @@ -1,154 +0,0 @@ -[ - { - "id": "1", - "name": "Headlines", - "shortDescription": "News you'll definitely be interested in", - "longDescription": "The latest events and announcements from the world of Android development.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f", - "url": "" - }, - { - "id": "2", - "name": "UI", - "shortDescription": "not including Compose", - "longDescription": "Stay up to date on Material Design, Navigation, Text, Paging, Compose, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets and much more!\n\nTo get Compose specific news, make sure you also follow the Compose topic. ", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594", - "url": "" - }, - { - "id": "3", - "name": "Compose", - "shortDescription": "", - "longDescription": "All the latest and greatest news on Jetpack Compose - Android’s modern toolkit for building native user interfaces.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Compose.svg?alt=media&token=9f0228e8-fdf2-45ee-9fd0-7e51fda23b48", - "url": "" - }, - { - "id": "4", - "name": "Architecture", - "shortDescription": "", - "longDescription": "Stay up-to-date with Android architecture best practices including scalability and modularization. ", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Architecture.svg?alt=media&token=e69ed228-fa91-49ae-9017-c8b7331f4269", - "url": "" - }, - { - "id": "5", - "name": "Android Studio & Tools", - "shortDescription": "", - "longDescription": "The latest news on Android development tools, including Android Studio, Gradle, device emulators, debugging tools and more.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Studio.svg?alt=media&token=b28b82dc-5aa1-4098-9eff-deb04636d3ac", - "url": "" - }, - { - "id": "6", - "name": "Testing", - "shortDescription": "", - "longDescription": "The latest news on testing, including unit and UI testing, and continuous integration. ", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Testing.svg?alt=media&token=a11533c4-7cc8-4b11-91a3-806158ebf428", - "url": "" - }, - { - "id": "7", - "name": "Performance", - "shortDescription": "", - "longDescription": "Up-to-date content on optimizing your app performance, including profiling, tracing and jank avoidance.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Performance.svg?alt=media&token=558fdf02-1918-4527-b13f-323db67e31cc", - "url": "" - }, - { - "id": "8", - "name": "New APIs & Libraries", - "shortDescription": "", - "longDescription": "Stay up-to-date with new APIs & library releases, including Jetpack.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_New-APIs-_-Libraries.svg?alt=media&token=8efd12df-6dd9-4b1b-81fd-017a49a866ac", - "url": "" - }, - { - "id": "9", - "name": "Data Storage", - "shortDescription": "", - "longDescription": "Everything to do with data storage, including Room and DataStore.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Data-Storage.svg?alt=media&token=c9f78039-f371-4ce1-ba82-2c0c1e20d180", - "url": "" - }, - { - "id": "10", - "name": "Kotlin", - "shortDescription": "", - "longDescription": "New language features and guidance for getting the best out of Kotlin on Android. ", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Kotlin.svg?alt=media&token=bdc73380-e80d-47df-8954-d9b61cccacd2", - "url": "" - }, - { - "id": "11", - "name": "Privacy & Security", - "shortDescription": "", - "longDescription": "The latest news on security best practices, APIs and libraries.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Privacy-_-Security.svg?alt=media&token=6232fd17-c1cc-43b3-bf70-a734323fa6df", - "url": "" - }, - { - "id": "12", - "name": "Publishing & Distribution", - "shortDescription": "", - "longDescription": "Everything to do with publishing and distributing your app, including Google Play.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Publishing-_-Distribution.svg?alt=media&token=64a5aeaf-269a-479d-8a44-29f59d337dbf", - "url": "" - }, - { - "id": "13", - "name": "Platform & Releases", - "shortDescription": "", - "longDescription": "Stay up-to-date with the latest Android releases and features.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Platform-_-Releases.svg?alt=media&token=ff6d7a38-5205-4a51-8b6a-721e665dc515", - "url": "" - }, - { - "id": "14", - "name": "Accessibility", - "shortDescription": "", - "longDescription": "The latest news on accessibility features and services, helping you to improve your app's usability, particularly for users with disabilities.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Accessibility.svg?alt=media&token=5b783a03-dd3b-4d0c-9e0c-16ae8350295f", - "url": "" - }, - { - "id": "15", - "name": "Android Auto", - "shortDescription": "", - "longDescription": "The latest news on Android Automotive OS and Android Auto.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Auto.svg?alt=media&token=56453754-14a5-4953-b596-66d63c56c196", - "url": "" - }, - { - "id": "16", - "name": "Android TV", - "shortDescription": "", - "longDescription": "Stay up-to-date on everything to do with building apps for Android TV.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-TV.svg?alt=media&token=a78ca0df-f1ba-44a6-a89d-3912c82ef661", - "url": "" - }, - { - "id": "17", - "name": "Games", - "shortDescription": "", - "longDescription": "The latest news on Android game development.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Games.svg?alt=media&token=4effa537-cc42-4d7f-b6bd-f1f14568db07", - "url": "" - }, - { - "id": "18", - "name": "Camera & Media", - "shortDescription": "", - "longDescription": "The latest news on capturing and playing media on Android, including the Camera and Media APIs. ", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Camera-_-Media.svg?alt=media&token=73adea20-20d4-4f4c-8f3b-eb47c1097496", - "url": "" - }, - { - "id": "19", - "name": "Wear OS", - "shortDescription": "", - "longDescription": "The latest news on app development for Wear OS.", - "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Wear.svg?alt=media&token=bd11fe4c-9c92-4536-8ebc-5210f44d09be", - "url": "" - } -] \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt new file mode 100644 index 000000000..3c47eade9 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt @@ -0,0 +1,4764 @@ +/* + * 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.network.assets + +/** + * A file to store sample data in the code to avoid fetching resources from the assets + * Since it is kinda hard to fetch assets in the common module + * TODO Read the data from the assets + */ + +internal val TOPICS_DATA = """ + [ + { + "id": "1", + "name": "Headlines", + "shortDescription": "News you'll definitely be interested in", + "longDescription": "The latest events and announcements from the world of Android development.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f", + "url": "" + }, + { + "id": "2", + "name": "UI", + "shortDescription": "not including Compose", + "longDescription": "Stay up to date on Material Design, Navigation, Text, Paging, Compose, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets and much more!\n\nTo get Compose specific news, make sure you also follow the Compose topic. ", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594", + "url": "" + }, + { + "id": "3", + "name": "Compose", + "shortDescription": "", + "longDescription": "All the latest and greatest news on Jetpack Compose - Android’s modern toolkit for building native user interfaces.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Compose.svg?alt=media&token=9f0228e8-fdf2-45ee-9fd0-7e51fda23b48", + "url": "" + }, + { + "id": "4", + "name": "Architecture", + "shortDescription": "", + "longDescription": "Stay up-to-date with Android architecture best practices including scalability and modularization. ", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Architecture.svg?alt=media&token=e69ed228-fa91-49ae-9017-c8b7331f4269", + "url": "" + }, + { + "id": "5", + "name": "Android Studio & Tools", + "shortDescription": "", + "longDescription": "The latest news on Android development tools, including Android Studio, Gradle, device emulators, debugging tools and more.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Studio.svg?alt=media&token=b28b82dc-5aa1-4098-9eff-deb04636d3ac", + "url": "" + }, + { + "id": "6", + "name": "Testing", + "shortDescription": "", + "longDescription": "The latest news on testing, including unit and UI testing, and continuous integration. ", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Testing.svg?alt=media&token=a11533c4-7cc8-4b11-91a3-806158ebf428", + "url": "" + }, + { + "id": "7", + "name": "Performance", + "shortDescription": "", + "longDescription": "Up-to-date content on optimizing your app performance, including profiling, tracing and jank avoidance.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Performance.svg?alt=media&token=558fdf02-1918-4527-b13f-323db67e31cc", + "url": "" + }, + { + "id": "8", + "name": "New APIs & Libraries", + "shortDescription": "", + "longDescription": "Stay up-to-date with new APIs & library releases, including Jetpack.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_New-APIs-_-Libraries.svg?alt=media&token=8efd12df-6dd9-4b1b-81fd-017a49a866ac", + "url": "" + }, + { + "id": "9", + "name": "Data Storage", + "shortDescription": "", + "longDescription": "Everything to do with data storage, including Room and DataStore.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Data-Storage.svg?alt=media&token=c9f78039-f371-4ce1-ba82-2c0c1e20d180", + "url": "" + }, + { + "id": "10", + "name": "Kotlin", + "shortDescription": "", + "longDescription": "New language features and guidance for getting the best out of Kotlin on Android. ", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Kotlin.svg?alt=media&token=bdc73380-e80d-47df-8954-d9b61cccacd2", + "url": "" + }, + { + "id": "11", + "name": "Privacy & Security", + "shortDescription": "", + "longDescription": "The latest news on security best practices, APIs and libraries.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Privacy-_-Security.svg?alt=media&token=6232fd17-c1cc-43b3-bf70-a734323fa6df", + "url": "" + }, + { + "id": "12", + "name": "Publishing & Distribution", + "shortDescription": "", + "longDescription": "Everything to do with publishing and distributing your app, including Google Play.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Publishing-_-Distribution.svg?alt=media&token=64a5aeaf-269a-479d-8a44-29f59d337dbf", + "url": "" + }, + { + "id": "13", + "name": "Platform & Releases", + "shortDescription": "", + "longDescription": "Stay up-to-date with the latest Android releases and features.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Platform-_-Releases.svg?alt=media&token=ff6d7a38-5205-4a51-8b6a-721e665dc515", + "url": "" + }, + { + "id": "14", + "name": "Accessibility", + "shortDescription": "", + "longDescription": "The latest news on accessibility features and services, helping you to improve your app's usability, particularly for users with disabilities.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Accessibility.svg?alt=media&token=5b783a03-dd3b-4d0c-9e0c-16ae8350295f", + "url": "" + }, + { + "id": "15", + "name": "Android Auto", + "shortDescription": "", + "longDescription": "The latest news on Android Automotive OS and Android Auto.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-Auto.svg?alt=media&token=56453754-14a5-4953-b596-66d63c56c196", + "url": "" + }, + { + "id": "16", + "name": "Android TV", + "shortDescription": "", + "longDescription": "Stay up-to-date on everything to do with building apps for Android TV.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Android-TV.svg?alt=media&token=a78ca0df-f1ba-44a6-a89d-3912c82ef661", + "url": "" + }, + { + "id": "17", + "name": "Games", + "shortDescription": "", + "longDescription": "The latest news on Android game development.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Games.svg?alt=media&token=4effa537-cc42-4d7f-b6bd-f1f14568db07", + "url": "" + }, + { + "id": "18", + "name": "Camera & Media", + "shortDescription": "", + "longDescription": "The latest news on capturing and playing media on Android, including the Camera and Media APIs. ", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Camera-_-Media.svg?alt=media&token=73adea20-20d4-4f4c-8f3b-eb47c1097496", + "url": "" + }, + { + "id": "19", + "name": "Wear OS", + "shortDescription": "", + "longDescription": "The latest news on app development for Wear OS.", + "imageUrl": "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Wear.svg?alt=media&token=bd11fe4c-9c92-4536-8ebc-5210f44d09be", + "url": "" + } + ] +""".trimIndent() + +internal val NEWS_DATA = """ + [ + { + "id": "1", + "title": "Android Dev Summit ’22: Coming to you, online and around the world! ⛰️", + "content": "Android Dev Summit is back, so join us online or in person — for the first time since 2019 — at locations around the world. We’ll be sharing the sessions live on YouTube in three tracks spread across three weeks.", + "url": "https://android-developers.googleblog.com/2022/10/android-dev-summit.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1VWQmqQu6wDswls9f_5NpEQnq4eR57g2NwzWvhKItcKtV6rb_Cyo75XSyL6vvmCIo4tzQn-8taNagEp7QG0KU1L4yMqwbYozNMzBMEFxEN2XintAhy5nLI4RQDaOXr8dgiIFdGOBMdl577Ndelzc0tDBzjI6mz7e4MF8_Tn09KWguZi6I-bS5NbJn/w1200-h630-p-k-no-nu/unnamed%20%2816%29.png", + "publishDate": "2022-10-04T23:00:00.000Z", + "type": "Event 📆", + "topics": [ + "1" + ], + "authors": [ + "64" + ] + }, + { + "id": "2", + "title": "The new Google Pixel Watch is here — start building for Wear OS! ⌚", + "content": "We launched the Google Pixel Watch, powered by Wear OS 3.5, at the Made by Google event — the perfect device to showcase apps built with Compose for Wear OS. With Compose for Wear OS, the Tiles Material library, and the tools in Android Studio Dolphin, it’s now simpler and more efficient than ever to make apps for WearOS.", + "url": "https://android-developers.googleblog.com/2022/10/the-new-google-pixel-watch-is-here.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH63icac2kmydOI8Fs2I09KiuRA3GUo2pfZ1Wpf0M5JIEoVQ8dj9LYSl8jpxSQlmlsUVXoeXbwN4UbHMCf5p0M7FHh_EXzMeFRAJ-6feI9-7eIyhBmtGZSD5o-MItwFLH_ESi15Cxd01AlznWaGy9WDqhK0NWtMQwiWELg3xE1I7hba-_7eVqs747V/w1200-h630-p-k-no-nu/WhasNewinPixelDevices_Social.png", + "publishDate": "2022-10-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1", + "3", + "19" + ], + "authors": [] + }, + { + "id": "3", + "title": "Listen to our major Text to Speech upgrades for 64 bit devices 💬", + "content": "An upgrade to Speech Services by Google is rolling out to 64-bit Android devices via Google Play over the next few weeks, providing clearer, more natural voices. All 421 voices in 67 languages are being upgraded with a new voice model and synthesizer. The post includes more on this update, including demonstrations of some voice upgrades, along with guidance on how to use text to speech in your projects.", + "url": "https://android-developers.googleblog.com/2022/09/listen-to-our-major-text-to-speech-upgrades-for-64-bit-devices.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrHro6d3BTw7ZZ4IXgfb6_8aESB7-SsWfelDSSInZVamiMSnYpBZzGBaZBBrWxWwYgLqOHuOtroGvGjxrwzdUkhjwuIvM1u6chIblGKS1gQ6JVkjXr-Vztyk2zoYb1ylvhNgLgC5q6M-7LaiXT1xnAT96DvkPx89APNb8JEaz-1mnMRcfaOYYBHzQL/w1200-h630-p-k-no-nu/Text%20to%20Speech%20-%20Social%20-%201024x512.png", + "publishDate": "2022-09-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "14" + ], + "authors": [ + "66", + "67" + ] + }, + { + "id": "4", + "title": "MAD Skills Compose: Powerful Toolkit", + "content": "Learn about the powerful toolkit of UI components that ship with Compose enabling you to build rich UIs and interactions.", + "url": "https://medium.com/androiddevelopers/compose-toolkit-8d3651228764", + "headerImageUrl": "https://miro.medium.com/max/1200/1*3FZeNmAPZDYUCmgL0cBXoA.png", + "publishDate": "2022-09-29T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "68" + ] + }, + { + "id": "5", + "title": "MAD Skills Compose: Accelerate development with tooling", + "content": "Learn how to accelerate your Compose development process with a dive into Android Studio tooling support, including live templates, gutter icons for drawables and colors, composable preview functions, multipreview, preview on device, live edits of literals, and the Layout Inspector.", + "url": "https://medium.com/androiddevelopers/compose-tooling-42621bd8719b", + "headerImageUrl": "https://miro.medium.com/max/1200/1*WVUBQsNgePqQxmrHjaID4g.png", + "publishDate": "2022-10-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "68" + ] + }, + { + "id": "6", + "title": "Deep Links Crash Course: Part 3 - Troubleshooting", + "content": "In part 3 of the Deep Links Crash Course you'll learn how to diagnose and debug common issues with deep links using command line tools and the Android Debug Bridge (ADB).", + "url": "https://www.youtube.com/watch?v=OAxJ2kWG4ZI", + "headerImageUrl": "https://i.ytimg.com/vi/OAxJ2kWG4ZI/maxresdefault.jpg", + "publishDate": "2022-09-29T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2", + "5" + ], + "authors": [] + }, + { + "id": "7", + "title": "Deep Links Crash Course: Part 4 - Deep links for your business", + "content": "Part 4 of the Deep Links Crash Course explores Deep links for your business, covering the importance and benefits of implementing deep links for your app, your users, and your business, including success stories and how App Links provide an optimal experience for users.", + "url": "https://www.youtube.com/watch?v=UvMIswgsJF8", + "headerImageUrl": "https://i.ytimg.com/vi/UvMIswgsJF8/maxresdefault.jpg", + "publishDate": "2022-10-05T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2", + "5" + ], + "authors": [] + }, + { + "id": "8", + "title": "Migrating the AOSP QuickSearchBox App to Kotlin", + "content": "This article includes the methodology used in the migration to Kotlin, how manual changes were addressed, and what the impact to APK size and build speed was.", + "url": "https://medium.com/androiddevelopers/migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec", + "headerImageUrl": "https://miro.medium.com/max/720/1*cWnPe-kD4hAVuH3IIcNUcA.png", + "publishDate": "2022-09-22T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "10" + ], + "authors": [ + "69" + ] + }, + { + "id": "9", + "title": "Prepare your Android Project for Android Gradle plugin 8.0 API changes", + "content": "How to prepare your Android Project for Android Gradle plugin 8.0 API changes; this article specifically addresses migrating from the Transform APIs — which slow down builds and will be removed in 8.0 — to the Artifacts API and Instrumentation API.", + "url": "https://android-developers.googleblog.com/2022/10/prepare-your-android-project-for-agp8-changes.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy74acii7wq-Z2pW8TUSga1YGpRKjLZjroOlZlUillRJuTFIlGpUi93PPletKlkcAaz9u6NgF8_LejO9686VYEWNtO2ypawLgpY2QW7JMtrMSVTlPsRGgEDUiQJKUfzEXw2Q_Y7qX1CSUlH9lma8Jjdm3AqMogbEI6PScD3AK1XsWgHmVeqJlVqUiK/w1200-h630-p-k-no-nu/Header-PrepareyourAndroidProjectforAGP8.0Changes%20.png", + "publishDate": "2022-10-05T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5", + "5" + ], + "authors": [ + "70", + "71", + "72" + ] + }, + { + "id": "10", + "title": "Optimize for Android (Go edition) : Lessons from Google apps Part 2", + "content": "Part two of the Optimize for Android Go : Lessons from Google apps blog series, covering strategies Google apps used to improve startup latency and optimize app size — things that will improve the user experience for any app.", + "url": "https://android-developers.googleblog.com/2022/09/optimize-for-android-go-lessons-from-google-apps-part-2.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD7uoBIqlA_WYXwuhyDxKy1Nt2ibaa_GYd9l8ewfQcC7f-f11t0WRCTTS6XhwnkJMqWUxSX-nxAq9DD8oBbk_Om2ik0yNMOV8lHw0sGmRAixLY2T0dxpKtQe0DnsVrzmexNSX1-BYdz9p0cvCtdXoxgDi1Mx6OLixzC5JAnxEEAf1TkHrTQON0fURg/w1200-h630-p-k-no-nu/MAD%20App%20Architecture%20launch%20-%20Mobile%20%281%29%20%281%29.png", + "publishDate": "2022-09-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "73" + ] + }, + { + "id": "11", + "title": "Helping users discover apps for all their devices from their phone", + "content": "Changes in Google Play are helping users discover apps for all their devices from their phone, including homepages for non-phone devices, a device search filter, and the ability to remotely install an app to another device.", + "url": "https://android-developers.googleblog.com/2022/09/helping-users-discover-apps-for-all-their-devices-from-their-phone.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyGaErzRykqFDWOwEmkHWos7vU7OhdETz5GNEjObA7FOhCudnf5DiQ1hAfpxuq102pwxuBf_ZCeifURihNeAwNnLj46pkdoBdbl_JYn8A9plqwaqS8D_0XML6B7Bupt0GhPZuABbfTXB_nkWsVVW6faVQXqpetHIV6QlNQyl1WD6zuojFf-U7wDSHO/w1200-h630-p-k-no-nu/image3.gif", + "publishDate": "2022-09-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [ + "74" + ] + }, + { + "id": "12", + "title": "Deep Links Crash Course : Part 3 Troubleshooting Your Deep Links", + "content": "The Deep Links Crash Course continues with Summers writing an article on troubleshooting deep links. He goes over common issues that can occur with deep links and how to solve them.", + "url": "https://medium.com/androiddevelopers/deep-links-crash-course-part-3-troubleshooting-your-deep-links-61329fecb93", + "headerImageUrl": "https://miro.medium.com/max/1200/1*IsRqP0Fe9I6YLxrJybIG6Q.png", + "publishDate": "2022-09-15T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "6" + ], + "authors": [ + "79" + ] + }, + { + "id": "13", + "title": "Jetpack Compose Composition Tracing", + "content": "Ben covered Compose Composition Tracing, a new feature that allows showing Jetpack Compose composable functions in the Android Studio Flamingo system trace profiler. This feature gives you the low intrusiveness from system tracing, with method tracing levels of detail in composition. This is great for checking your Compose app’s performance and working out why your app may not be performing as you expect. Learn more about this feature in the post!", + "url": "https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535", + "headerImageUrl": "https://miro.medium.com/max/700/1*jPVPY4GjPRK3prnJ2o09cQ.png", + "publishDate": "2022-09-19T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "7" + ], + "authors": [ + "80" + ] + }, + { + "id": "14", + "title": "Android Studio: Dolphin is available", + "content": "Android Studio Dolphin is here! In this post, Takeshi goes over the three key themes: Jetpack Compose, Wear OS, and development productivity. Exciting features include the Compose Animation Inspector, the Wear OS Emulator Pairing Assistant, and Gradle managed virtual devices. Learn about all the new features in the blog post or the video!", + "url": "https://www.youtube.com/watch?v=EQ_btxhpRzU&t=1s", + "headerImageUrl": "https://i.ytimg.com/vi/EQ_btxhpRzU/maxresdefault.jpg", + "publishDate": "2022-09-14T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5", + "5" + ], + "authors": [ + "81" + ] + }, + { + "id": "15", + "title": "Mad Skills: Compose", + "content": "Chris started a brand new MAD Skills series on Compose. This series is a great place to start to learn how to think and start building apps with Compose.", + "url": "https://www.youtube.com/watch?v=4UXJTeb9Khg&t=1s", + "headerImageUrl": "https://i.ytimg.com/vi/4UXJTeb9Khg/maxresdefault.jpg", + "publishDate": "2022-09-12T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3" + ], + "authors": [ + "68" + ] + }, + { + "id": "16", + "title": "Introducing Compose Camp", + "content": "We launched Compose Camp, a series of in-person and virtual sessions where you can learn how to build Android apps with Jetpack Compose alongside your peers. Compose Camp has two tracks: the beginner track caters to complete Android beginners including people that have no coding experience, and the experienced track is for Android developers who want to learn how to migrate to Compose and stop using XML.", + "url": "https://android-developers.googleblog.com/2022/09/learn-jetpack-compose-at-compose-camp-near-you.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn6__UOZ_lipXjY09TrHeXW4HjKodPUdFzmovYRn1tLwdYr8GVKjCN6wfgKpcby5rrJ6JFrUmZozT7aeDkp8x7v46QdH6wtz9ysQFTZAQPwswFfGWQkWcPmKHbeELq_BUUhazt4ppYm9ErYEo7HbFzPCfBid4IQ9qL-hydSgRpJx7w2lNZKh5ylNcb/w1200-h630-p-k-no-nu/Compose%20Camp%203.png", + "publishDate": "2022-09-12T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1", + "3" + ], + "authors": [ + "78" + ] + }, + { + "id": "17", + "title": "Privacy Sandbox developer preview 5 🔐", + "content": "The Privacy Sandbox aims to develop new technologies that improve user privacy and enable effective, personalized advertising experiences for mobile apps. Developer Preview 5 was released, this version is a major milestone that will become the foundation for upcoming Privacy Sandbox Beta releases. Please keep giving us your feedback! See what’s changed in the blog post.", + "url": "https://android-developers.googleblog.com/2022/09/privacy-sandbox-developer-preview-5-is-here.html", + "headerImageUrl": "https://services.google.com/fb/forms/privacysandbox/fb/forms/getlogo/5679849861677056/", + "publishDate": "2022-09-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11" + ], + "authors": [ + "77" + ] + }, + { + "id": "18", + "title": "Guide to app modularization 🧩", + "content": "The team just released new guidance on modularization. Guidance on this topic has been one of the top community requests and here it is! The guide is split into two parts. The overview page gives you a high level, theoretical overview of the matter and its benefits. The common modularization patterns page dives deep into practical examples in the context of modern Android architecture. Take a look at the guide announcement blog post to learn more about this.", + "url": "https://android-developers.googleblog.com/2022/09/announcing-new-guide-to-android-app-modularization.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0ZsQN4PZ_SQ975Hfbc-LDFXMTgOr6RVf42kSUBqNxfv9OcDvc6dTYRZPynsYx0JIlXT7k5TMz_Kjq7bJstYb4dUy2ZX6ilugMH20JudIZISLWEsa19f8sN0hDxA7JWXgS570gDxkXNp3ioHxxH42tvquQ0wUK-qPS6Qv2OeGK06HhumP2vvC0V07V/s1600/Android-AppModularization_4209x1253.png", + "publishDate": "2022-09-05T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "82" + ] + }, + { + "id": "19", + "title": "Build an offline-first app", + "content": "If you need to make your app work offline, we got you covered. The new Build an offline-first app guide helps you design your app to properly handle reads and writes, and deal with synchronization and conflict resolution in a device with no Internet connectivity.", + "url": "https://developer.android.com/topic/architecture/data-layer/offline-first", + "headerImageUrl": "https://developer.android.com/static/images/topic/architecture/data-layer/data-layer.png", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "20", + "title": "State holders and UI state page ", + "content": "Another new guide is the State holders and UI state page in the UI layer docs. Not everything needs to be present in ViewModel classes. This page goes through the different types of state holders you can find in the UI layer and what their responsibilities are.", + "url": "https://developer.android.com/topic/architecture/ui-layer/stateholders", + "headerImageUrl": "https://developer.android.com/static/images/topic/architecture/ui-layer/udf.png", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "4", + "3" + ], + "authors": [] + }, + { + "id": "21", + "title": "Architecture pathway", + "content": "If you want to learn all about Architecture and be up-to-date with our current best practices, check out the Architecture pathway, that got updated with all the videos of the Architecture MAD Skills series we produced early this year and the new docs.", + "url": "https://developer.android.com/courses/pathways/android-architecture", + "headerImageUrl": "https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-overview.png", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Codelab", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "22", + "title": "Mad Skills: Performance ⚡️", + "content": "Ben wrote this blog post that contains a summary of all the videos on MAD Skills: Performance! Don't miss it out!", + "url": "https://medium.com/androiddevelopers/mad-skills-performance-wrap-up-33688abfc51f", + "headerImageUrl": "https://miro.medium.com/max/720/0*qdkZp112bKTGtQIN", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "36" + ] + }, + { + "id": "23", + "title": "AndroidX releases 🚀", + "content": "Since the previous episode, there are some AndroidX releases worth highlighting.Core and core-ktx made it to 1.9.0 stable. This version improves compatibility with Android 13 adding parity between the accessibility framework and compat APIs, and some other additions. Other releases include new in beta Car app 1.3, and new in alpha Navigation 2.6 and Test Ui Automator 2.3.", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "24", + "title": "Precise Improvements: How TikTok Enhanced its Video Social Experience on Android", + "content": "The Developer Relations team wrote about how TikTok Enhanced its Video Social Experience on Android. They were able to significantly improve their overall performance by following Android’s performance guidance, and employing their deep understanding of development tools such as Android Gradle Plugin and Jetpack libraries. Read more here!", + "url": "https://android-developers.googleblog.com/2022/08/precise-improvements-how-tiktok-enhanced-its-social-experience-on-android.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvwJsvRHyECCaiD1FaBBCLGrUr-PoZoDaqm9aUKoswBFPOlWyKNvcC94FhX6M6Ugdo0wVTdZyI48BUmKaiA1xSgOcEE_xOFt9EGmoHd9PDHyJ4mAiKrfjnFHBIEKgjL1JhFeTQWbjWec4DJX-q9lnYAw5b9l0vC7nM09QBKtItv7JmBNxjYosCroQI/s1600/241588700__38488906__148018.png", + "publishDate": "2022-09-13T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [] + }, + { + "id": "25", + "title": "The deep links crash course, Part 1: Introduction to deep links", + "content": "Sabs started a crash course series about deep links. The first part is an introduction to deep links with a blog post and a video. Get to know what a deep link is, go from URIs to app links, and more!", + "url": "https://medium.com/androiddevelopers/the-deep-links-crash-course-part-1-introduction-to-deep-links-2189e509e269", + "headerImageUrl": "https://miro.medium.com/max/720/1*m44rS8zc3W23lmDy1_Vu8g.png", + "publishDate": "2022-09-01T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "83" + ] + }, + { + "id": "26", + "title": "Experimenting with Jetpack Glance", + "content": "Marcel wrote about experimenting with Jetpack Glance that covers a standalone experimental repository to supplement Jetpack Glance with tools that are commonly required for development but not yet available. At the moment, it includes a composable to display RemoteViews inside your app (enabling Live Edit), a debug tool to view and interact with AppWidget snapshots embedded inside the app, and a Material3 Scaffold for AppWidgets.", + "url": "https://medium.com/androiddevelopers/experimenting-with-jetpack-glance-35fbffe520f4", + "headerImageUrl": "https://miro.medium.com/max/720/0*gfm9zG95iVoEX5Gu", + "publishDate": "2022-08-31T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "2" + ], + "authors": [ + "9" + ] + }, + { + "id": "27", + "title": "Jetpack Compose: Debugging Recomposition", + "content": "Ben Trengove wrote about Debugging recomposition in Compose. Check it out because it also contains a screencast of Ben fixing a performance issue in Jetsnack, a Compose sample. For this, Ben also uses the layout inspector in Android Studio where you can see the recomposition and skip counts of composable functions.", + "url": "https://medium.com/androiddevelopers/jetpack-compose-debugging-recomposition-bfcf4a6f8d37", + "headerImageUrl": "https://miro.medium.com/max/720/1*gwdtRcu1bo_PoH8rwh5E4A.png", + "publishDate": "2022-09-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7", + "3", + "5", + "5" + ], + "authors": [ + "80" + ] + }, + { + "id": "28", + "title": "Optimize for Android (Go edition): Lessons from Google apps - Part 1", + "content": "Nikariha started another blog post series about optimizing for Android Go edition. The first part introduces Android Go edition, why you’d want to build for it, and some best practices based on experience building the Gboard and Camera from Google apps.", + "url": "https://android-developers.googleblog.com/2022/09/optimize-for-android-go-lessons-from-google-apps-part-1.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT34hXV07gVlKKi5X9mjpDGRlawITJfAKr7BpE7E02gtIYVqxYW8RoyjX_SPWJo0KS4PcBNy9rqITsAx0UnXeZp0V6zEoldaBCy9FJ9wyyebLEpPoxJgT6BENWxJqpIrihbpcwUsXO45qhcDAJJ3zTldnKkT8Dw_5VGxl2xYTA2trIVGsczYZLJgKj/s1600/MAD%20App%20Architecture%20launch%20-%20header%20%282%29.png", + "publishDate": "2022-09-07T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "73" + ] + }, + { + "id": "29", + "title": "ADB Podcast Episodes🎙: Episode 188 - Android 13", + "content": "In Episode 188: Android 13, Chet, Romain, and Tor talk about some of their favorite new features and changes of the new version of Android, both for users and developers.", + "url": "https://adbackstage.libsyn.com/episode-188-android-13", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/4/6/e/5/46e518b4880184c288c4a68c3ddbc4f2/ADB_188_Android_13.jpg", + "publishDate": "2022-08-31T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "13" + ], + "authors": [ + "31" + ] + }, + { + "id": "30", + "title": "Cross device SDK Developer Preview 📱↔️📱", + "content": "We launched the Cross device SDK for Android developer preview, which allows you to build rich multi-device experiences, abstracting away the intricacies involved with working with device discovery, authentication, and connection protocols.", + "url": "https://android-developers.googleblog.com/2022/07/announcing-cross-device-SDK-Developer-Preview-for-Android.html", + "headerImageUrl": "https://miro.medium.com/max/1280/0*1CDepdsU40_03H5K.png", + "publishDate": "2022-08-25T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8", + "6" + ], + "authors": [ + "84" + ] + }, + { + "id": "31", + "title": "CameraX 1.2 is now in Beta", + "content": "CameraX version 1.2 is now in Beta. It introduces a zero shutter lag capture mode along with MlKitAnalyzer, an implementation of ImageAnalysis.Analyzer that handles much of the ML Kit setup for you. MlKitAnalyzer works with both cameraController and cameraProvider workflows and can even handle coordinate transformations between ML Kit output and your PreviewView. Zero shutter lag greatly reduces image capture lag on supported devices by using a circular buffer of captures to get the frame closest to the actual press of the shutter button.", + "url": "https://android-developers.googleblog.com/2022/08/camerax-12-is-now-in-beta.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS_SSchtaoz90hvgXHZQzwD61bSnd06zOvd7L2sLG-isR8ykrzy7Afk1snnZjCBVkNtMXrmCJIMJfp-gP3X3NMXSbPdVvEgmpqSfTIph-vc_QkBVPDgH8ZQonnMu-XY5Aasi4tp1nmI5jetU2eF4TK_AMOWqA0gLxadk-0dPt2wjpruoDOhxP4PhE_/w681-h202/Android_NewCameraXInBeta_4209x1253.png", + "publishDate": "2022-08-23T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [ + "85" + ] + }, + { + "id": "32", + "title": "Build Tiles fast with the WearOS Material Tiles Library ⌚", + "content": "Tiles are one of the most used surfaces on Wear OS, providing users glanceable access to the information and actions they need to get things done quickly. We launched the Tiles Material library allowing you to use pre-built Material components such as Button, Chip, CompactChip, TitleChip, CircularProgressIndicator, and Text along with layouts such as PrimaryLayout, EdgeContentLayout, MultiButtonLayout, and MultiSlotLayout to create tiles that embrace the latest Material design for Wear OS. Together with the Tiles Design Kit, they help you to easily follow the Tiles Design Guidelines.", + "url": "https://android-developers.googleblog.com/2022/08/wear-os-tiles-material-library-build-tiles-fast.html", + "headerImageUrl": "https://miro.medium.com/max/1278/0*tgTI6u6xZZFHhvSc.png", + "publishDate": "2022-08-23T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "2" + ], + "authors": [ + "16", + "86" + ] + }, + { + "id": "33", + "title": "New deep links monitor in Play Console 🔗", + "content": "Deep links allow you to get your users directly to in-app content by accepting traffic from external sources, including the web. Since answering basic questions like “is this URL deep-linked?” or “why is this deep link not working?” can be difficult to answer, many apps have partial, broken, or no deep links configured. To make it easier for you to keep your deep links in good shape, we’ve introduced a new, dedicated Play Console page that gives you a quick but comprehensive snapshot of your current setup along with tooling to help you identify and troubleshoot issues at a glance.", + "url": "https://android-developers.googleblog.com/2022/08/monitor-your-deep-links-in-one-place.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj42yJ079EEgbm1oAfoXwCKB_LxBw_0iEPsixWd572w6BThCaA5P-O6Ahp7P6SCcCgLZ59rKPtQFkfbpGeVn-f7dk2ef81nSMMqHz3IEw1FL9fAfhiFGgPJZNu5wny2AoWWZ0Ma1PAqGkSGS60eGB59abQHdQ_Hb-_9VdEnS7yg4JLmUIUuW3dNxg0l/w640-h190/Android-DeepLinksWithGooglePlayConsole_4209x1253%20.png", + "publishDate": "2022-08-21T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12", + "2", + "5" + ], + "authors": [ + "87", + "88" + ] + }, + { + "id": "34", + "title": "Celebrating 5 years of Kotlin on Android 🎉", + "content": "Five years ago Android announced official support for the interoperable, mature, production-ready, and open source Kotlin programming language. Since then, JetBrains and Google have been collaborating around the development of Kotlin, and the Kotlin Foundation was co-founded by the two companies; JetBrains developing both the language and tooling has given Kotlin outstanding IDE support. We put together some posts and videos to celebrate the journey and elaborate the milestones of Kotlin on Android with many of the people that helped to make it happen.", + "url": "https://android-developers.googleblog.com/2022/08/celebrating-5-years-of-kotlin-on-android.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRKysS-6n5YNTJAtLz2PkRNV5XsFnSlod6hwTvKRHbUb0W5pE8RszvmTfFK6GNbh2TKa3dbTP1AjB4pI0NB3agCRb1F4MbP5LQb6Q-8oveLb-mDjqFteWaZnIaztK4W1yONSJ5M6ffWAt-qu9CAu04v0PBIg6OIm9kFHMX6kolmf3zkagX2MIDDOtn/w640-h192/Kotlin%20Header.png", + "publishDate": "2022-08-16T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "10" + ], + "authors": [ + "1" + ] + }, + { + "id": "35", + "title": "Mad Skills: Performance 🏎️💨", + "content": "The MAD Skills series on Performance continued with a blog post from \nBen and a video from Tomáš that covers how to use the Macrobenchmark library along with UIAutomator to help generate Baseline Profiles for you. Baseline Profiles help your app to start and run faster by optimizing critical code paths ahead of time, allowing for a smoother user experience.", + "url": "https://medium.com/androiddevelopers/improving-performance-with-baseline-profiles-fdd0db0d8cc6", + "headerImageUrl": "https://miro.medium.com/max/1400/0*Tztd-PrhMpbWTXGC", + "publishDate": "2022-08-22T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "36" + ] + }, + { + "id": "36", + "title": "AndroidX releases 🚀", + "content": "In AndroidX, the Wear Compose Version 1.0.1 release fixed a logic bug in ScalingLazyColumn. As mentioned before, we released Wear Tiles Version 1.1. Webkit Version 1.5 added setAlgorithmicDarkeningAllowed, and added support for setting an allow-list of URLs for the configured proxy.", + "url": "https://developer.android.com/jetpack/androidx/versions", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-10-25T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "8", + "19" + ], + "authors": [] + }, + { + "id": "37", + "title": "A story of App Excellence, featuring Tik Tok", + "content": "Over in video, we covered how TikTok used Android tools to improve app startup and make the user experience more seamless, and how it impacted app usage and Play Store ratings.", + "url": "https://youtu.be/k9Pdgiugleg", + "headerImageUrl": "https://i.ytimg.com/vi/k9Pdgiugleg/maxresdefault.jpg", + "publishDate": "2022-08-31T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5", + "7", + "2" + ], + "authors": [ + "61" + ] + }, + { + "id": "38", + "title": "Design high-quality apps for kids", + "content": "We introduced the Google Play Academy course around designing kids’ apps, ensuring that they’re fun, usable, and appropriate for their target age group. The course covers the framework for rating kids apps on Google Play that teachers across the US use, so you can understand what they’re looking for to help your app stand out.", + "url": "https://youtu.be/-FUmVUPThX8", + "headerImageUrl": "https://i.ytimg.com/vi/-FUmVUPThX8/hqdefault.jpg", + "publishDate": "2022-08-31T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "12", + "2" + ], + "authors": [ + "61" + ] + }, + { + "id": "39", + "title": "MAD about Media", + "content": "Avish, our summer Android DevRel Engineer intern, discussed modern approaches to creating Android media apps leveraging experience in converting the Universal Android Media Player (UAMP) media playback sample app to Compose, updating it to use modern libraries such as Media3.", + "url": "https://medium.com/androiddevelopers/mad-about-media-f536f7d601c", + "headerImageUrl": "https://miro.medium.com/max/1400/1*BKAchEMpYdP3dEyaIAP5xA.png", + "publishDate": "2022-08-25T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [ + "61" + ] + }, + { + "id": "40", + "title": "Top Tips for Adopting Android’s Notification Permission", + "content": "Terence covered tips to improve your app’s user experience with notifications before targeting Android 13, as well as how to test your app’s integration with the permission without flashing different OS versions onto your device.", + "url": "https://medium.com/androiddevelopers/top-tips-for-adopting-androids-notification-permission-bf69afd677b8", + "headerImageUrl": "https://miro.medium.com/max/1400/1*XQmi35H84FdYhY_ONP6ntQ.png", + "publishDate": "2022-08-23T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13", + "6" + ], + "authors": [ + "89" + ] + }, + { + "id": "41", + "title": "Jetpack Compose Accompanist — An FAQ.", + "content": "Ben wrote a FAQ on the Jetpack Compose Accompanist, a labs-like environment for new Compose APIs. Accompanist is used to help fill known gaps in the Compose toolkit, experiment with new APIs and to gather insight into the development experience of building a Compose library. The goal of Accompanist is to eventually upstream libraries into the official toolkit. (at which point they will be deprecated and removed from Accompanist) Current libraries in Accompanist include support for Flow Layouts, Pager, Navigation Transitions, and Swipe Refresh.", + "url": "https://medium.com/androiddevelopers/jetpack-compose-accompanist-an-faq-b55117b02712", + "headerImageUrl": "https://miro.medium.com/max/1400/1*w_MA7M6H9HpwdWb_fx-2IA.png", + "publishDate": "2022-08-18T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "80" + ] + }, + { + "id": "42", + "title": "Introducing the MAD Skills series on Performance", + "content": "It’s time for another series of MAD Skills on Performance! Performance spans every aspect of Android development, and as part of Modern Android Development we’re aiming to make it more approachable and user-friendly. We have also found direct correlations between improved app performance, user satisfaction and business metrics.", + "url": "https://medium.com/androiddevelopers/introducing-the-mad-skills-series-on-performance-7dbb26e8b17f", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkWBi6t47sZvF2EqduUT_a38uamN_jLbjDIoada1oN9PSbkyyduU1f_x6t4H8gX1ghq11Wyt09dBjw-l3efO5EO62AvdrVELnk4qc6Xft96Fk_ViJ8xipsPXirDnvVoYw44tl-gJqUHqOXxrdbPbZjjGwXGmoLL992o_5AMdkpnWyoL0oz8HrAJagH/w1200-h630-p-k-no-nu/unnamed_%281%29.png.jpeg", + "publishDate": "2022-08-09T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7", + "5" + ], + "authors": [ + "36" + ] + }, + { + "id": "43", + "title": "MAD Skills: Important Performance Metrics", + "content": "Before you begin to work with performance effectively, we recommend you make yourself familiar with key performance metrics. By understanding what metrics you should look at, you will make inspecting, improving and monitoring performance easier.", + "url": "https://medium.com/androiddevelopers/important-performance-metrics-c7dacf018eb3", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYdlNQTXP5pTpqN3fQwfF__WHtEmflMVuLS6ErWorUPYM9MwThUmwuBfFhDMVtw5X1jVmchC9z20Bl_yD7M_thVbCmhRJLyZqh3sHZBm6Sryz_xyu4cvusk_xx1kJCh5ANM-TtsvG1WwqMUPllTZegJlstUj3KDesGJ2Xrh6AsLU9HvaFCLT4RLZd7/w1200-h630-p-k-no-nu/resize%20play_10-android_dev.png", + "publishDate": "2022-08-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "36" + ] + }, + { + "id": "44", + "title": "MAD Skills: Inspecting Performance\n", + "content": "This MAD Skills article on inspecting performance introduces you to tools and methods that help when your code’s performance. When you inspect performance you make sure that what’s happening in your app aligns with what you expect to happen.", + "url": "https://medium.com/androiddevelopers/inspecting-performance-95b76477a3d7", + "headerImageUrl": "https://1.bp.blogspot.com/-9MiK78CFMLM/YQFurOq9AII/AAAAAAAAQ1A/lKj5GiDnO_MkPLb72XqgnvD5uxOsHO-eACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-Compose-1.0-header-v2.png", + "publishDate": "2022-08-14T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7", + "5" + ], + "authors": [ + "36" + ] + }, + { + "id": "45", + "title": "Consuming flows safely in Jetpack Compose", + "content": "Collecting flows in a lifecycle-aware manner is the recommended way to collect flows on Android. If you’re building an Android app with Jetpack Compose, use the collectAsStateWithLifecycle API to collect flows in a lifecycle-aware manner from your UI.", + "url": "https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3", + "headerImageUrl": "https://miro.medium.com/max/720/1*LL7FLWzjT4c6bQdGlvdz7w.png", + "publishDate": "2022-08-09T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "8", + "4" + ], + "authors": [ + "23" + ] + }, + { + "id": "46", + "title": "Brushing up on Compose Text coloring", + "content": "Alejandra Stamato’s last article covered compose brush text coloring, and this week she taught us how to take it a step further and add animation to brush text coloring. She covers using the animation APIs in conjunction with the Brush APIs, demonstrating these with a candy cane shimmer effect and a back-and-forth shimmer effect.", + "url": "https://medium.com/androiddevelopers/brushing-up-on-compose-text-coloring-84d7d70dd8fa", + "headerImageUrl": "https://miro.medium.com/max/720/1*PZK1BRIYM22iLQhexPGT1Q.png", + "publishDate": "2022-07-24T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "91" + ] + }, + { + "id": "47", + "title": "Final Android 13 Beta update, official release is next!", + "content": "We’re just a few weeks away from the official release of Android 13! Meanwhile, we published the last Beta for your testing and development. We reached Platform Stability at Beta 3, so all app-facing surfaces are final, including SDK and NDK APIs, app-facing system behaviors, and restrictions on non-SDK interfaces. With these and the latest fixes and optimizations, Beta 4 gives you everything you need to complete your testing.", + "url": "https://android-developers.googleblog.com/2022/07/Final-Android-13-Beta-update-official-release-is-next.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKBGMJx7yKi1RuRF9Q1X-1GOgfCvJ7XVIhNZlsmYgeabTPyljHhEOr2F0iGkF2BM7jeE1HYnL75GXllESyWgpEZEQWm9xfKU5a8kDgrvS5-ZZN0eTq0QaTsCBOAFkJzGX9kBTZxfo_4p6O3DYkXVqsBMRynTq1Mw3EDq3hwEL5OcoCrSQ8rOvFrraK/w1200-h630-p-k-no-nu/Compose%20Blog%20social.jpg", + "publishDate": "2022-07-12T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [ + "62" + ] + }, + { + "id": "48", + "title": "10 years of Google Play", + "content": "In 2012, the team opened the (digital) doors of Google Play. A decade later, more than 2.5 billion people in over 190 countries use Google Play every month to discover apps, games and digital content. And more than 2 million developers work with us to build their businesses and reach people around the globe! Congratulations to the Google Play team for this huge milestone.", + "url": "https://android-developers.googleblog.com/2022/03/celebrating-10-years-of-google-play.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-07-24T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [ + "63" + ] + }, + { + "id": "49", + "title": "Android Basics with Compose Unit 3 available", + "content": "The Unit 3 of Android Basics with Compose course is available already! Unit 3 covers how to build apps that display a list of data and how to make your apps more beautiful with Material Design.", + "url": "https://developer.android.com/courses/android-basics-compose/course", + "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", + "publishDate": "2022-08-02T23:00:00.000Z", + "type": "Codelab", + "topics": [ + "2", + "3", + "10" + ], + "authors": [] + }, + { + "id": "50", + "title": "Jetpack Compose 1.2 is now stable", + "content": "This release contains new features like downloadable fonts, lazy grids, and improvements for tablets and Chrome OS with better focus, mouse, and input handling.", + "url": "https://android-developers.googleblog.com/2022/07/jetpack-compose-1-2-is-now-stable.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1vg5QdkR-Hj7oQ3yueza1VGYFrNOBbuAxRQycRYGO6HLR-Hf2LR9DHT__OxVFecRYFZbVq6cYg6ca15h7kkKO99zzheFMB_x6MdTO74DaJpTR933pmrkJ-pWVq_7fEmN38nYHQv2u1l7-Ukk8RtNPrtGnn-ChdYwcbbx8Q-MnbJ3z3BjSj3U0Q-YX/w1200-h630-p-k-no-nu/header-image-predictive-back-blog%20%281%29.png", + "publishDate": "2022-07-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "65" + ] + }, + { + "id": "51", + "title": "Compose for Wear OS is now 1.0", + "content": "Compose for Wear OS makes building apps for Wear OS easier, faster, and more intuitive by following the declarative approach and offering powerful Kotlin syntax. Moving forward, Compose for Wear OS is our recommended approach for building user interfaces for Wear OS apps.", + "url": "https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsdruRjulgaFFtqwrnp6Z0mzIAhmMzJZIOUjVqugCB3i5noivoLOVecpMNBQGVIsG_kjkzthRTpibL-CEmlsn5nZJUhnSkkdhEe0V2yaNPQt2l-FGh0sQz1JnOZVRHRDZIr72twcPZQL7Q0kdgb-JzxgKJlZSsESJkMLuAkvqCfyXoE7d-XxFQYVoJ/w1200-h630-p-k-no-nu/Google_Android_DeveloperPoweredCTS_4209x1253.jpg", + "publishDate": "2022-07-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "19" + ], + "authors": [ + "75" + ] + }, + { + "id": "52", + "title": "AndroidX releases 🚀", + "content": "Jetpack Compose 1.2, Compose for Wear OS 1.0, Core splashscreen v1.0, and Profile Installer v1.2 went stable. In RC, you can find AppCompat v1.5, Compose Compiler v1.3 (that brings support to Kotlin version 1.7.10), Emoji2 v1.2, and ShareTarget v1.2. Lastly, Wear Tiles v1.1 reached its first beta version!", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirdkVqgyYoZDm0ktFFXjyIATaFKJUCVU0lIzQpTw4dlJjvqruWxLJn5mJ5xHoZijqVQ-poghVIGWGCpZM0Nb_bzx274kr1Lo_nn0PvEzMXcU_DgNEFrKzw5HtuE_vA9zfRVy8RDuiAIgC_aDVhHmGdqSLhzsPK5Pj2m3QNB4lzsf4E0VkbctqiowND/w1200-h630-p-k-no-nu/Android-discountinuing-kotlin-synthetics-for-views-social%20%281%29.png", + "publishDate": "2022-08-02T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "53", + "title": "Make your app large screen ready", + "content": "Learn how to get started with large screen support and why it is so important. Accurately handling orientation changes, aspect ratios, and adaptive layouts may seem challenging, but with new large screen experiences in mind, multiple form factors bring new possibilities to your users.", + "url": "https://medium.com/androiddevelopers/make-your-app-large-screen-ready-baf8fe505ae7", + "headerImageUrl": "https://miro.medium.com/max/1200/0*1hkxEoydoX8GzK9N", + "publishDate": "2022-07-19T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "90" + ] + }, + { + "id": "54", + "title": "Get familiar with Wear OS 3 (without a physical device)", + "content": "You don’t need a physical device to test your Wear apps. Read this article to take a brief look at unique UI surfaces on Wear OS, create a Wear emulator and explore it from a user perspective.", + "url": "https://medium.com/androiddevelopers/get-familiar-with-wear-os-3-without-a-physical-device-e7962c97f02b", + "headerImageUrl": "https://miro.medium.com/max/1200/1*3M48bGiXnBX8y83eYLgFtw.png", + "publishDate": "2022-07-20T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "2" + ], + "authors": [ + "86" + ] + }, + { + "id": "55", + "title": "Jetpack Compose Interop: Using Compose in a RecyclerView", + "content": "What versions of Compose and RecyclerView do you need to use to get the best performance? In addition, you’ll understand how the interop works under the hood.", + "url": "https://medium.com/androiddevelopers/jetpack-compose-interop-using-compose-in-a-recyclerview-569c7ec7a583", + "headerImageUrl": "https://miro.medium.com/max/1200/1*aBNjsK7y35V05OKNQ2oIZg.png", + "publishDate": "2022-07-21T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "2" + ], + "authors": [ + "68" + ] + }, + { + "id": "56", + "title": "Brushing up on Compose Text coloring", + "content": "A very colorful blog post about how to work with Brush API together with TextStyle to achieve complex text coloring like giving a gradient to your text in a simple way.", + "url": "https://medium.com/androiddevelopers/brushing-up-on-compose-text-coloring-84d7d70dd8fa", + "headerImageUrl": "https://miro.medium.com/max/1200/1*PZK1BRIYM22iLQhexPGT1Q.png", + "publishDate": "2022-07-24T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "2" + ], + "authors": [ + "91" + ] + }, + { + "id": "57", + "title": "Animating brush Text coloring in Compose 🖌️", + "content": "Learn how to animate gradients in your text using the Brush API and Compose animations. Go check them out, I can’t stop looking at those animations now!", + "url": "https://medium.com/androiddevelopers/animating-brush-text-coloring-in-compose-%EF%B8%8F-26ae99d9b402", + "headerImageUrl": "https://miro.medium.com/max/1200/1*9fEDrtJES1CQEVlyI7WjgQ.png", + "publishDate": "2022-07-31T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "2" + ], + "authors": [ + "91" + ] + }, + { + "id": "58", + "title": "Prepare your app to support predictive back gestures", + "content": "Predictive back gestures is a feature that will be available in future versions of Android. However, to give you more time to adopt it, we made it available in the developer options of Android 13 Beta 4. Read the blog post for details on how to try out the new gesture and support it in your apps. Spoiler alert: it’s straightforward for most apps!", + "url": "https://android-developers.googleblog.com/2022/07/prepare-your-app-to-support-predictive-back-gestures.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9NXOkaUCvb2KLyrnCp4DWpr2dyryXzHsqvX94Dcrw3r_5znwMZFy6PwmaHJj25D0DKYcZlF8Jac5C0KhM1s2j_mEc0VULf-eiCpT3JGbYgI_jg105SyUEwNG7w2dvF-60npxBgZidqgXqx7A1iWRftv9lOZrM9OAfc4f105met0ZauGQ5hRQC0_wE/w1200-h630-p-k-no-nu/image3.jpg", + "publishDate": "2022-07-28T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13" + ], + "authors": [ + "92", + "93", + "94" + ] + }, + { + "id": "59", + "title": "ADB Podcast Episodes🎙187: System UI: A Retrospective", + "content": "In this episode Tor and Chet meet Dan Sandler and Adam Cohen from the SystemUI team. They dip into a bit of history, talking about where things were at when they joined the team, and how things have developed in the many years since. They also talk about how to expose (or not) gestures and features in a UI system.", + "url": "https://adbackstage.libsyn.com/episode-187-system-ui-a-retrospective", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/9/4/d/b/94dbe077f2f14ee640be95ea3302a6a1/ADB184_Skia_and_AGSL.png", + "publishDate": "2022-07-24T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "2" + ], + "authors": [ + "32", + "31" + ] + }, + { + "id": "60", + "title": "Developer-Powered CTS (CTS-D) 🧪", + "content": "The Compatibility Test Suite (CTS) is a key part of the Android Compatibility Program that helps to ensure that devices provide a stable, consistent environment for your apps. To enhance CTS, we are adding a new test suite called CTS-D that is built and run by developers like you. You can build and contribute test cases to CTS-D to help catch pain points that you are seeing in the field — places where device behavior doesn’t match the Android public APIs and the Android Compatibility Definition Document (CDD).", + "url": "https://android-developers.googleblog.com/2022/06/developer-powered-cts-cts-d.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsdruRjulgaFFtqwrnp6Z0mzIAhmMzJZIOUjVqugCB3i5noivoLOVecpMNBQGVIsG_kjkzthRTpibL-CEmlsn5nZJUhnSkkdhEe0V2yaNPQt2l-FGh0sQz1JnOZVRHRDZIr72twcPZQL7Q0kdgb-JzxgKJlZSsESJkMLuAkvqCfyXoE7d-XxFQYVoJ/w1200-h630-p-k-no-nu/Google_Android_DeveloperPoweredCTS_4209x1253.jpg", + "publishDate": "2022-06-22T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "6", + "13" + ], + "authors": [ + "98" + ] + }, + { + "id": "61", + "title": "Independent versioning of Jetpack Compose libraries ✒️", + "content": "We announced that the various Jetpack Compose libraries will move to independent versioning schemes, making it easier to incrementally upgrade your application and stay up-to-date with the latest Compose features.", + "url": "https://android-developers.googleblog.com/2022/06/independent-versioning-of-Jetpack-Compose-libraries.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjolHnYXFnb81t2qu38Z8BPxU0QNitCVulwRwgZlijGDwCbcSSPETvSVr9apTSV_eDknzPDs1BwccZU_lYr15czYU_ddiXete76bVxWWIhNE29-PfOCxMzvashjOwvGWrzZ4rynym-k4aNQ4c-tmN7v4O5vh0iaRpFZTMuYTFqjFLrNpHNlOwSyZyf6/w1200-h630-p-k-no-nu/unnamed_%281%29.png.jpeg", + "publishDate": "2022-06-28T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "65" + ] + }, + { + "id": "62", + "title": "Notes from Google Play: making Play work for everyone ▶️", + "content": "In the latest edition of Notes from Google Play, we touched on the Play Integrity API, the Data Safety section, the Privacy Sandbox on Android, and the newly-launched Google Play SDK Index, which provides data and insights about over 100 of the most widely used commercial SDKs. We covered new subscription capabilities that allow you to create multiple base plans and offers for each subscription, as well as the option to lower prices starting at the equivalent of 5 US cents to adapt to local purchasing power.", + "url": "https://android-developers.googleblog.com/2022/06/notes-from-google-play-making-play-work.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicboZEaxs6kOlhHSoRE__yCFdTkFvVW7z9ksAfVlkdCVgNQzkG1B1z4RVCV6l3g-Up3ZPGchGjq5idAKV5prrVVy9T2o6MtJh-iXZtUKcKyNY1Cqt39bi5VzwZ2CLy7N3FANcklla-mHIGboZzvNRl3eN5ZMvjl29tjtGGLGubVKIYwIUCh6q2-FhT/w1200-h630-p-k-no-nu/AD%20Blog%20Social.png", + "publishDate": "2022-06-21T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "63", + "title": "Dark theme testing in Pre-Launch Report 🕶️", + "content": "After you upload and publish a test Android App Bundle to Google Play, we install it on a set of Android devices, launch and crawl your app for several minutes, and compile your results into the pre-launch report. We’ve introduced a new test in the Pre-Launch Report that runs accessibility checks on a device switched to dark theme; this can detect color contrast issues that make it difficult to differentiate text and icons from a background. ", + "url": "https://support.google.com/googleplay/thread/170731936/dark-theme-test-now-included-in-pre-launch-report", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJtTKEaaohJa7b5H5mkmCNu1LbQ3FPrPv0hSVWdEacemvtEHRWhk-DCi5aEartYwL0OMg6NOHJ1Vnn1fqeJ5cMc7Bl08SY7JcEBpKp5Vde-y_VDIPoVNlhb5VZbyv4PlauW_xpvnf6iS8yszMOnuo5w0Rw5NmYZ45reEvulY2KgGoPaG9NZ6H8hO2b/w1200-h630-p-k-no-nu/Android_SandboxPreview_V2_1024x512.jpg", + "publishDate": "2022-07-10T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "12", + "6" + ], + "authors": [] + }, + { + "id": "64", + "title": "Performance tips to achieve App Excellence", + "content": "The Performance tips to achieve App Excellence video covered five app performance issues along with the tools that Android Studio and Google Play Console provide to help you diagnose and troubleshoot them.", + "url": "https://www.youtube.com/watch?v=VJItLXb7_V8", + "headerImageUrl": "https://i.ytimg.com/vi/VJItLXb7_V8/maxresdefault.jpg", + "publishDate": "2022-07-05T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "7", + "2" + ], + "authors": [] + }, + { + "id": "65", + "title": "Making Sense of Intent Filters in Android 13", + "content": "Before Android 13, when an app registered an exported component in its manifest and added an , the component could be started by any explicit intent — even those that do not match the intent filter. In some circumstances this can allow other apps to trigger internal-only functionality.\n\nThis behavior has been updated in Android 13. Now intents that specify actions and originate from external apps are delivered to an exported component if and only if the intent matches its declared elements.", + "url": "https://medium.com/androiddevelopers/making-sense-of-intent-filters-in-android-13-8f6656903dde", + "headerImageUrl": "https://miro.medium.com/max/1200/1*PX8VuYcLzaC-AvOtSPgMRw.png", + "publishDate": "2022-07-05T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13", + "2" + ], + "authors": [ + "99" + ] + }, + { + "id": "66", + "title": "Customizing AnimatedContent in Jetpack Compose 🌟", + "content": "Rebecca covers using AnimatedContent to transition between different composables with a smoother and more custom transition effect. Even the default behavior of AnimatedContent can make a big difference to the look and feel of your app, without much effort.", + "url": "https://medium.com/androiddevelopers/customizing-animatedcontent-in-jetpack-compose-629c67b45894", + "headerImageUrl": "https://miro.medium.com/max/1200/1*Yu-W3qMxx1YyPm_AJMcXGg.png", + "publishDate": "2022-06-30T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "96" + ] + }, + { + "id": "67", + "title": "Jetpack Compose Stability Explained", + "content": "Ben does a detailed exploration of how Compose determines the stability of each parameter of your composables to see what can be skipped during recomposition, including using compiler reports to determine what stability is being inferred about your classes. ", + "url": "https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8", + "headerImageUrl": "https://miro.medium.com/max/1200/1*iLEtRB3BpIkD6CgjWFP2RQ.png", + "publishDate": "2022-06-29T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "80" + ] + }, + { + "id": "68", + "title": "Migrating to the new coroutines 1.6 test APIs", + "content": "kotlinx.coroutines 1.6 introduces a set of new testing APIs, and the previous testing APIs are now deprecated.\n \nMarton talked about how we’ve migrated some of our own samples to the new APIs, covering a bunch of the necessary work for most Android projects.", + "url": "https://medium.com/androiddevelopers/migrating-to-the-new-coroutines-1-6-test-apis-b99f7fc47774", + "headerImageUrl": "https://miro.medium.com/max/1200/1*XQmi35H84FdYhY_ONP6ntQ.png", + "publishDate": "2022-06-28T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "10", + "6" + ], + "authors": [ + "1" + ] + }, + { + "id": "69", + "title": "Android 13 beta 3", + "content": "We released the third beta of Android 13! Android 13 has been built on our core themes of privacy and security, developer productivity, and large screen support. ​​Beta 3 takes Android 13 to Platform Stability, which means that the developer APIs and all app-facing behaviors are now final; you can confidently develop and release your updates. Read all about it in the post!\n", + "url": "https://android-developers.googleblog.com/2022/06/android-13-beta-3-platform-stability.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-06-07T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1", + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "70", + "title": "Google Play @ Google I/O - 3 updates you need to know", + "content": "In this video and blog post, Phalene tells us about the top three things to know about Google Play from Google I/O. These include updates on custom store listings, introducing more developers to the LiveOps beta, and new subscription capabilities. Learn more about these three topics in the video or blog post!\n", + "url": "https://android-developers.googleblog.com/2022/06/google-play-google-io-3-updates-you-need-to-know_01537187872.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg40pvJlLB9LP1shkyFOq4pIexSrdI-kSH9uPxMEdhjynUPm2Zdfy4W1sHb6v0d5hZqycnWP9qTVjxHu0DADwL2BrtBwkPrOOIFXA8-H2RC6W70ehcnYUTgKXy8eRvwvWDeu2J-0iVmMgkd4c1lyYUBnZi39mPVTJV5Ke83DvWMBioeLOWPivE0Tpvv/w1200-h630-p-k-no-nu/Android-io-spotlight-modern-android-development-social.png", + "publishDate": "2022-06-12T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1", + "12" + ], + "authors": [ + "76" + ] + }, + { + "id": "71", + "title": "Privacy Sandbox Developer Preview 3", + "content": "The Privacy Sandbox on Android aims to develop new solutions that preserve user privacy while enabling effective, personalized advertising experiences for apps. Now it is in Developer Preview 3, which adds APIs and resources for conversion measurement and remarketing use cases; this allows you to begin testing and evaluating impact on all key APIs for Privacy Sandbox on Android.", + "url": "https://android-developers.googleblog.com/2022/06/privacy-sandbox-developer-preview-3.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg5R2hd0VZp__xKUUgs6-tfKHEoabMPqo2aY6uoGvzre-9E4gUlz6RbGsrE-Txszbrc3OaNL9r2TshsZmzGhEiM3M-_eO8M39K6ljm9NrX2BMHRLHM3HeF04YgJf8l4Z1-kNaP9YV8BCRe3n2zTUTSx3FOvA5IRc4PCjVPiJ7CEw7M7Y7uAJLVd7WQ/w1200-h630-p-k-no-nu/Android-GoogleIO3thingstoknowaboutFormFactors_4209x1253.png", + "publishDate": "2022-06-15T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11" + ], + "authors": [ + "77" + ] + }, + { + "id": "72", + "title": "ADB Podcast Episodes🎙186: Live Edit", + "content": "In this episode, we talk with Alan and Esteban from Android Studio about the new \"Live Edit\" feature recently launched at Google I/O in the Electric Eel canary build. We dive into the technology -- how it works, what the technical challenges are, and its current state.", + "url": "https://adbackstage.libsyn.com/episode-186-live-edit", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2022-06-07T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "5", + "5" + ], + "authors": [ + "31", + "30", + "32" + ] + }, + { + "id": "73", + "title": "AndroidX releases", + "content": "Let’s take a look at what’s been up with AndroidX releases since the last episode of Now in Android. We have a few new features that are stable including Wear Watchface, Games-Activity, Benchmark, and Annotation", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizEliXuy2sTNnhRQtwNAvL46sKpcBLbHiuROrEiOtPDTJ1D0eQlWDDUjspVECqlDw3_sLhFzJO8SCrGJuFaT2QQ7Iezi0xrkhD7yWqpbacVLRC8aX-1bx0aZ-RM1k_S-S0LFTE0PrvX-BlNsmmilGCGMdvRk0v6zhHs8nKwdFv-AluPQIRjAtFx938/w1200-h630-p-k-no-nu/Android-GoogleIOSpotlightPrivacyAndSecurity_1024x512.png", + "publishDate": "2022-06-22T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "74", + "title": "Google I/O recaps ⏱ - Modern Android Development!\n", + "content": "Our goal is to make developing beautiful and engaging Android apps as fast and easy as possible. We want to take on the complex parts of building apps so that you can focus on your app’s features and deliver high quality experiences to your users.\n\nWe call this approach Modern Android Development (or MAD for short!) and deliver it through a suite of tools, libraries and guidance. At Google I/O we announced a number of updates and additions to our MAD offerings; here’s a recap of the three largest announcements.", + "url": "https://android-developers.googleblog.com/2022/05/io22-spotlights-mad.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpSQB-YhRGzOJ4X1hoh1DlnMx9cOxfUKdoriSncXDPuaXZXpiXGYBXpxJUsMVKPhGTbTKkT-Gn3g52Tqcy7Alyv6gkMQEKd7twzAj1JbR2DwdFUZYbIcnMgXD2PeRrkTq9jZw8XId5t0D9im6i3XkVCK-YIk10g3E7ut_pLnVdA9tVnGJU5T7XFLi8/w1200-h630-p-k-no-nu/unnamed.png", + "publishDate": "2022-05-22T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "5" + ], + "authors": [ + "57" + ] + }, + { + "id": "75", + "title": "Google I/O recaps ⏱ - Form factors!", + "content": "With close to half a billion cars, TVs, watches and laptops running on Android, it is more important than ever for apps to work seamlessly across every device. This year at I/O, we renewed our focus on form factors and announced major updates for Wear OS and Large Screens. To help you get to the bottom of what’s new, here are the three things you need to know about Form Factors at Google I/O", + "url": "https://android-developers.googleblog.com/2022/05/form-factors-google-io-22.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7lx3OtPkMXTr0cwNlItkSUwDQYcTAO1cP-fE8n_NLtnZQ5uBnoP-y0MfENfmuB_2HGRUbmrx_ADz4FmDW8VkBmp_WcdISO0uQiO4Dw2yi9XjBUPqwjX2o24j8lUEhvYWJidFi6ra9WrjHxO1sTCjwBZrLyXHhIjgbRZzFQX-oUOKeqvf1dXg4XQ-A/w1200-h630-p-k-no-nu/Android_ImplementingColor_1024x512.jpg", + "publishDate": "2022-05-30T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "2", + "16", + "15" + ], + "authors": [] + }, + { + "id": "76", + "title": "Google I/O recaps ⏱ - Android Privacy, Platform & Security!", + "content": "Amidst the whirlwind of content at Google I/O, we shared huge announcements involving privacy, security, and the Android platform. Read on for the details, and don’t forget to watch the topic playlist on YouTube.", + "url": "https://android-developers.googleblog.com/2022/06/privacy-security-google-io-22.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-06-05T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11", + "13" + ], + "authors": [ + "95" + ] + }, + { + "id": "77", + "title": "Spot your UI jank using CPU profiler in Android Studio", + "content": "Takeshi wrote about spotting your UI jank using the CPU profiler in Android Studio. The article goes through a step by step walkthrough about how to use the new jank detection UI in Android Studio Chipmunk including how to record a trace, and how to inspect janky frames.", + "url": "https://medium.com/androiddevelopers/spot-your-ui-jank-using-cpu-profiler-in-android-studio-9a4c41a54dab", + "headerImageUrl": "https://miro.medium.com/max/1200/1*FkkN0FugRiSDxhfp1TQz-Q.png", + "publishDate": "2022-05-15T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5" + ], + "authors": [ + "81" + ] + }, + { + "id": "78", + "title": "Custom Canvas Animations in Jetpack Compose ✨", + "content": "Rebecca Franks wrote about custom Canvas animations in Jetpack Compose. Using the Animatable states and some side-effects, you’ll be able to achieve custom animations as you were able to do with ValueAnimator in the View system.", + "url": "https://medium.com/androiddevelopers/custom-canvas-animations-in-jetpack-compose-e7767e349339", + "headerImageUrl": "https://miro.medium.com/max/1200/1*16bn5V--jLMAJLCWspst2Q.png", + "publishDate": "2022-05-16T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "96" + ] + }, + { + "id": "79", + "title": "Implementing Dynamic Color: Lessons from the Chrome team", + "content": "If you’re interested in Material You dynamic color, Rebecca Gutteridge talks about how the Chrome team implemented it and the things they kept in mind such as accessibility, custom colors, incognito, and more. It also comes with a really helpful list of recommendations from the designers and developers of the team.", + "url": "https://android-developers.googleblog.com/2022/05/implementing-dynamic-color-lessons-from.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-05-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2", + "3" + ], + "authors": [ + "97" + ] + }, + { + "id": "80", + "title": "ViewModel: One-off event antipatterns", + "content": "We’re very opinionated about what to do with ViewModel events in our UI events Architecture guidance, and this blog post explains why the alternatives to our recommendation might bring higher engineering costs to developers and a worse user experience.", + "url": "https://medium.com/androiddevelopers/viewmodel-one-off-event-antipatterns-16a1da869b95", + "headerImageUrl": "https://miro.medium.com/max/1200/0*ROW1i16idpH-rHO-.png", + "publishDate": "2022-05-31T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "23" + ] + }, + { + "id": "81", + "title": "Diving Into Compose — Lessons Learned While Building Maps Compose", + "content": "If you’re interested in Compose and Google Maps, Chris Arriola wrote about the lessons learned while building Maps Compose. You’ll see how they took advantage of Kotlin features, how to aim for binary compatibility, subcomposition, and more!", + "url": "https://medium.com/androiddevelopers/diving-into-compose-lessons-learned-while-building-maps-compose-d20ef5dfe1bb", + "headerImageUrl": "https://miro.medium.com/max/1200/1*6rFVWLu8FXGXfmASVP3zYQ.jpeg", + "publishDate": "2022-06-02T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "68" + ] + }, + { + "id": "82", + "title": "I/O 22: What's New in Android", + "content": "For a survey of what I/O 22 offers to Android developers in video form check out the What’s New in Android talk.", + "url": "https://youtu.be/Z6iFhczA3NY", + "headerImageUrl": "https://i.ytimg.com/vi/Z6iFhczA3NY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "83", + "title": "I/O 22: What's New for Android Devs", + "content": "Move quickly through some of the top material for Android Devs at IO/22", + "url": "https://www.youtube.com/watch?v=l0iBPh7k_HQ", + "headerImageUrl": "https://i.ytimg.com/vi/l0iBPh7k_HQ/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGUgUShhMA8=&rs=AOn4CLCNz_S_i8TyDdvX_y1-SZGyAfoK3A", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "1", + "5", + "3", + "8" + ], + "authors": [] + }, + { + "id": "84", + "title": "I/O 22: Android Fireside Chat", + "content": "Android Fireside Chat is back! Android leaders answered your questions from the stage.", + "url": "https://www.youtube.com/watch?v=wq3Et-D9P5Y", + "headerImageUrl": "https://i.ytimg.com/vi/wq3Et-D9P5Y/maxresdefault.jpg", + "publishDate": "2022-05-17T23:00:00.000Z", + "type": "", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "85", + "title": "13 Things to know for Android developers at Google I/O!", + "content": "There aren’t many platforms where you can build something and instantly reach billions of people around the world, not only on their phones—but their TVs, cars, tablets, watches, and more. Today, at Google I/O, we covered a number of ways Android helps you make the most of this opportunity, and how Modern Android Development brings as much commonality as possible, to make it faster and easier for you to create experiences that tailor to all the different screens we use in our daily lives.", + "url": "https://android-developers.googleblog.com/2022/05/13-things-to-know-for-android-developers-at-google-io.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC2X0sIY_AGvgi6jD8Eh_u8rOdZXKA6PP18tnJdA6jQxR-n4bF6vsIVI2D4FTOnHAlqSY5hJShEjHcRQr7P8QM-YyP3sM3Su_KxFRdBXhg8WUIoXr74luWfFvtgYGJHWdDe_gPnwpCsLR4YhE0U88QcSqrYs3LLjp7dGqQul_pRoerJr__-mD8lUPA/w1200-h630-p-k-no-nu/Android-IO22AndroidDevRecap_Social.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1", + "3", + "2", + "5", + "19" + ], + "authors": [ + "62" + ] + }, + { + "id": "86", + "title": "I/O 22: Now in Android, the App ⏱️", + "content": "After being available on this blog, our YouTube series, and a podcast, starting today, you can check out the alpha version of the Now in Android app on GitHub that was featured in the Google I/O 2022 Developer Keynote 🎉\n\nThe app showcases best practices, opinionated designs, and solutions to complex real-world problems. ", + "url": "https://android-developers.googleblog.com/2022/05/now-in-android-sample-app-alpha.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCfJQnd9fqg_J5d_j4lWDbJQ-u-sHd_Z_z8srVPoEuO3_CWY3eVZBulaRTPxqQV3VNkA_1qqkleLVYBI7tRtSIZsOsIDzOKKstOLehI8a1RAUwFgHpXY-3kEmoEPujjQZU1VUk08DesedqDdiA1ZOxUxR-XJIMb66G3gruUq3cxqHwokSQSWycIRPl/w1200-h630-p-k-no-nu/Now-in-Android%28Splash%29.jpg", + "publishDate": "2022-05-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "2" + ], + "authors": [ + "42" + ] + }, + { + "id": "87", + "title": "I/O 22: Jetpack, Compose, and Tooling 🚀", + "content": "What’s new in Jetpack covered additions and updates to the over 120 libraries we’ve created to address common pain points and make development easier.", + "url": "https://android-developers.googleblog.com/2022/05/whats-new-in-jetpack-compose.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQyCrAWdIb8-moiYuP7EdpznRLOLaKoZWJ04MLzMi1wkJrMfLKQshwXhB_ODNz3T6_aoOwQ0YccVpSbLO2K9qkpx-HTklvNm3ZR_spOINLr861_PgDXDnh6LgpptIyzR5Nv-UjlQ-5FyeLpHwOCb4NjZ8darLIomTVjHM2VvDv7YZdzO-FS6zMKEhlCQ/w1200-h630-p-k-no-nu/Android-JetpackCompose1.2-Social.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "8" + ], + "authors": [ + "65", + "52" + ] + }, + { + "id": "88", + "title": "I/O 22: Lazy layouts in Compose", + "content": "Compose brings a simple and performant way of creating scrolling lists, with fewer lines of code than RecyclerView. Learn how lazy layouts enable adding content on demand, how to use Lazy composables, how these work under the hood, and optimization tips for better performance.", + "url": "https://youtu.be/1ANt65eoNhQ", + "headerImageUrl": "https://i.ytimg.com/vi/1ANt65eoNhQ/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "5" + ], + "authors": [] + }, + { + "id": "89", + "title": "I/O 22: Fragments: The good (non-deprecated) parts", + "content": "Fragments have been in constant motion over the past couple of years as we move towards a world where all the behavior is defined. Some of these moves have resulted in new APIs designed to do a specific function and replace old, unreliable ones. Learn about the changes in menus, fragment communication, the new strict mode, single lifecycle, and more.", + "url": "https://www.youtube.com/watch?v=OE-tDh3d1F4", + "headerImageUrl": "https://i.ytimg.com/vi/OE-tDh3d1F4/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "5" + ], + "authors": [] + }, + { + "id": "90", + "title": "I/O 22: Performance best practices for Jetpack Compose", + "content": "Jetpack Compose can feel like magic, but what do you do when the magic isn't as performant as you want? Discover best practices in Jetpack Compose with regards to performance, common mistakes, and how to avoid them.", + "url": "https://www.youtube.com/watch?v=EOQB8PTLkpY", + "headerImageUrl": "https://i.ytimg.com/vi/EOQB8PTLkpY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "7" + ], + "authors": [] + }, + { + "id": "91", + "title": "I/O 22: Workshop: Basic layouts in Compose", + "content": "Learn how to implement a realistic and complex UI using Compose Layouts.", + "url": "https://www.youtube.com/watch?v=kyH01Lg4G1E", + "headerImageUrl": "https://i.ytimg.com/vi/kyH01Lg4G1E/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "2" + ], + "authors": [] + }, + { + "id": "92", + "title": "I/O 22: Workshop: State in Jetpack Compose", + "content": "Discover the core concepts of using state in Jetpack Compose by building a wellness app. Learn how the app's state determines what is displayed in the UI, how Compose keeps the UI updated when state changes, how to optimize the structure of your composable functions, and work with ViewModels in a Compose app.", + "url": "https://www.youtube.com/watch?v=PMMY23F0CFg", + "headerImageUrl": "https://i.ytimg.com/vi/PMMY23F0CFg/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "2" + ], + "authors": [] + }, + { + "id": "93", + "title": "I/O 22: Airbnb uses Jetpack Compose to empower devs to do their best work", + "content": "Jetpack Compose, Android’s modern UI-building toolkit, directly supports both of Airbnb’s development principles. Compose provided a solid foundation for adaptable, quality engineering and reduced boilerplate code, so developers could focus on delivering a great user experience — and advance their two-fold pursuit of engineering excellence.", + "url": "https://android-developers.googleblog.com/2022/05/airbnb-uses-jetpack-compose.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1-9FyHvhui7rTgRvUNvyQE8Mmrx5vQ1ZHnuYs0DMdkwFeBK7DuGIP2VL1sgxiQgBtrGvKF4j0QcKInGgSWXCx4bszhPM4VwmuUHm1VIBjmkJqBSWPYk4D9fPmDVhK3asVTNqmkxRjTidzZpzaUzQYn0JmQzjwblhp3el20qowfr00yTpPdKyhefcT/w1200-h630-p-k-no-nu/image1.gif", + "publishDate": "2022-05-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [] + }, + { + "id": "94", + "title": "I/O 22: What’s new in Android development tools ", + "content": "Get an overview of what's new in Android Studio for Android app developers, including demos and a presentation of relevant features that can accelerate developers' workflow on the latest Android APIs.", + "url": "https://www.youtube.com/watch?v=RFv8GkLd5OY", + "headerImageUrl": "https://i.ytimg.com/vi/RFv8GkLd5OY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5", + "2" + ], + "authors": [] + }, + { + "id": "95", + "title": "I/O 22: Designing apps for large screens", + "content": "Explore key concepts and strategies for adapting mobile apps to large screen devices, such as tablets and foldables. Dig into the challenges of optimizing design and finding ways to meet the changing expectations of your users, in order to offer the highest quality large screen app experience.", + "url": "https://www.youtube.com/watch?v=pvdqeIM6mh0&t", + "headerImageUrl": "https://i.ytimg.com/vi/pvdqeIM6mh0/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "96", + "title": "I/O 22: Learn how to update your app for the larger screen", + "content": "You already have an app you’ve been working on for years, with a set, and hard to change, architecture. Discover the many options to bring your existing app forward, and build optimized large screen experiences without starting from scratch.", + "url": "https://www.youtube.com/watch?v=1ZOQ_-XPSv8", + "headerImageUrl": "https://i.ytimg.com/vi/1ZOQ_-XPSv8/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "97", + "title": "I/O 22: Implementing Android apps for all screen sizes ", + "content": "Get a better understanding of how to build your app to fill all screen formats. Explore development best practices to optimize applications for all devices with an emphasis on Jetpack Compose, navigation, managing state, and testing.", + "url": "https://www.youtube.com/watch?v=MPwf5DklKp0&t", + "headerImageUrl": "https://i.ytimg.com/vi/MPwf5DklKp0/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "98", + "title": "I/O 22: Input for all screens", + "content": "Learn about some straight-forward best practices to support input methods like keyboard, mouse, and stylus. These simple changes can elevate your app experience and grow user engagement.", + "url": "https://www.youtube.com/watch?v=XtImpP23uhE", + "headerImageUrl": "https://i.ytimg.com/vi/XtImpP23uhE/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "99", + "title": "I/O 22: Building an adaptive layout with SlidingPaneLayout", + "content": "Learn how to add a list and detail layout to a View-based app with SlidingPaneLayout. Explore how to reconfigure your fragments, open and close the detail pane, handle custom back navigation, and disable gesture navigation.", + "url": "https://www.youtube.com/watch?v=2rtLdF9UFqg", + "headerImageUrl": "https://i.ytimg.com/vi/2rtLdF9UFqg/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "100", + "title": "I/O 22: Android Developer Story: eBay gets a 4.7 Google Play rating", + "content": "Matthew Mossman, Android engineer for eBay, shares how he and his team optimized the eBay app for large screens and discusses the impact they saw in their customer’s engagement and experience on the tablets.", + "url": "https://www.youtube.com/watch?v=8gGXwOxHQjk", + "headerImageUrl": "https://i.ytimg.com/vi/8gGXwOxHQjk/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "101", + "title": "I/O 22: Tablet moments, built by you!", + "content": "Android developers around the world are building some amazing experiences for tablets and large screen devices. You can see how Facebook, TikTok, HBO Max, Zoom and Google Slides are all enhancing their applications.", + "url": "https://www.youtube.com/watch?v=IRiEcVfJJko", + "headerImageUrl": "https://i.ytimg.com/vi/IRiEcVfJJko/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "102", + "title": "I/O 22: Second Beta of Android 13", + "content": "The Android 13 Beta is available to test on a range of devices from Asus, Lenovo, Nokia, OnePlus, Oppo, Realme, Sharp, TECNO, Vivo, Xiaomi, and ZTE.", + "url": "https://android-developers.googleblog.com/2022/05/second-beta-of-android-13.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjV9RykVMNPi7wNhkdMXSTn14sT_GE3-0m7iHPi6zfEIMlLjUr9I8icC7vKh7u0bTKqpU6PKnMKGufNHfE7QJJjvwU6PcTygM0Umd0sEh3C1lYKkAxyeJfOCJblem10kjPCZWlwpUT6E-ITy1F3AglIvqQAoA6mxcHCUAmjNzXfNXw2lCOLUQvjTHj/w1200-h630-p-k-no-nu/Android13_dpp.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "103", + "title": "I/O 22: Developing privacy user-centric apps", + "content": "Keeping users in control of their privacy and safeguarding everything they do online is more important than ever, particularly when it comes to the mobile operating system.", + "url": "https://www.youtube.com/watch?v=opGkUl8C-HM&t", + "headerImageUrl": "https://i.ytimg.com/vi/opGkUl8C-HM/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13", + "11" + ], + "authors": [] + }, + { + "id": "104", + "title": "I/O 22: Building the Privacy Sandbox", + "content": "The Privacy Sandbox initiative is an industry-wide effort to make Android and the web private. It introduces a set of privacy-preserving APIs that give both developers and entrepreneurs the tools they need to build thriving digital businesses and protect people's privacy online. Hear from a panel of Privacy Sandbox team members to hear answers to popular questions.", + "url": "https://www.youtube.com/watch?v=NKz5oT6kXI4&t", + "headerImageUrl": "https://i.ytimg.com/vi/NKz5oT6kXI4/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13", + "11" + ], + "authors": [] + }, + { + "id": "105", + "title": "I/O 22: Overview of the Privacy Sandbox on Android", + "content": "The Privacy Sandbox on Android is intended to fundamentally advance privacy for the ecosystem, without sacrificing key ads functionality and without putting access to free ad-supported apps at risk. Listen to an overview of the key changes and technical considerations for developers.", + "url": "https://www.youtube.com/watch?v=pQdzFbmlvOo", + "headerImageUrl": "https://i.ytimg.com/vi/pQdzFbmlvOo/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13", + "11" + ], + "authors": [] + }, + { + "id": "106", + "title": "I/O 22: Best practices for running background work on Android", + "content": "Learn about several changes to how and when apps can run tasks in the background. Discover why the changes were made and some best practices for developers to run work in the background.", + "url": "https://www.youtube.com/watch?v=t1_8WSEguDY", + "headerImageUrl": "https://i.ytimg.com/vi/t1_8WSEguDY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13", + "7" + ], + "authors": [] + }, + { + "id": "107", + "title": "I/O 22: What’s new in Android machine learning", + "content": "Learn about the latest APIs and early access programs for machine learning (ML) on Android.", + "url": "https://www.youtube.com/watch?v=tG6hiQNMLmE", + "headerImageUrl": "https://i.ytimg.com/vi/tG6hiQNMLmE/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "108", + "title": "I/O 22: What’s new in Android Camera", + "content": "A camera is one of the top reasons consumers purchase phones and devices. Explore the latest in Android Camera and upcoming launches. Learn best practices when using Android Camera and how to bring delightful experiences to users.", + "url": "https://www.youtube.com/watch?v=n8mubjwEVxQ", + "headerImageUrl": "https://i.ytimg.com/vi/n8mubjwEVxQ/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "18" + ], + "authors": [] + }, + { + "id": "109", + "title": "I/O 22: What’s new in Android media", + "content": "Get a high level overview of everything that's new in Android media, including media features and changes in Android 12+, as well as new features in ExoPlayer and the Jetpack media libraries. Hear about key announcements in Spatial audio, HDR video, video transcoding, editing APIs, AV1 decode, and Performance Class 13.", + "url": "https://www.youtube.com/watch?v=Ba70zmFZgk0", + "headerImageUrl": "https://i.ytimg.com/vi/Ba70zmFZgk0/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "18" + ], + "authors": [] + }, + { + "id": "110", + "title": "I/O 22: Workshop: How to optimize media streaming with ExoPlayer\n", + "content": "This workshop guides you through using the new Jetpack Media3 APIs to build a simple media app using ExoPlayer for progressive and adaptive streaming playback. Learn about the advantages of using ExoPlayer and the features it offers.", + "url": "https://www.youtube.com/watch?v=Hw0Jeq42FNU", + "headerImageUrl": "https://i.ytimg.com/vi/Hw0Jeq42FNU/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "18" + ], + "authors": [] + }, + { + "id": "111", + "title": "I/O 22: What’s new in Accessibility for developers\n", + "content": "Making applications accessible ensures equal access to roughly one billion people in the world with disabilities, and it can also benefit people without disabilities by providing a better user experience in general. Learn about new developments in Android Studio, a new API that improves the video consuming experience, and advancements in Jetpack Compose that can help you build more accessible apps.", + "url": "https://www.youtube.com/watch?v=6LsaP6oKxMY", + "headerImageUrl": "https://i.ytimg.com/vi/6LsaP6oKxMY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [] + }, + { + "id": "112", + "title": "I/O 22: What's new in app performance", + "content": "Users expect apps to launch quickly and scroll smoothly, even on low-end devices. That's why performance enhancements should be available on devices with older versions of Android. Learn how to write, maintain, and monitor apps that perform to users' high standards.", + "url": "https://www.youtube.com/watch?v=DYdHLqLVspY", + "headerImageUrl": "https://i.ytimg.com/vi/DYdHLqLVspY/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "7" + ], + "authors": [] + }, + { + "id": "113", + "title": "I/O 22: Introducing Google Wallet and developer API features", + "content": "Learn more about how to use the Google Wallet APIs to digitize tickets, loyalty cards, and much more. Discover the new Android SDK and additional developer tools to simplify your integration.", + "url": "https://www.youtube.com/watch?v=2gTCghy-dU4", + "headerImageUrl": "https://i.ytimg.com/vi/2gTCghy-dU4/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "114", + "title": "I/O 22: Android solutions for seamless sign-in across devices", + "content": "Discover Android solutions that enable seamless and secure login experiences so users can enjoy your app across devices.", + "url": "https://www.youtube.com/watch?v=xghjqgj4peA", + "headerImageUrl": "https://i.ytimg.com/vi/xghjqgj4peA/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "115", + "title": "I/O 22: Build power, multi-device experiences\n", + "content": "Developers play a critical role in Google's multi-device ecosystem. Hear about new tools available to you so that you can build your own powerful, multi-device experiences that span platforms and form factors. Learn about Cast, BlockStore, and the new abstraction layer D2DI.", + "url": "https://www.youtube.com/watch?v=H6UxTnghkMw", + "headerImageUrl": "https://i.ytimg.com/vi/H6UxTnghkMw/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "116", + "title": "I/O 22: Announcing Compose for Wear OS Beta!", + "content": "Today we’re launching the Beta release of Compose for Wear OS, our modern declarative UI toolkit designed to help developers create beautiful user experiences for Wear OS.", + "url": "https://android-developers.googleblog.com/2022/05/announcing-compose-for-wear-os-beta.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrfM5DyOkb4yfuiuJEOelmS5x4sTioxYCRPdnoSI1h64j-xWkWq9Wk0mZ61ljUw_tkO7NXxKsDUb5TbYMHLxLxhcY24rzNnnDhjzFOOClkM_WH--2bTLJFq93HODS7PHebGl00oluu0Sg5p0MTeNAfusLgHvorSxTDS26YwnQXkTJsDq2HJC36m7Jl/w1200-h630-p-k-no-nu/image4.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "3", + "8", + "13" + ], + "authors": [ + "75" + ] + }, + { + "id": "117", + "title": "I/O 22: Introducing Health Connect", + "content": "Introducint Health Connect, a new API for Android app developers to securely access user health dataAs Android developers, connecting and sharing health and fitness data between apps can help you provide more meaningful experiences and insights for your users", + "url": "https://android-developers.googleblog.com/2022/05/introducing-health-connect.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioB9TK2fLuYAv8nIzhQMGsmSQJFhcWTsEl4ZUkR1XXbEkmR4JR3ZBP2N3YLYU143Lo02Qsx3iXE2VBobBBDJ0fr9V_2_epxOtnDLRA9S2XpkUdAWO-OyBejhkrf53wv4sIqnaqmjRB8iu8XzeFhCgM00gxgln1M-ipVeww9WG5bduNTBcpxRpASMha/w1200-h630-p-k-no-nu/231491533__37260715__148018.png", + "publishDate": "2022-05-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "118", + "title": "I/O 22: What’s new with Google TV & Android TV OS", + "content": "Since last year’s I/O, we’ve continued our commitment to enable you to build better and more engaging experiences on Android TV OS. In addition to platform updates, new features, like expanded integrations with the Live tab, offer opportunities for users to better engage with your content.", + "url": "https://android-developers.googleblog.com/2022/05/whats-new-with-google-tv-android-tv-os.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkqwL9j8jLb9ItKoISX9Yo0d3r_IdPdNMje6cca3obZpFDuNmmfKsL6Qj-E3agkDK7E312kuVjVLw3Ez2dF-vVj9UeRSUUPuOuc2T3T9d-HqEaZ6g76NQXEvqwIik0rAqFRZPiE6NH2PfgFmaDDQh6hb81HRgQrzmWGT6WJyuyK-yxnrrCfV4YHYnh/w1200-h630-p-k-no-nu/Android_WhatsNewInAndroidTVGoogleTV_1024x512.png", + "publishDate": "2022-05-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "16", + "13" + ], + "authors": [ + "27" + ] + }, + { + "id": "119", + "title": "I/O 22: What’s new with Android for Cars", + "content": "We’re excited to share some of our combined accomplishments from this past year, and introduce new updates that will make it easier for you to provide users with an even better experience in the car.", + "url": "https://android-developers.googleblog.com/2022/05/whats-new-with-android-for-cars.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNnogZQP4IGFjoSw7QWt1vr7MphwOi1OtRAAfCAVaNDTdnD5_CAaKhsf11nzxS_XrQ3ERet5yhpK7bs0e5YXdarv6o8iuzNYRqJ25fZrRL8TPfyBGpADg3oOrGM364foSvEdNhSTqDOF_2_TTBkRq-rElETpNAC6pOIHioH7ug3s8z8iJ_f3vWL5pTuQ/w1200-h630-p-k-no-nu/Android-whats-new-with-android-for-cars-io22-social%20%281%29.png", + "publishDate": "2022-05-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "15" + ], + "authors": [] + }, + { + "id": "120", + "title": "I/O 22: What's new in Google Play", + "content": "At this year’s Google I/O, we focused on three major ways we can help you continue growing your business on Google Play: Privacy and security initiatives ; Tools to help you improve your app quality; New ways to help you acquire users and engage with existing ones", + "url": "https://android-developers.googleblog.com/2022/05/whats-new-in-google-play.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugYmi_1-WFvfxVe8BJ5GCiZjAp1R_B42dvrxu-fHkL1WkswlvjZpAVImVJIJdgzEERdFyzF9QzNZYPmoAJDEe2lfwdOnpSr2zHiQy0od18YP2ozVpC_fR7WQUpeB9kJyqTLx7udivLZn1w3trWfVeT4ejl8e9bZqVJfUyH05k0SgODQpYsUb4Junn/w1200-h630-p-k-no-nu/Play-new-google-play-io-01.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12", + "8", + "11" + ], + "authors": [] + }, + { + "id": "121", + "title": "I/O 22: New Google Play SDK Index", + "content": "New Google Play SDK Index helps you choose the right SDKs for your app. Helping developers, like you, make informed decisions about SDKs is part of keeping Google Play a safe, trusted space for billions of people.", + "url": "https://android-developers.googleblog.com/2022/05/new-google-play-sdk-index.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEBzXvqydpodashPiKchgLE9NA6WXVUYbTFBuooFn8_XwK6E4cMEbM7hyiTRPZ-H3pwTvyHY8143pGB5zgUt_zgUuzsjAIkRSQsngYBd4_dusLSXF94j6KZ0r1UiZC3vQFrabOw9vXdA0Wzcm3SDm_LvlCFdxW67-FplcMJLpZYLQ02I2EUrvORXHl/w1200-h630-p-k-no-nu/Play-new-google-play-sdk-index-social-V2.png", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [ + "87" + ] + }, + { + "id": "122", + "title": "I/O 22: How to integrate Android widgets with Google Assistant", + "content": "Explore intermediate-level concepts for integrating Android widgets with Google Assistant. Provide quick answers and interactive app experiences on Assistant enabled surfaces like Android and Android Auto to their users.", + "url": "https://www.youtube.com/watch?v=6vXZcg9g_Mo", + "headerImageUrl": "https://i.ytimg.com/vi/6vXZcg9g_Mo/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "123", + "title": "I/O 22: Integrate Google Assistant into Android for cars", + "content": "Learn how to integrate voice into apps built for Android Auto. Learn to add voice to Widgets for Auto, and explore some of the other voice-first features coming to the platform.", + "url": "https://www.youtube.com/watch?v=hhdVMLG5Y10", + "headerImageUrl": "https://i.ytimg.com/vi/hhdVMLG5Y10/maxresdefault.jpg", + "publishDate": "2022-05-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2", + "15" + ], + "authors": [] + }, + { + "id": "124", + "title": "I/O 22: ADB Podcast Episode 185 : Play Store🎙", + "content": "In this episode Tor, Chet, and Romain chat with Jon and Andrew from the Play team about the Play Store app, which recently went through a major refactoring.", + "url": "https://adbackstage.libsyn.com/episode-185-play-store", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2022-05-17T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "125", + "title": "Android Basics with Compose", + "content": "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. ", + "url": "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html", + "headerImageUrl": "https://developer.android.com/images/hero-assets/android-basics-compose.svg", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "Codelab", + "topics": [ + "2", + "3", + "10" + ], + "authors": [ + "25" + ] + }, + { + "id": "126", + "title": "Android 13 Beta 1", + "content": "Beta 1 includes the latest updates to photo picker, themed app icons, improved localization and language support, and the new notification permission which requires apps targeting Android 13 to request the notification permission from the user before posting notifications. Check out the beta by visiting the Android 13 developer site.", + "url": "https://android-developers.googleblog.com/2022/04/android-13-beta-1-blog.html", + "headerImageUrl": "https://developer.android.com/about/versions/13/images/android-13-hero_1440.png", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "127", + "title": "Architecture: Entities - MAD Skills", + "content": "In this episode, Garima from GoDaddy Studio talks about entities and more specifically how creating separate entities per layer per project leads to clean and scalable model architecture.", + "url": "https://www.youtube.com/watch?v=cfak1jDKM_4", + "headerImageUrl": "https://i3.ytimg.com/vi/cfak1jDKM_4/maxresdefault.jpg", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "128", + "title": "Architecture: Live Q&A - MAD Skills", + "content": "Manuel , Yigit , TJ , and Milosz hosted a very special Architecture Q&A and answered questions from the community. Find out the answers to: “Is LiveData deprecated?”, “MVVM or MVI for Compose”, “Should we use flow in DataSources” and more.", + "url": "https://www.youtube.com/watch?v=_2BtE1P6MPE", + "headerImageUrl": "https://i3.ytimg.com/vi/_2BtE1P6MPE/maxresdefault.jpg", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [ + "23", + "34", + "38" + ] + }, + { + "id": "129", + "title": "MAD Skills: Architecture", + "content": "To wrap up the Architecture Android MAD skills series, \nManuel wrote a blog post summarizing each episode of the series! Check it out to get caught up on all things Android Architecture.", + "url": "https://android-developers.googleblog.com/2022/04/architecture-mad-skills-series-wrap-up.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwpLUhIDR7IVIgPnCayAMbm0cYAC0ktSWLT9_vWJ1au0oZK_0un_IlXfu4HixEtc4G_AOi4BkWATw6BsyFCTPtCiu2wSvnfL3OVqWVNdblp6neIuFh9N3KH02SYDBgr6hIpAU7A9KjX9mT3oFJI6uuasaYqqMg_GZgptg0aooIqLywmeTp_PrpTAOj/s1600/1_J2NKRQ4qedvMVWoxL_4ZLA.jpeg", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "23" + ] + }, + { + "id": "130", + "title": "The first developer preview of Privacy Sandbox on Android", + "content": "Privacy Sandbox is a program to help you conduct initial testing of the proposed APIs and evaluate how you might adopt them for your solutions. The Developer Preview provides additional platform APIs and services on top of the Android 13 Developer Beta release, SDK, system images, Preview APIs, API reference, and support references. See the release notes for more details on what’s included in the release.", + "url": "https://android-developers.googleblog.com/2022/04/first-preview-privacy-sandbox-android.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimeEtKqNjaaRY2oxecefVbcytzULjln30fNMxfJQfWOu6Tqy9XYQKAVkwLTeGRiPh50RBIxyA6HD86_Qm_Vpiit7eEO1ZmeZttgdsH187-cL8YE-w6NOvqYDwcn-MzIPmk0yiJy-4_kbsZ4_k3CngfP36F-U5g-PQmunzFpPAHuWtBNCsHcbP80flJ/s1600/Android_PrivacySandboxonAndroidDeveloper_4209x1253.png", + "publishDate": "2022-05-04T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "11", + "13" + ], + "authors": [ + "77" + ] + }, + { + "id": "131", + "title": "Expanding Play’s Target Level API Requirements 🎯", + "content": "Starting on November 1, 2022, apps that don’t target an API level within two years of the latest major Android release version will not be available on Google Play to new users with devices running Android OS versions newer than your app’s target API level. For example, as of this November, existing apps need to target at least API level 30, Android 11, to be available to new users on Android 12 and 13 devices.", + "url": "https://android-developers.googleblog.com/2022/04/expanding-plays-target-level-api-requirements-to-strengthen-user-security.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifh6osWctzfS76FGmd91DArGexlVFw7BNh0ZCqgSuU5aO1AU2pt2T554nkpGy8AzeY_oIOY-TWc0YsS_DwMR9yp3aV_TSrgh7-XPNAg8jSDe_8ySG4ae6D6OqVUMzPmwEoPDXvEhA09um5qahSO1cfSjWIk03bq7vUVDvDHnvt-EubXLKw_Dz2uoUI/s1600/Android-New-policy-update-to-strengthen-Google-Play-social.png", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "12" + ], + "authors": [ + "10" + ] + }, + { + "id": "132", + "title": "Google Play PolicyBytes - April 2022 policy updates", + "content": "Users who have previously installed your app from Google Play will continue to be able to discover, re-install, and use your app, even if they move to a new Android device. App updates still also need to target an API level within a year of the latest major Android release version. Expanding our target level API requirements will protect users from installing older apps that may not have the privacy and security protections in place that newer Android releases offer.", + "url": "https://www.youtube.com/watch?v=O0UwUF2DgQc", + "headerImageUrl": "https://i3.ytimg.com/vi/O0UwUF2DgQc/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "133", + "title": "Upgrading Android Attestation: Remote Provisioning 🔐", + "content": "Attestation for device integrity has been mandated since Android 8.0, and is used in a variety of services such as SafetyNet. Android 12 added the option of Remote Key Provisioning for device manufacturers, and it will be mandated in Android 13. If you’re leveraging attestation in your app, watch out for a longer certificate chain structure, a new root of trust, the deprecation of RSA attestation, and short-lived certificates/attestation keys.", + "url": "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGH6tNkY1UkXgIQluciMoaSR9hZNKAoKcRyv_UxyHbEuPRvTVfWT4A_3BQEb_HCMUALR5bScXZsIEzHiRJwrFgm9fhouknFkP5H5ngCUtdf7uiGpTuCOm5dF5rtDrjR5Vm0r9NNU4J7lzN3k0sdMQumgan-NPp2nPSgXypTqj-yqn6BBS9URGrh1F/s1600/Android-KeyAttestation-Header.png", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "134", + "title": "Architecture: Handling UI events - MAD Skills", + "content": "With this episode of MAD skills we continue with our architecture series of videos. In this video you'll learn about UI events. Developer Relations Engineer Manuel Vivo covers the different types of UI events, the best practices for handling them, and more!", + "url": "https://www.youtube.com/watch?v=lwGtp0Yr0PE", + "headerImageUrl": "https://i3.ytimg.com/vi/lwGtp0Yr0PE/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [ + "23" + ] + }, + { + "id": "135", + "title": "Architecture: The Domain Layer - MAD Skills", + "content": "In this episode of MAD skills you'll learn about the Domain Layer - an optional layer which sits between the UI and Data layers. Developer Relations Engineer Don Turner will explain how the domain layer can simplify your app architecture, making it easier to understand and test.", + "url": "https://www.youtube.com/watch?v=gIhjCh3U88I", + "headerImageUrl": "https://i3.ytimg.com/vi/gIhjCh3U88I/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [ + "42" + ] + }, + { + "id": "136", + "title": "Architecture: Organizing modules - MAD Skills", + "content": "In this episode of Architecture for Modern Android Development Skills, Emily Kager shares a tip around organizing modules in Android apps.", + "url": "https://www.youtube.com/watch?v=HB_r9wn49Gc", + "headerImageUrl": "https://i3.ytimg.com/vi/HB_r9wn49Gc/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "137", + "title": "App Excellence: Android Architecture", + "content": "How do you build a successful app? Our advice: think like a building architect. If you need help getting started, we have the perfect blueprint for success when building on Android. Check out our updated guide to Android App Architecture, and build something that your users will love.", + "url": "https://www.youtube.com/watch?v=fodD6UHjLmw", + "headerImageUrl": "https://i3.ytimg.com/vi/fodD6UHjLmw/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "138", + "title": "Accessibility on TV - Integrate with Android TV & Google TV", + "content": "Thinking about accessibility is critical when developing a quality app on Android TV OS. Ian will be covering the most common issues, the solution to these issues, and some more complex scenarios. ", + "url": "https://www.youtube.com/watch?v=GyglHvJ6LMY", + "headerImageUrl": "https://i3.ytimg.com/vi/GyglHvJ6LMY/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "16", + "14" + ], + "authors": [] + }, + { + "id": "139", + "title": "Google Play Billing - Integrate with Android TV & Google TV", + "content": "In this episode of Integrate with Android TV & Google TV, Thomas will discuss how you can monetize your Android TV app using Google Play Billing. ", + "url": "https://www.youtube.com/watch?v=gb55CjH7NHY", + "headerImageUrl": "https://i3.ytimg.com/vi/gb55CjH7NHY/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "16" + ], + "authors": [] + }, + { + "id": "140", + "title": "Android for Cars 🚗", + "content": "Android for cars has introduced media recommendations powered by Google Assistant, a progress bar for long form content, and per-item content styles to allow browsable/playable items to be individually assigned to a list or grid. Head on over to the developer documentation to learn about all of these changes.", + "url": "https://developer.android.com/cars", + "headerImageUrl": "https://developers.google.com/cars/design/images/designforcars_1920.png", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "16" + ], + "authors": [] + }, + { + "id": "141", + "title": "Google Play Academy🎓 - Go Global: Japan\n", + "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Japan, one of the largest mobile app and gaming markets, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", + "url": "https://www.youtube.com/watch?v=hY1HH-9efkg", + "headerImageUrl": "https://i3.ytimg.com/vi/hY1HH-9efkg/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "142", + "title": "Google Play Academy🎓 - Go Global: Southeast Asia", + "content": "With over 2 billion active Android devices globally, more and more developers are looking for new markets to expand. If you’re looking to succeed in Southeast Asia, a fast-growing market that spends as much as 1.5x more time on the mobile internet than any other region, these courses will cover strategies to make your content relevant across development, marketing and growth, and monetization.", + "url": "https://www.youtube.com/watch?v=j9VRzvDhTO0", + "headerImageUrl": "https://i3.ytimg.com/vi/j9VRzvDhTO0/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "143", + "title": "Google Play Academy🎓 - Design for All Users", + "content": "Learn how to optimize for onboarding, build accessible apps, and reduce app size to reach more users.", + "url": "https://www.youtube.com/watch?v=07NUULjEJ5A", + "headerImageUrl": "https://i3.ytimg.com/vi/07NUULjEJ5A/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "144", + "title": "Game development 🎮", + "content": "We covered how to help you monitor your game’s stability using Android vitals on Google Play Console, how to best optimize your game to improve your customer engagement during the month of Ramadan, and announced that the Indie Games Accelerator & Indie Games Festival 2022 from Google Play is coming soon, offering a way to get notified when submissions open.", + "url": "https://www.youtube.com/watch?v=m2gTnT2kCRQ", + "headerImageUrl": "https://i3.ytimg.com/vi/m2gTnT2kCRQ/maxresdefault.jpg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "17" + ], + "authors": [] + }, + { + "id": "145", + "title": "Migrating Architecture Blueprints to Jetpack Compose", + "content": "Manuel wrote about how and why we’ve Migrated our Architecture Blueprints to Jetpack Compose, and some issues we faced in doing so.", + "url": "https://medium.com/androiddevelopers/migrating-architecture-blueprints-to-jetpack-compose-8ffa6615ede3", + "headerImageUrl": "https://miro.medium.com/max/1400/1*J2NKRQ4qedvMVWoxL_4ZLA.jpeg", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4", + "3" + ], + "authors": [ + "23" + ] + }, + { + "id": "146", + "title": "The curious case of FLAG_ACTIVITY_LAUNCH_ADJACENT", + "content": "Pietro wrote about how to enable split screen use cases using the Android 7.0 FLAG_ACTIVITY_LAUNCH_ADJACENT flag to open your Activity in a new adjacent window on Android 12L. (and some supported Android 11+ devices)", + "url": "https://medium.com/androiddevelopers/the-curious-case-of-flag-activity-launch-adjacent-f1212f37a8e0", + "headerImageUrl": "https://miro.medium.com/max/1400/1*YWg6uZkqSakAb5vW6uc-gg.png", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "12" + ] + }, + { + "id": "147", + "title": "AndroidX releases 🚀", + "content": "AppCompat AppCompat-Resources Version 1.5.0-alpha01 contains a bunch of bugfixes, as well as updated nullability to match Android 13 DP2 and a few small compatibility features involving TextView, AppCompatDialog, SearchView, and SwitchCompat.\n\nNavigation Version 2.4.2 has been released with all the new bugfixes backported from the 2.5 alpha releases.", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhxtjouMCgk8sv8wkvC5Aip4muxMo4TLnfSVtZ3Hw7ZpuqXQmk-EkV9qk9PKim0yVFVFlpjEJG-vqh6gCLFkQPuf2dQk6qEdQZM_9brvuxBA0dtOUlvUh7tMIQsF11RnSnSPWOPKDIzeiixfapL2ax4YgMahJppgG_a5rjs_4QBjzzgzqsDs9Wc-Ldx=w1200-h630-p-k-no-nu", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "148", + "title": "ADB Podcast Episodes🎙184: Skia and AGSL - Shaders of Things to Come", + "content": "In this episode Tor, Chet, and Romain chat with Derek and Brian from the Skia team about Skia (the graphics layer that backs the Android Canvas APIs), pixel shaders, and the new “AGSL” API that lets you provide pixel shaders for advanced graphics effects, which was recently added as part of the Android T preview release. If you’re interested in graphics technology, this is the episode for you.", + "url": "https://adbackstage.libsyn.com/episode-184-skia-and-agsl-shaders-of-things-to-come", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/9/4/d/b/94dbe077f2f14ee640be95ea3302a6a1/ADB184_Skia_and_AGSL.png", + "publishDate": "2022-04-13T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "2", + "8" + ], + "authors": [ + "32", + "30", + "31" + ] + }, + { + "id": "149", + "title": "Android 13 DP 2 😍", + "content": "Recently we shared Android 13 Developer Preview 2 with more new features and changes for you to try in your apps! Some notable features include Developer downgradable permissions which allows your app to protect user privacy by downgrading previously granted runtime permissions, and Bluetooth LE Audio which helps users receive high fidelity audio without sacrificing battery life; it can also seamlessly switch between different use cases that were not possible with Bluetooth Classic. Check out all the new features in the post!", + "url": "https://android-developers.googleblog.com/2022/03/second-preview-android-13.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjnrShXcFkBmErmhgdmx82vJbaKBIxU6p2Yz2Vr1V7AlFkD2tGwRmx_a7tWcInPmiUh8VpPmEEqXut-EjP23lFYG9wiMO4sKBDEwbZ3MNppZOy_HW54OXO4SkdQVH08cWdi7QnTMMwGELFoPq_r7_cyaGU8fx2InJG2R-NfkqF1IRt7rKOfA8M1GhUy", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "150", + "title": "Architecture: The data layer - MAD Skills", + "content": "Jose goes over the data layer and its two components: repositories and data sources. You will dive deeper into what the roles of these two are and understand their differences. You will also learn about data immutability, error handling, threading testing and more!", + "url": "https://www.youtube.com/watch?v=r5AseKQh2ZE", + "headerImageUrl": "https://i3.ytimg.com/vi/r5AseKQh2ZE/maxresdefault.jpg", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "151", + "title": "Architecture: The UI layer - MAD Skills", + "content": "TJ covers the UI layer in this episode of MAD skills using the JetNews sample app as a case study You will learn UI Layer pipeline, UI state exposure, UI state consumption and more!", + "url": "https://www.youtube.com/watch?v=p9VR8KbmzEE", + "headerImageUrl": "https://i3.ytimg.com/vi/p9VR8KbmzEE/maxresdefault.jpg", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [ + "38" + ] + }, + { + "id": "152", + "title": "Account Linking - Integrate with Android TV & Google TV", + "content": "Miguel and Adekunle discuss account linking. ​​Google Account Linking lets you safely link a user’s Google Account with their account on your platform, granting Google applications and devices access to your services and is needed for many integrations on Android TV & Google TV. They discuss the basics of OAuth like implementing your authorization, token exchange, and revocation endpoints. You will also learn the difference between the Web OAuth, Streamlined, and App Flip linking flows.", + "url": "https://www.youtube.com/watch?v=-Fa99hpUsdk", + "headerImageUrl": "https://i3.ytimg.com/vi/-Fa99hpUsdk/maxresdefault.jpg", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "16" + ], + "authors": [] + }, + { + "id": "153", + "title": "Modern media playback on Android - Integrate with Android TV & Google TV", + "content": "Paul explores best practices for integrating and validating media sessions, the unified way for Android apps to interact with media content. MediaSessions allows different devices like smart speakers, watches, peripherals and accessories to surface and interact with playback and content metadata.", + "url": "https://www.youtube.com/watch?v=OYy41ceW59s", + "headerImageUrl": "https://i3.ytimg.com/vi/OYy41ceW59s/maxresdefault.jpg", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "16" + ], + "authors": [ + "27" + ] + }, + { + "id": "154", + "title": "FHIR SDK for Android Developers 🏥", + "content": "Community health workers in low-and-middle-income countries use mobile devices as critical tools for doing community outreach and providing crucial health services. Unfortunately, the lack of data interoperability means that patient records are fragmented and caregivers may only receive incomplete information. Last year, Google introduced a collaboration with the World Health Organization to build an open source software developer kit designed to help developers build mobile solutions using the Fast Healthcare Interoperability Resources (FHIR) global standard for healthcare data. Read the article to learn more about how this SDK can help you create apps to aid community health workers in low-and-middle-income countries.", + "url": "https://medium.com/androiddevelopers/our-fhir-sdk-for-android-developers-9f8455e0b42f", + "headerImageUrl": "https://miro.medium.com/max/1400/1*azSHuKma0fz1RxcPcqiusg.png", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "155", + "title": "Helping Users Discover Quality Apps on Large Screens 🔎", + "content": "Adoption of large screens is growing rapidly and now there are over 250M active Android tablets, foldables, and ChromeOS devices. To help people get the most from their devices, we’re making three big changes in Google Play to enable users to discover and engage with high quality apps and games: ranking and promotability changes, alerts for low quality apps, and device-specific ratings and reviews. Read all about it in the post!", + "url": "https://android-developers.googleblog.com/2022/03/helping-users-discover-quality-apps-on.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEiWXMaly6_CP_gSHmxE92yVBXUQ1X5EcTA6pdKwo_NsAtO1Ouv_RhHxG1HqtbStufdnylV51VbHI0FmmPV8lvmLAOqNzhcD2znU3vWVajQXfOlFw_kP-01jxSvrzVIXZG7SCQMiw58yUaWgmqzO-dsaso5DOeYVKnwQm3Vdu9lFmogfCkQT5u7H0sVt", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "2" + ], + "authors": [ + "61" + ] + }, + { + "id": "156", + "title": "Access Android vitals data through the new Play Developer Reporting API", + "content": "In this article Lauren talks about Android vitals which are a great way to track how your app is performing in Google Play Console. Now there are new use cases for Android vitals which include building internal dashboards, joining with other datasets for deeper analysis and automation troubleshooting and releases. Learn more about the API and how to use it in this post!", + "url": "https://android-developers.googleblog.com/2022/03/play-developer-reporting-API.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhnvMF36lJv9wDDHWLQb7AfVBajueyEuocw_9ne1jgKJAO5dgXWcAyrKa92f4miTcFoSH5usz_Jha2C1gJwJNSr6et8sZGSCnkZTgtdaKPemEfwaHJDjiurWaPtqFF3qI0aX7aRB7B9WUW1VXT_Wgkyyq8nYK7RrOy9zW4a7gROkzd3H5m9T36Bc7Ww", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "12" + ], + "authors": [] + }, + { + "id": "157", + "title": "Using performance class to optimize your user experience", + "content": "The Jetpack Core Performance library in alpha has launched! This library enables you to easily identify what a device is capable of, and tailor your user experience accordingly. As developer, this means you can reliably group devices with the same level of performance and tailor your app’s behavior to those different groups. This enables you to deliver an optimal experience to users with both more and less capable devices.", + "url": "https://android-developers.googleblog.com/2022/03/using-performance-class-to-optimize.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYa28aPEBLCSzLkir02bVSWusH5BBIiAcq_CzCx5DD3iQu5WyDLC1sZe1y5OInomon5KHJKemMCa5q6XAtfkMhljMoePuebLGDz6yRDU3cjkwMo7sV5WKe20KNzWhP1ktdOn7OxOxeiXqzeDrPwLcpoVaStm8840phqHOqDptiQ0twMsGTD2u3o0Xf/s1600/Android-using-performance-class-to-optimize-user-experience-header%20%281%29.png", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "7" + ], + "authors": [ + "42" + ] + }, + { + "id": "158", + "title": "AndroidX releases 🚀", + "content": "We have a few libraries in alpha-01 including Activity Version 1.6.0-alpha01, CustomView-Poolingcontainer Version 1.0.0-alpha01, and Junit-Gtest Version 1.0.0-alpha01.\n\nCar App Version 1.2.0-rc01 and Mediarouter Version 1.3.0-rc01 are also in rc-01.", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEi6y_NUo9gnpHYdRc7lwnbVnraBtUSIZTnIoAcHXkbq8Z0AFHBUHDI_s7HwwP2h2nTwo571RnRuXN-sUWdgJ7qkNb2MSslYiXWP3tteXooTdwAS_YzbZMTux25eLZk0kgdLtXmWTRLdolft-ZcsgGjCyJnH-CjzHsZXGy8vNVxB5oFZkBExOpBwvoDL=w1200-h630-p-k-no-nu", + "publishDate": "2022-03-29T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "159", + "title": "Discontinuing Kotlin synthetics for views", + "content": "Synthetic properties to access views were created as a way to eliminate the common boilerplate of findViewById calls. These synthetics are provided by JetBrains in the Kotlin Android Extensions Gradle plugin (not to be confused with Android KTX).", + "url": "https://android-developers.googleblog.com/2022/02/discontinuing-kotlin-synthetics-for-views.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhAba1fyHq6ddgUfT09YxU3XkAhnolKyLnXE2GmcJABVT-y8PWLKUiC7LmesY7Txak65fc6nW8T7yar9_4Uz4ezcBA_MDZ-yqBR2cj4ipSN-5l_E57exa3m9qt2MHFo_RdLWc_YDX7J7AOMkyzs43ylbGtwl6Z8GSf1zgs71Te36cQ-9Z_qgMgFroLq=w1200-h630-p-k-no-nu", + "publishDate": "2022-02-18T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2", + "10" + ], + "authors": [ + "1" + ] + }, + { + "id": "160", + "title": "Things to know from the 2022 Google for Games Developer Summit", + "content": "This week marked the 2022 Google for Games Developer Summit, Google’s biggest event of the year centered around game development. The Android team shared information around the next generation of services, tools and features to help you develop and deliver high quality games. ", + "url": "https://android-developers.googleblog.com/2022/03/GGDS-recap-blog.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhW4RL-UKUurgM2bVJRepqjKehVETjf9bqdXllyspPaWTTt8s86MGvfxlxLkDyJAnnkGr7vDpDTPx6bQbgkThYXMSaW1GQvXw9V57xybA8Y89vIE45JDElGxSNFHwOAndATPYrGmc200fkyBTRSNi7w53hTbS1ao-TSoEBFs8jvTgz6ud5Tcb1qitkt", + "publishDate": "2022-03-15T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "17" + ], + "authors": [ + "2" + ] + }, + { + "id": "161", + "title": "MAD Skills: DataStore and Introduction to Architecture💡", + "content": "Now that our MAD Skills series on Jetpack DataStore is complete, let’s do a quick wrap up of all the things we’ve covered in each episode.", + "url": "https://android-developers.googleblog.com/2022/03/jetpack-datastore-wrap-up.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgo2-I1LhMjWd1zzpIQXzjMCPoZeUZc35n43UosKDuLMyP7rIDe8cGfs23tmkSAed6Wxw9EoNTIpvvWCljermK_lCu0etlrCnONx3WeXMCGe-s8I45hYhuVo6w_Q2UTNATMTA70t2o9MS5p2pBdPFz5Ye4b2ajOJjNlW9rELtqWcEW4O1Rkzy4lfqRO", + "publishDate": "2022-03-14T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "162", + "title": "Play Time with Jetpack Compose", + "content": "Learn about Google Play Store’s strategy for adopting Jetpack Compose, how they overcame specific performance challenges, and improved developer productivity and happiness.", + "url": "https://android-developers.googleblog.com/2022/03/play-time-with-jetpack-compose.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-03-10T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3", + "12" + ], + "authors": [ + "4", + "5" + ] + }, + { + "id": "163", + "title": "App Excellence Summit 2022 ⭐", + "content": "Did you know that 54% of users who left a 1-star review in the Play Store mentioned app stability and bugs? *\n\nTo help product managers and business decision makers understand how high quality app experiences drive business growth and what tools they can use to make sound business and technical decisions, we are hosting our first Android App Excellence Summit in just a few weeks!", + "url": "https://android-developers.googleblog.com/2022/03/app-excellence-summit-2022.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEh4Vck7mqle-tLweEgrIc1WT0ycY6O6zBxv9mC1Dt1xCnJN5COTGFxDSQlIM1rbbMKIMZHPtjzXgENMGk80oxb5Mn8kTn6qO7kgUXC_N5YSB0dWxcXvQOIPHEEgNJze9g8eZrY1xgA9_oBls71NLItDJKTYeoJGEXxIBiAE_c6SkXv2jSELZEoFfqVq", + "publishDate": "2022-03-10T00:00:00.000Z", + "type": "Event 📆", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "164", + "title": "#TheAndroidShow: Tablets, Jetpack Compose, and Android 13 📹", + "content": "Last week, Florina and Huyen hosted #TheAndroidShow, where we went Behind the scenes with animations & Jetpack Compose, asked whether now is the moment to think tablet first, and covered Android 13 along with other key themes for Android this year.", + "url": "https://www.youtube.com/watch?v=WL9h46CymlU", + "headerImageUrl": "https://i.ytimg.com/vi/WL9h46CymlU/maxresdefault.jpg", + "publishDate": "2022-03-09T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "3", + "2", + "13", + "1" + ], + "authors": [ + "6" + ] + }, + { + "id": "165", + "title": "Freeing up 60% of storage for apps 💾", + "content": "App archiving will allow users to reclaim ~60% of app storage temporarily by removing parts of the app rather than uninstalling the app completely.", + "url": "https://android-developers.googleblog.com/2022/03/freeing-up-60-of-storage-for-apps.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-03-08T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "7", + "8" + ] + }, + { + "id": "166", + "title": "Demystifying Jetpack Glance for app widgets", + "content": "We recently announced the first Alpha version of Glance, initially with support for AppWidgets and now for Tiles for Wear OS. This new framework is built on top of the Jetpack Compose runtime and designed to make it faster and easier to build “glanceables” such as app widgets without having to handle a lot of boilerplate code or lifecycle events to connect different components.", + "url": "https://medium.com/androiddevelopers/demystifying-jetpack-glance-for-app-widgets-8fbc7041955c", + "headerImageUrl": "https://miro.medium.com/max/1400/1*mlswR3fyxaIG-C1OUifYVw.jpeg", + "publishDate": "2022-03-07T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2", + "19" + ], + "authors": [ + "9" + ] + }, + { + "id": "167", + "title": "Keeping Google Play safe with our key 2022 initiatives 🔒", + "content": "We shared information about what’s ahead in 2022 for Google Play’s privacy and safety initiatives to give you time to prepare.", + "url": "https://android-developers.googleblog.com/2022/03/privacy-and-security-direction.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhh3FMLL-etD7iDzhSI6CoYbuwgB9ZADjXa6A9C4aM3W-eRqj1FGfP8dyMY4i5RlMtQJD8Sx1y1NHFuaCae10iZkAs_cETaCAllzCDU075awpkAc1pkhld7uxwjTmwNdihGhB-FtySiSsf9aknd1ZULz0zkRtybX4gRUp8JCbPh2n3pPEhjK0mTjNWS", + "publishDate": "2022-03-03T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11" + ], + "authors": [ + "10" + ] + }, + { + "id": "168", + "title": "Games-Activity Version 1.1.0", + "content": "adds WindowInsets listening/querying for notch and IME response along with key and motion event filters.", + "url": "https://developer.android.com/jetpack/androidx/releases/games#1.1.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-02-23T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "17" + ], + "authors": [] + }, + { + "id": "169", + "title": "Room Version 2.5.0-alpha01", + "content": "Converted room-common, room-migration, and paging related files in room-runtime from Java to Kotlin along with a new API for multi-process lock to protect multi-process 1st time database creation and migrations", + "url": "https://developer.android.com/jetpack/androidx/releases/room#2.5.0-alpha01", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-02-23T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "9" + ], + "authors": [] + }, + { + "id": "170", + "title": "Media Version 1.6.0-alpha 01", + "content": "Adds the extras necessary to setup a signin/settings page using CarAppLibrary.", + "url": "https://developer.android.com/jetpack/androidx/releases/media#media-1.6.0-alpha01", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjo_CL5arSn2zb_YKP8hKjaMG3nqXXPQ_zn05X9FQ0XLdE2Ii6WeGG0eD_miObCRv2iz3hJ2T0lIIn3iDFyT3yN8B7NFET_fE5nhcw6MHQmOKK4G4R5XgXTkEIyqY4kjz2F5hpPscvQgsz0aRkVqSLynp-6x9HqkoldNYwDSp7kbttmh2JCW1cwUXhG=w1200-h630-p-k-no-nu", + "publishDate": "2022-02-23T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "18" + ], + "authors": [] + }, + { + "id": "171", + "title": "AppCompat-Resources Version 1.6.0-alpha01", + "content": "Adds support for customizing locales, providing backwards compatibility for the Android 13 per-language preferences API", + "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.6.0-alpha01", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhxsx2mynFgTjtKCAxJiWvIYuJF1sNFfRCPnEbBWSmQLATP_Z6Bmz81sr9WmS2CWVUqIzW4uYyRyW2wQSLR73i9WXLUzGc-LbMS-QEcQQZI5qoymfRf3pyrMnOeGuFAKsfLaAEtquvNyqA2KaO28BnF3plt0jr6kVYIyl0tkpWhxHa47CPuNvhEehQ1=w1200-h630-p-k-no-nu", + "publishDate": "2022-02-23T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "172", + "title": "Recording Video with CameraX VideoCapture API", + "content": "A picture is worth a thousand words, and CameraX ImageCapture has already made it much easier to tell your story through still images on Android. Now with the new VideoCapture API, CameraX can help you create thousands of continuous pictures to tell an even better and more engaging story!", + "url": "https://medium.com/androiddevelopers/recording-video-with-camerax-videocapture-api-a36cfd8a48c8", + "headerImageUrl": "https://miro.medium.com/max/1400/1*GZmhCFMCrG4L_mOtwSb0zA.png", + "publishDate": "2022-02-23T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [ + "11" + ] + }, + { + "id": "173", + "title": "Unbundling the stable WindowManager", + "content": "The 1.0.0 stable release of Jetpack WindowManager, the foundation for great experiences on all types of large screen devices.", + "url": "https://medium.com/androiddevelopers/unbundling-the-stable-windowmanager-a5471ff2907", + "headerImageUrl": "https://miro.medium.com/max/1400/0*dIXjHF8_-47CvYTb.png", + "publishDate": "2022-02-17T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "12" + ] + }, + { + "id": "174", + "title": "Jetpack Compose 1.1 is now stable!", + "content": "Last week we released version 1.1 of Jetpack Compose and Florina Muntenescu wrote an article giving us all the information! This release contains new features like improved focus handling, touch target sizing, ImageVector caching and support for Android 12 stretch overscroll. This also means that previously experimental APIs are now stable. Check out our recently updated samples, codelabs, and the Accompanist library!", + "url": "https://android-developers.googleblog.com/2022/02/jetpack-compose-11-now-stable.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEiEIiQOoFF-f-sDcbYOMINZw5-2R9aQjrREfiXFMGsRYODVfaz1sgdCS2C3UjgeJjCII5oyE4y97kbvQIUsl9wIx8RqTSZPSdIoCywW89lvmAJ5a15bkFOwoR9UacCEUb4CjOMy0omVMfC0CQhUfz9VMTZR4iyjDGagEZfNuMid8BT0lvarns9Tp6PC", + "publishDate": "2022-02-09T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "2" + ], + "authors": [ + "6" + ] + }, + { + "id": "175", + "title": "MAD Skills: DataStore", + "content": "The DataStore MAD Skills series rolls on! In the sixth episode, Simona Stojanovic covered DataStore: Best Practices part 2 covering DataStore-to-DataStore migration. This is used when you make significant changes to your dataset like renaming your data model values or changing their type. ", + "url": "https://medium.com/androiddevelopers/datastore-and-data-migration-fdca806eb1aa", + "headerImageUrl": "https://miro.medium.com/max/1400/0*8wsdb7Z7QxT1d4lM", + "publishDate": "2022-02-15T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "176", + "title": "DataStore and Testing", + "content": "For the final part of the DataStore series, Simona covered DataStore and testing and teaches you how to fully test your DataStore.", + "url": "https://medium.com/androiddevelopers/datastore-and-testing-edf7ae8df3d8", + "headerImageUrl": "https://miro.medium.com/max/1400/1*5_yt1M6_QEMN0OgGU8VaZw.png", + "publishDate": "2022-02-16T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "177", + "title": "Material You: Coming to more Android Devices near you", + "content": "Material You will soon be available on more Android 12 phones globally including devices by Samsung, Oppo, OnePlus and more! Material You has made the Android experience more fluid and personal than ever. Our OEM partners continue to work with us to ensure that key design APIs work consistently across the Android ecosystem so developers can benefit from a cohesive experience.", + "url": "https://android-developers.googleblog.com/2022/02/material-you-coming-to-more-android.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhDOIPFoqZ8uvg7VmH5EuY3ocfxvKZXawUQ9NczUCEtOdpw3v42vSTrpUSvHjbph5KmTlDH-XtnmGeXmCFTMaHDnRS9ibzLUHBip_XnVHUL7xv-3UrVL6plimErj_oK_KyW5ULpmj6orVTaTq9r56K0V3npQFdIrBPE7_caRWb_QA5E9FljpREWVB7Y", + "publishDate": "2022-02-10T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2", + "13" + ], + "authors": [ + "13" + ] + }, + { + "id": "178", + "title": "The first developer preview of Android 13", + "content": "We’re sharing a first look at the next release of Android, with the Android 13 Developer Preview 1. With Android 13 we’re continuing some important themes: privacy and security, as well as developer productivity. We’ll also build on some of the newer updates we made in 12L to help you take advantage of the 250+ million large screen Android devices currently running.", + "url": "https://android-developers.googleblog.com/2022/02/first-preview-android-13.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEjnrShXcFkBmErmhgdmx82vJbaKBIxU6p2Yz2Vr1V7AlFkD2tGwRmx_a7tWcInPmiUh8VpPmEEqXut-EjP23lFYG9wiMO4sKBDEwbZ3MNppZOy_HW54OXO4SkdQVH08cWdi7QnTMMwGELFoPq_r7_cyaGU8fx2InJG2R-NfkqF1IRt7rKOfA8M1GhUy", + "publishDate": "2022-02-10T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "179", + "title": "AndroidX releases 🚀", + "content": "Since Compose just went stable, the Animation, Compiler, Foundation, Material, Runtime and UI Versions also went stable! Games-Text-Input and ProfileInstaller also went stable! \n\nThere are a bunch of new APIs in alpha including new Testing APIs (Test Runner, Test Monitor, Test Services and Test Orchestrator), Metrics Version and Startup Version.", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#february_9_2022", + "headerImageUrl": "https://miro.medium.com/max/1400/0*bux1xKYcB3A9pBFx", + "publishDate": "2022-02-09T00:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "3", + "8" + ], + "authors": [ + "15" + ] + }, + { + "id": "180", + "title": "DataStore best practices part 1", + "content": "learn about performing synchronous work and how to make it work with Kotlin data class serialization and Hilt.", + "url": "https://www.youtube.com/watch?v=S10ci36lBJ4", + "headerImageUrl": "https://i.ytimg.com/vi/S10ci36lBJ4/maxresdefault.jpg", + "publishDate": "2022-02-07T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "181", + "title": "All about Proto DataStore", + "content": "In this post, we will learn about Proto DataStore, one of two DataStore implementations. We will discuss how to create it, read and write data and how to handle exceptions, to better understand the scenarios that make Proto a great choice.", + "url": "https://medium.com/androiddevelopers/all-about-proto-datastore-1b1af6cd2879", + "headerImageUrl": "https://miro.medium.com/max/1400/1*UtNu7pmbt3WEA213SW9p9Q.png", + "publishDate": "2022-01-31T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "182", + "title": "Glance: Tiles for Wear OS made simple ⌚️", + "content": "Last year we announced the Wear Tiles API. To complement that Java API, we are excited to announce that support for Wear OS Tiles has been added to Glance, a new framework built on top of Jetpack Compose designed to make it easier to build for surfaces outside your Android app. As this library is in alpha, we’d love to get your feedback.", + "url": "https://android-developers.googleblog.com/2022/01/announcing-glance-tiles-for-wear-os.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-01-26T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19" + ], + "authors": [ + "16" + ] + }, + { + "id": "183", + "title": "Android Studio Bumblebee 🐝 stable", + "content": "Android Studio Bumblebee (2021.1.1) is now stable. We’ve since patched it to address some launch issues — so make sure to upgrade! It improves functionality across the typical developer workflow: Build and Deploy, Profiling and Inspection, and Design.", + "url": "https://android-developers.googleblog.com/2022/01/android-studio-bumblebee-202111-stable.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhQ7R2ySipHb8y5jNJeiIj3pE8dZfWAV7EF0wQZ4rQ65lB4MsZroAT4R_7rSfznMZ30xBMLx9_dwnt05V6I0Du0EfI7mvLicK6LwdkuZsF_Gc3sPqrZGxkojTJpHCXFI3Kvr3bLyoSjElldtt1NUpGSBzHgG3O1pvS9BR02L9R2_FYTUgPLfUoNLWYQ", + "publishDate": "2022-01-25T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5" + ], + "authors": [ + "17" + ] + }, + { + "id": "184", + "title": "All about Preferences DataStore", + "content": "In this post, we will take a look at Preferences DataStore, one of two DataStore implementations. We will go over how to create it, read and write data, and how to handle exceptions, all of which should, hopefully, provide you with enough information to decide if it’s the right choice for your app.", + "url": "https://medium.com/androiddevelopers/all-about-preferences-datastore-cc7995679334", + "headerImageUrl": "https://miro.medium.com/max/1400/1*UtNu7pmbt3WEA213SW9p9Q.png", + "publishDate": "2022-01-24T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "185", + "title": "Building apps for Android Automotive OS 🚘", + "content": "The Car App Library version 1.2 is already in beta, enabling app developers to start building their navigation, parking, and charging apps for Android Automotive OS. Now, developers can begin building and testing apps for these categories using the Automotive OS emulator across both Android Automotive OS and Android Auto.", + "url": "https://android-developers.googleblog.com/2022/01/building-apps-for-android-automotive-os.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEicCVDoRaflBAdKr9_Zh2cAGUB8pphAj9m0w1iN7VLizNZ6L2iNGNSnt7tvD6MP72BW8eqobZpU751t32aF47bpNDv2walZ6zzsXxyuAjCyhBl0b4o06X1j3bPi0AAU0EedqYjp5FSXMQHHzvxBedjsST3MIIFvalX3tZpgiFZgEdqbB2f_H741Irrb=w1200-h630-p-k-no-nu", + "publishDate": "2022-01-27T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "15" + ], + "authors": [ + "18" + ] + }, + { + "id": "186", + "title": "Navigation 2.4 is stable ", + "content": "It’s been rewritten in Kotlin, with two pane integration, Navigation routes + Kotlin DSL improvements, Navigation Compose’s first stable release, and multiple back stack support.", + "url": "https://developer.android.com/jetpack/androidx/releases/navigation#2.4.0", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEhsJgTKhUlIb-1X_G1rWQLCxpd0KMmGTUgqUfSNr4__CsxxjiOdJgJHCtgO9dG8mZdwzAHat9HyIcMsvA-fS0o6T0-_ut_Ej74hKfn09AJUPNc3YscwfGG6hqFS-W_oTczgtd1aGNzpCdDDo4b4lrUM3n8OsFKjvslqE6pHRY3w0aZSTHsaYytSnQSA=w1200-h630-p-k-no-nu", + "publishDate": "2022-01-26T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "187", + "title": "Google Maps with Jetpack Compose", + "content": "A project which contains Jetpack Compose components for the Google Maps SDK for Android.\n\n", + "url": "https://github.com/googlemaps/android-maps-compose", + "headerImageUrl": "https://opengraph.githubassets.com/0952eadfbb07f5ce9f631fd0312d87e8f0e2557df01bac3b587311ca864cf836/googlemaps/android-maps-compose", + "publishDate": "2022-02-11T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "3" + ], + "authors": [] + }, + { + "id": "188", + "title": "Improving App Performance with Baseline Profiles", + "content": "In this blog post we’ll discuss Baseline Profiles and how they improve app and library performance, including startup time by up to 40%. While this blogpost focuses on startup, baseline profiles also significantly improve jank as well.", + "url": "https://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-01-28T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [ + "19", + "20", + "21" + ] + }, + { + "id": "189", + "title": "Smule Adopts Google’s Oboe to Improve Recording Quality & Completion Rates", + "content": "As the most downloaded singing app of all time, Smule Inc. has been investing on Android to improve the overall audio quality and, more specifically, to reduce latency, i.e. allowing singers to hear their voices in the headset as they perform. The teams specialized in Audio and Video allocated a significant part of 2021 into making the necessary changes to convert the Smule application used by over ten million Android users from using the OpenSL audio API to the Oboe audio library, enabling roughly a 10%+ increase in recording completion rate.", + "url": "https://android-developers.googleblog.com/2022/02/smule-adopts-googles-oboe-to-improve.html", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-02-02T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [] + }, + { + "id": "190", + "title": "Guide to background work", + "content": "Do you use coroutines or WorkManager for background work? The team updated the guide to background work to help you choose which library is best for your use case. It depends on whether or not the work is persistent, and if it needs to run immediately, it’s long running, or deferrable.", + "url": "https://developer.android.com/guide/background", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-02-11T00:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "4" + ], + "authors": [] + }, + { + "id": "191", + "title": "Accessibility best practices", + "content": "If you work on Android TV, you should be aware of the accessibility best practices that the team created. It provides recommendations for both native and non-native apps. Get to know why accessibility is important for your TV app, how to evaluate your apps when TalkBack is used, how to adopt system caption settings, and more!", + "url": "https://developer.android.com/training/tv/accessibility", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2022-02-11T00:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "16", + "14" + ], + "authors": [] + }, + { + "id": "192", + "title": "TalkBack - the Google screen reader", + "content": "Next up in the Accessibility series is TalkBack, the Google screen reader! In this video, learn what TalkBack is, how to set it up, how to navigate through your app with it, and how you can use it to improve the Accessibility of your app.", + "url": "https://www.youtube.com/watch?v=_1yRVwhEv5I", + "headerImageUrl": "https://i.ytimg.com/vi/_1yRVwhEv5I/maxresdefault.jpg", + "publishDate": "2022-01-21T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [] + }, + { + "id": "193", + "title": "ADB Podcast 182: Large screens are a big deal", + "content": "Clara, Florina and Daniel join your usual hosts to talk about large screens, what they are and what they mean for app developers. You will also learn about the resources at your disposal to build high quality experiences on large screen devices: from samples and guidance to canonical layouts and new APIs such as window size classes. Disclaimer: Florina is very excited about this, don’t miss the epic Large screens! Large screens! Large screens! intro!", + "url": "https://adbackstage.libsyn.com/episode-182-large-screens-are-a-big-deal", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2022-02-01T00:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "194", + "title": "Jetpack Alpha for Glance Widgets 🔍", + "content": "We made the first release of Jetpack Glance available, a new framework designed to make it faster and easier to build app widgets for the home screen and other surfaces. Glance offers similar modern, declarative Kotlin APIs that you are used to with Jetpack Compose, helping you build beautiful, responsive app widgets with way less code. Glance provides a base-set of its own Composables to help build “glanceable” experiences — starting today with app widget components but with more coming. Using the Jetpack Compose runtime, Glance translates these Composables into RemoteViews that can be displayed in an app widget", + "url": "https://android-developers.googleblog.com/2021/12/announcing-jetpack-glance-alpha-for-app.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgol-A5cMCZY79MH5v0axcekWIVJ--ymPUe0U5Q4BLsC0BA1LTbWIlZ76XWi2cHjxHVu-kbpv0o2QJWBjNAda_93Ah7AW_PcAgz9o082cd6zyTJZAM8HjQnrZ69A6CaKQaCFuf2LLi4p6xRvS_WUn9tVA2K2wmV3_qB6JDKnFNhO3Guvn5tPc_SuoaY", + "publishDate": "2021-12-15T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8" + ], + "authors": [ + "9" + ] + }, + { + "id": "195", + "title": "Jetpack Watch Face Library ⌚", + "content": "We launched the Jetpack Watch Face library written from the ground up in Kotlin, including all functionality from the Wearable Support Library along with many new features such as: Watch face styling which persists across both the watch and phone (with no need for your own database or companion app); Support for a WYSIWYG watch face configuration UI on the phone; Smaller, separate libraries (that only include what you need); Battery improvements through promoting good battery usage patterns out of the box, such as automatically reducing the interactive frame rate when the battery is low; New screenshot APIs so users can see previews of their watch face changes in real time on both the watch and phone.\n\nIf you are still using the Wearable Support Library, we strongly encourage migrating to the new Jetpack libraries to take advantage of the new APIs and upcoming features and bug fixes.", + "url": "https://android-developers.googleblog.com/2021/12/develop-watch-faces-with-stable-jetpack.html", + "headerImageUrl": "https://1.bp.blogspot.com/-P4S1eEhqouE/YaaFy_bGD1I/AAAAAAAARNA/-w5O05Mppo8pe0hoeMC1yDNRWiX_mnTOgCLcBGAsYHQ/s0/image1.png", + "publishDate": "2021-12-01T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "3", + "8" + ], + "authors": [ + "22" + ] + }, + { + "id": "196", + "title": "Rebuilding our Guide to App Architecture 📐", + "content": "We launched a revamped guide to app architecture which includes best practices. As Android apps grow in size, it’s important to design the code with an architecture in place that allows the app to scale, improves quality and robustness, and makes testing easier. The guide contains pages for UI, domain, and data layers including deep dives into more complex topics, such as how to handle UI events. We also have a learning pathway to walk you through it.", + "url": "https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgnJ0CCtKClhEOE_BDOoWiXGr2eA6LWjn-RPvFjFx8Va97f_1_xCmpF3uI_bUILoQPqJUDlXUbIRVPjvi3oCiFtRVZlcAAkHBa1cJlufG5OvmeovQeiHgH9bLhxREufi-fw7FnxIcmxGmzWuW0DmYUZolsM6rywTSZIm3KtI6yx9jSIeRpuYzRZubke", + "publishDate": "2021-12-14T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "23" + ] + }, + { + "id": "197", + "title": "Google Play Games on PC Beta 🎮", + "content": "We announced that we’re opening sign-ups for Google Play Games on PC as a beta in Korea, Taiwan, and Hong Kong, allowing users participating in the beta to play a catalog of Google Play games on their PC via a standalone application built by Google. The developer site has a form to express interest, along with information about bringing your Android game to PCs. It involves many of the same updates that you do to optimize your game for Chrome OS devices, such as support for Mouse and Keyboard controls.", + "url": "https://developers.googleblog.com/2022/01/googleplaygames.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgsNv-PVLNLlX2SYd2p5DwTN2Jxwb54Rc7Ekbm0LgcFuwHBrF_5Y-DiUblL9oTjmeJ1Y44nPRMMkH5K-xlC0OApgUGxqBpUcfuV1LYPVvKsI67BKTpc_gNhaHsNda6Q1Uk1UvTznmMydqNHtXSqTgSJbjpQCoTGZM_ZLXlkGwMoBFfnMQkAIdl2zjsC", + "publishDate": "2022-01-19T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "17" + ], + "authors": [ + "24" + ] + }, + { + "id": "198", + "title": "MAD Skills: Gradle 🐘", + "content": "Murat covered building custom plugins in more depth, including the Artifact API in addition to the Variant API covered previously. It demonstrates building a plugin which automatically updates the version code specified in the app manifest with the git version. With the AGP 7.0 release, you can use these APIs to control build inputs, read, modify, or even replace intermediate and final artifacts.", + "url": "https://medium.com/androiddevelopers/gradle-and-agp-build-apis-taking-your-plugin-to-the-next-step-95e7bd1cd4c9", + "headerImageUrl": "https://miro.medium.com/max/1400/0*WkRft2aAKv19MoIm.jpeg", + "publishDate": "2021-12-01T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "199", + "title": "Gradle and AGP Build APIs: Community tip - MAD Skills", + "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Alex Saveau walks you through manipulating Android build artifacts with the Android Gradle Plugin (AGP) and Gradle APIs.", + "url": "https://www.youtube.com/watch?v=8SFfffaB0CU", + "headerImageUrl": "https://i3.ytimg.com/vi/8SFfffaB0CU/maxresdefault.jpg", + "publishDate": "2021-12-15T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "26" + ] + }, + { + "id": "200", + "title": "Gradle and AGP Build APIs: Taking your plugin to the next step - MAD Skills", + "content": "On this episode of Gradle and AGP Build APIs for MAD Skills, Murat will discuss Gradle tasks, providers, properties, and basics of task inputs and outputs. Next, you will be able to take your plugin a step further and learn how to get access to various build artifacts using the new Artifact API. ", + "url": "https://www.youtube.com/watch?v=SB4QlngQQW0", + "headerImageUrl": "https://i3.ytimg.com/vi/SB4QlngQQW0/maxresdefault.jpg", + "publishDate": "2021-11-29T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "201", + "title": "MAD Skills Gradle and AGP build APIs Wrap Up!", + "content": "This wrap-up post summarizes the whole MAD Skills Gradle series", + "url": "https://android-developers.googleblog.com/2021/12/mad-skills-gradle-and-agp-build-apis.html", + "headerImageUrl": "https://blogger.googleusercontent.com/img/a/AVvXsEgo1Fw61B9qtQESKdVJzcNXOG0RzhA2k85zkDMDNidBiQY7B6uguHXQ9t9IPB9BiHS0WTB1b4fwIgeN5zEIJrmznF9pt5lu9186wvXxJ3IKfLi8Fci8LyMDbQKGYc7nnijJ9_lhrNHtRQamaF2GTSXyJq5_lQk7we3cSfSviOxhgKN9TscMJaGgdMZJ", + "publishDate": "2021-12-16T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "202", + "title": "MAD Skills: DataStore 🗄️", + "content": "Simona began MAD Skills: DataStore. DataStore is a thread-safe, non-blocking library in Android Jetpack that provides a safe and consistent way to store small amounts of data, such as preferences or application state, replacing SharedPreferences. It provides an implementation that stores typed objects backed by protocol buffers (Proto DataStore) and an implementation that stores key-value pairs (Preferences DataStore).", + "url": "https://www.youtube.com/watch?v=9ws-cJzlJkU", + "headerImageUrl": "https://i3.ytimg.com/vi/9ws-cJzlJkU/maxresdefault.jpg", + "publishDate": "2022-01-18T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "9" + ], + "authors": [ + "3" + ] + }, + { + "id": "203", + "title": "AndroidX releases 🚀", + "content": "Since the last Now in Android episode, a lot of libraries were promoted to stable! Compose ConstraintLayout brings support for ConstraintLayout syntax to Compose. We also released CoordinatorLayout 1.2, Car App 1.1.0, Room 2.4.0, Sqlite 2.2.0, Collection 1.2.0, and Wear Watchface 1.0.0.\n\nOur first alpha of Jetpack Compose 1.2 was released, along with alphas for Glance 1.0.0, Core-Ktx 1.8.0, WorkManager 2.8.0, Mediarouter 1.3.0, Emoji2 1.1.0, Annotation 1.4.0, Core-RemoteViews, Core-Peformance, and more.", + "url": "https://developer.android.com/jetpack/androidx/versions/all-channel#december_1_2021", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-12-01T00:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "8" + ], + "authors": [] + }, + { + "id": "204", + "title": "Jetnews for every screen", + "content": "Alex wrote about the recent updates to Jetnews that improves its behavior across big and small mobile devices. It describes our design and development process so that you can learn our philosophy and associated implementation steps for building an application optimized for all screens with Jetpack Compose, including how to build a list/detail layout.", + "url": "https://medium.com/androiddevelopers/jetnews-for-every-screen-4d8e7927752", + "headerImageUrl": "https://miro.medium.com/max/1400/1*678DlYtu4G7wFrq30FQ7Mw.png", + "publishDate": "2022-01-18T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "22" + ] + }, + { + "id": "205", + "title": "Simplifying drag and drop", + "content": "Paul wrote about drag & drop, and how the Android Jetpack DragAndDrop library alpha makes it easier to handle data dropped into your app.", + "url": "https://medium.com/androiddevelopers/simplifying-drag-and-drop-3713d6ef526e", + "headerImageUrl": "https://miro.medium.com/max/1400/1*pUe4RBLe7FVlISDtAqeQ4Q.png", + "publishDate": "2021-12-15T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8" + ], + "authors": [ + "27" + ] + }, + { + "id": "206", + "title": "Accessibility series 🌐: Handling content that times out - Accessibility on Android", + "content": "The accessibility series continues on, beginning with an episode on how to properly implement UI elements that disappear after a set amount of time.", + "url": "https://www.youtube.com/watch?v=X97P6Y8WHl0", + "headerImageUrl": "https://i3.ytimg.com/vi/X97P6Y8WHl0/maxresdefault.jpg", + "publishDate": "2021-12-03T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "28" + ] + }, + { + "id": "207", + "title": "Accessibility series 🌐: Acessibility Scanner", + "content": "We also cover how Accessibility Scanner can help you improve your app for all users by suggesting improvements in areas of accessibility.", + "url": "https://www.youtube.com/watch?v=i1gMzQv0hWU", + "headerImageUrl": "https://i3.ytimg.com/vi/i1gMzQv0hWU/maxresdefault.jpg", + "publishDate": "2021-12-10T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "28" + ] + }, + { + "id": "208", + "title": "Accessibility series 🌐: Accessibility test framework and Espresso - Accessibility on Android", + "content": "We investigate how Espresso and the Accessibility Test Framework can help you create automated accessibility tests.", + "url": "https://www.youtube.com/watch?v=DLN2s16HwcE", + "headerImageUrl": "https://i3.ytimg.com/vi/DLN2s16HwcE/maxresdefault.jpg", + "publishDate": "2021-12-22T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "28" + ] + }, + { + "id": "209", + "title": "Android TV & Google TV 📺", + "content": "Mayuri covered best practices for the Watch Next API on Android TV & Google TV, which increases engagement with your app by allowing your content to show up in the Watch Next row.", + "url": "https://www.youtube.com/watch?v=QFMIP5GOo70", + "headerImageUrl": "https://i3.ytimg.com/vi/QFMIP5GOo70/maxresdefault.jpg", + "publishDate": "2022-01-14T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "16" + ], + "authors": [ + "29" + ] + }, + { + "id": "210", + "title": "ADB Podcast 179: Flibberty Widget", + "content": "In this episode, Chet and Romain talked with Nicole McWilliams and Petr Čermák from the London engineering office about their work on App Widgets and Digital Wellbeing.", + "url": "https://adbackstage.libsyn.com/flibberty-widget", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/4/0/e/c/40ec1fb11096bffed959afa2a1bf1c87/adb-180-flibberty-widget.png", + "publishDate": "2021-11-30T00:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "13" + ], + "authors": [ + "30", + "31", + "32", + "33" + ] + }, + { + "id": "211", + "title": "ADB Podcast 180: Kotlin Magic Platform", + "content": "In this episode, we chat with Yigit Boyar from the Android Toolkit Team about Kotlin multi platform, while Romain provides light background music on his piano.", + "url": "https://adbackstage.libsyn.com/episode-180-kotlin-magic-platform", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/2/6/2/5/262599d4ce76d20fa04421dee9605cbd/adb-181-kmp.png", + "publishDate": "2021-12-16T00:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "10" + ], + "authors": [ + "30", + "31", + "32", + "34" + ] + }, + { + "id": "212", + "title": "ADB Podcast 181: Architecture → Fewer bugs at the end", + "content": "In this episode, we chat with Yigit Boyar (again!) from the Android Toolkit Team and Manuel Vivo from the Developer Relations team about application architecture. The team has released new architecture guidance, and we talk about that guidance here, as well as how our architecture recommendations apply in the new Jetpack Compose world.", + "url": "https://adbackstage.libsyn.com/episode-181-architecture-fewer-bugs-at-the-end", + "headerImageUrl": "https://ssl-static.libsyn.com/p/assets/8/d/1/3/8d137b65f392a68c27a2322813b393ee/ADB_181_Architecture.png", + "publishDate": "2022-01-11T00:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "4" + ], + "authors": [ + "31", + "31", + "32", + "23" + ] + }, + { + "id": "213", + "title": "Android 12", + "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). We introduced a new design language called Material You. We reduced the CPU time used by core system services, added performance class device capabilities, and added new features to improve performance. Users have more control of their privacy with the Privacy Dashboard and other new security and privacy features. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", + "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", + "headerImageUrl": "https://1.bp.blogspot.com/-mGlzRmn42Rs/YVstltyrboI/AAAAAAAAK3A/44QpoNJDeuoHhlgrRJSbk0L_ZopgFDLFACLcBGAsYHQ/s0/Android%2B12%2Blogo.png", + "publishDate": "2021-10-03T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "214", + "title": "Compose", + "content": "Jetpack Compose, Android’s modern, native UI toolkit became stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system. You can learn more about working with Compose in the Compose learning path and see where we’re going in future Compose releases in the Compose roadmap.", + "url": "https://developer.android.com/jetpack/compose", + "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", + "publishDate": "2021-12-07T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "3" + ], + "authors": [] + }, + { + "id": "215", + "title": "Training", + "content": "This year, the Android Training Team released the final four new units of Android Basics in Kotlin.", + "url": "https://developer.android.com/courses/android-basics-kotlin/course", + "headerImageUrl": "https://developer.android.com/images/hero-assets/android-basics-kotlin.svg", + "publishDate": "2021-12-07T00:00:00.000Z", + "type": "Codelab", + "topics": [ + "10" + ], + "authors": [] + }, + { + "id": "216", + "title": "Introduction to Kotlin and Jetpack ", + "content": "Learn the basics of Jetpack KTX libraries, how to simplify callbacks with coroutines and Flow, and how to use and test Room/WorkManager APIs.", + "url": "https://youtu.be/nw7nnlHDkHw?list=PLWz5rJ2EKKc98e0f5ZbsgB63MdjZTFgsy", + "headerImageUrl": "https://i3.ytimg.com/vi/nw7nnlHDkHw/maxresdefault.jpg", + "publishDate": "2021-12-14T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "10", + "8" + ], + "authors": [ + "6" + ] + }, + { + "id": "217", + "title": "Introduction to Motion Layout", + "content": "Learn how to use MotionLayout and its design tool to create rich, animated experiences.", + "url": "https://www.youtube.com/watch?v=M1jE3W3_NTQ&list=PLWz5rJ2EKKc_PEOEHNBEyy6tPX1EgtUw2", + "headerImageUrl": "https://i3.ytimg.com/vi/M1jE3W3_NTQ/maxresdefault.jpg", + "publishDate": "2022-01-19T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "10", + "2", + "8" + ], + "authors": [ + "35" + ] + }, + { + "id": "218", + "title": "Introduction to WorkManager", + "content": "Learn how to schedule critical background work with WorkManager: from basic usage, threading, custom configuration and more.", + "url": "https://www.youtube.com/watch?v=NtpgWjiXEfg&list=PLWz5rJ2EKKc_J88-h0PhCO_aV0HIAs9Qk", + "headerImageUrl": "https://i3.ytimg.com/vi/NtpgWjiXEfg/maxresdefault.jpg", + "publishDate": "2022-03-01T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8" + ], + "authors": [ + "36" + ] + }, + { + "id": "219", + "title": "Introduction to Navigation", + "content": "Learn the basics of the Navigation component, specific features of the tool and the APIs to create and navigate to destinations.", + "url": "https://www.youtube.com/watch?list=PLWz5rJ2EKKc9VpBMZUS9geQtc5RJ2RsUd&v=fiQiMy0HzsY&feature=emb_title", + "headerImageUrl": "https://i3.ytimg.com/vi/fiQiMy0HzsY/maxresdefault.jpg", + "publishDate": "2022-03-25T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8" + ], + "authors": [ + "31" + ] + }, + { + "id": "220", + "title": "Introduction to Performance", + "content": "Learn about using system tracing and sampling profiling to debug performance issues in apps.", + "url": "https://www.youtube.com/watch?v=_5LgIrd4O5g&list=PLWz5rJ2EKKc-xjSI-rWn9SViXivBhQUnp", + "headerImageUrl": "https://i3.ytimg.com/vi/_5LgIrd4O5g/maxresdefault.jpg", + "publishDate": "2021-07-18T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "7" + ], + "authors": [ + "37" + ] + }, + { + "id": "221", + "title": "Introduction to Hilt", + "content": "Learn how to add and use Hilt for dependency injection in your Android app, best practices for testing with Hilt, and more advanced content.", + "url": "https://www.youtube.com/watch?v=mnMCgjuMJPA&list=PLWz5rJ2EKKc_9Qo-RBRYhVmME1iR4oeTK", + "headerImageUrl": "https://i3.ytimg.com/vi/mnMCgjuMJPA/maxresdefault.jpg", + "publishDate": "2021-08-22T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8" + ], + "authors": [ + "23" + ] + }, + { + "id": "222", + "title": "Paging", + "content": "Learn the basics of paging, from the core types to binding them to your UI elements.", + "url": "https://www.youtube.com/watch?v=Pw-jhS-ucYA&list=PLWz5rJ2EKKc9L-fmWJLhyXrdPi1YKmvqS", + "headerImageUrl": "https://i3.ytimg.com/vi/Pw-jhS-ucYA/maxresdefault.jpg", + "publishDate": "2021-09-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8" + ], + "authors": [ + "38" + ] + }, + { + "id": "223", + "title": "Introduction to Gradle and AGP Build APIs\n", + "content": "Learn how to configure your build, customize the build process to your needs and how to write your own plugins to extend your build even further.", + "url": "https://www.youtube.com/watch?v=mk0XBWenod8&list=PLWz5rJ2EKKc8fyNmwKXYvA2CqxMhXqKXX", + "headerImageUrl": "https://i3.ytimg.com/vi/mk0XBWenod8/maxresdefault.jpg", + "publishDate": "2021-11-15T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5", + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "224", + "title": "Google I/O", + "content": "At I/O we released updates in Jetpack, Compose, Android Studio tooling, Large screens, Wear OS, Testing, and more! Get caught up on all the Android videos from I/O!", + "url": "https://www.youtube.com/watch?v=D_mVOAXcrtc", + "headerImageUrl": "https://i3.ytimg.com/vi/D_mVOAXcrtc/maxresdefault.jpg", + "publishDate": "2021-05-17T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "225", + "title": "Android Dev Summit", + "content": "At Android Dev Summit we released updates on privacy and security, large screens, Android 12, Google Play & Games, Building across screens, Jetpack Compose, Modern Android Development and more. Check out all the videos from ADS!", + "url": "https://www.youtube.com/watch?v=WZgR5Yf1iq8", + "headerImageUrl": "https://i3.ytimg.com/vi/WZgR5Yf1iq8/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "226", + "title": "Conveying state for Accessibility", + "content": "In this episode of the Accessibility series, you can learn more about the StateDescription API, when to use stateDescription and contentDescription, and how to represent error states to the end user.", + "url": "https://youtu.be/JvWM2PjLJls", + "headerImageUrl": "https://i.ytimg.com/vi/JvWM2PjLJls/maxresdefault.jpg", + "publishDate": "2021-11-30T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [ + "39" + ] + }, + { + "id": "227", + "title": "Take your Gradle plugin to the next step", + "content": "This third and last episode of the Gradle MAD Skills series teaches you how to get access to various build artifacts using the new Artifact API.", + "url": "https://youtu.be/SB4QlngQQW0", + "headerImageUrl": "https://i.ytimg.com/vi/SB4QlngQQW0/maxresdefault.jpg", + "publishDate": "2021-11-29T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "228", + "title": "How to write a Gradle plugin", + "content": "In this second episode of the Gradle MAD Skills series, Murat explains how to write your own custom Gradle plugin.", + "url": "https://youtu.be/LPzBVtwGxlo", + "headerImageUrl": "https://i.ytimg.com/vi/LPzBVtwGxlo/maxresdefault.jpg", + "publishDate": "2021-11-22T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "229", + "title": "Convert YUV to RGB for CameraX Image Analysis", + "content": "Learn about a new feature in CameraX to convert YUV, the format that CameraX produces, to RGB used for image analysis capabilities available in TensorFlow Lite, for example. Read the blog post for more information about these formats and how to use the new conversion feature.", + "url": "https://medium.com/androiddevelopers/convert-yuv-to-rgb-for-camerax-imageanalysis-6c627f3a0292", + "headerImageUrl": "https://miro.medium.com/max/1400/1*cuOorbZgMbRvkSSGuDGccw.png", + "publishDate": "2021-11-19T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8", + "18" + ], + "authors": [ + "40" + ] + }, + { + "id": "230", + "title": "AppCompat, Activity, and Fragment to support multiple back stacks", + "content": "The 1.4.0 release of these libraries brings stable support for multiple back stacks.", + "url": "https://developer.android.com/jetpack/androidx/releases/appcompat#1.4.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8", + "2" + ], + "authors": [] + }, + { + "id": "231", + "title": "Emoji2 adds support for modern emojis", + "content": "The 1.0 stable release of Emoji2 allows you to use modern emojis in your app.", + "url": "https://developer.android.com/jetpack/androidx/releases/emoji2#1.0.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8", + "2" + ], + "authors": [] + }, + { + "id": "232", + "title": "Lifecycle introduces lifecycle-aware coroutine APIs", + "content": "The new 2.4 release of Lifecycle introduces repeatOnLifecycle and flowWithLifecycle.", + "url": "https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8", + "4" + ], + "authors": [] + }, + { + "id": "233", + "title": "Paging release brings changes to LoadState", + "content": "The new 3.1 release of Paging changes the behavior of LoadState.", + "url": "https://developer.android.com/jetpack/androidx/releases/paging#3.1.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8", + "2" + ], + "authors": [] + }, + { + "id": "234", + "title": "Wear tiles released as 1.0 stable", + "content": "The library that you use to build custom tiles for Wear OS devices is now stable.", + "url": "https://developer.android.com/jetpack/androidx/releases/wear-tiles#1.0.0", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "8", + "19" + ], + "authors": [] + }, + { + "id": "235", + "title": "About Custom Accessibility Actions", + "content": "The accessibility series continues on with more information on how to create custom accessibility actions to make your apps more accessible. You can provide a custom action to the accessibility services and implement logic related to the action. For more information, check out the following episode!", + "url": "https://youtu.be/wWDYIGk0Kdo", + "headerImageUrl": "https://i.ytimg.com/vi/wWDYIGk0Kdo/maxresdefault.jpg", + "publishDate": "2021-11-17T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [ + "39" + ] + }, + { + "id": "236", + "title": "Improving App Startup: Lessons from the Facebook App", + "content": "Improving app startup time is not a trivial task and requires a deep understanding of things that affect it. This year, the Android team and the Facebook app team have been working together on metrics and sharing approaches to improve app startup. Read more about the findings in this blog post.", + "url": "https://android-developers.googleblog.com/2021/11/improving-app-startup-facebook-app.html", + "headerImageUrl": "https://1.bp.blogspot.com/-5VyrQpFJufM/YaVKxf_DanI/AAAAAAAALS4/ybeza_emDKoKP0gjiNkqfDS_ltwo0075ACLcBGAsYHQ/w1200-h630-p-k-no-nu/AppExcellence_Editorial_LessonsFromFBApp_4209x1253-01%2B%25281%2529%2B%25281%2529.png", + "publishDate": "2021-11-16T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "7" + ], + "authors": [] + }, + { + "id": "237", + "title": "Gradle series kicks off", + "content": "Murat introduces the Gradle series and everything you'll learn in it.", + "url": "https://youtu.be/mk0XBWenod8", + "headerImageUrl": "https://i.ytimg.com/vi/mk0XBWenod8/maxresdefault.jpg", + "publishDate": "2021-11-15T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "238", + "title": "Intro to Gradle and AGP", + "content": "In the first episode of the Gradle MAD Skills series, Murat explains how the Android build system works, and how to configure your build.", + "url": "https://youtu.be/GjPS4xDMmQY", + "headerImageUrl": "https://i.ytimg.com/vi/GjPS4xDMmQY/maxresdefault.jpg", + "publishDate": "2021-11-15T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "5" + ], + "authors": [ + "25" + ] + }, + { + "id": "239", + "title": "ADB Podcast episode 179 Hosts 3, Guests 0", + "content": "Chet, Romain and Tor sit down to chat about the Android Developer Summit, and in particular all the new features arriving in Android Studio, along with a few other topics like Chet’s new jank stats library, the Android 12L release, and more.", + "url": "https://adbackstage.libsyn.com/episode-178-hosts-3-guests-0", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2021-11-15T00:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "5", + "7", + "13" + ], + "authors": [ + "31" + ] + }, + { + "id": "240", + "title": "The problem with emojis and how emoji2 can help out", + "content": "Meghan wrote about the new emoji2 library that just became stable.", + "url": "https://medium.com/androiddevelopers/support-modern-emoji-99f6dea8e57f", + "headerImageUrl": "https://miro.medium.com/max/1400/1*yAOOlpXKKUl5nWWsPkNb7g.png", + "publishDate": "2021-11-12T00:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8", + "2" + ], + "authors": [ + "15" + ] + }, + { + "id": "241", + "title": "Paging Q&A", + "content": "In this live session, TJ and Dustin answered your questions in the usual live Q&A format.", + "url": "https://youtu.be/8i6vrlbIVCc", + "headerImageUrl": "https://i.ytimg.com/vi/8i6vrlbIVCc/maxresdefault.jpg", + "publishDate": "2021-11-11T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "242", + "title": "Thanks for helping us reach 1M YouTube Subscribers", + "content": "Thank you everyone for following the Now in Android series and everything the Android Developers YouTube channel has to offer. During the Android Developer Summit, our YouTube channel reached 1 million subscribers! Here’s a small video to thank you all.", + "url": "https://youtu.be/-fJ6poHQrjM", + "headerImageUrl": "https://i.ytimg.com/vi/-fJ6poHQrjM/maxresdefault.jpg", + "publishDate": "2021-11-09T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "243", + "title": "Community tip on Paging", + "content": "Tips for using the Paging library from the developer community", + "url": "https://youtu.be/r5JgIyS3t3s", + "headerImageUrl": "https://i.ytimg.com/vi/r5JgIyS3t3s/maxresdefault.jpg", + "publishDate": "2021-11-08T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "244", + "title": "Transformations and customisations in the Paging Library", + "content": "A demonstration of different operations that can be performed with Paging. Transformations like inserting separators, when to create a new pager, and customisation options for consuming PagingData.", + "url": "https://youtu.be/ZARz0pjm5YM", + "headerImageUrl": "https://i.ytimg.com/vi/ZARz0pjm5YM/maxresdefault.jpg", + "publishDate": "2021-11-01T00:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [ + "38" + ] + }, + { + "id": "245", + "title": "New Compose for Wear OS codelab", + "content": "In this codelab, you can learn how Wear OS can work with Compose, what Wear OS specific composables are available, and more!", + "url": "https://developer.android.com/codelabs/compose-for-wear-os", + "headerImageUrl": "https://developer.android.com/codelabs/compose-for-wear-os/img/4d28d16f3f514083.png", + "publishDate": "2021-10-27T23:00:00.000Z", + "type": "Codelab", + "topics": [ + "3", + "19" + ], + "authors": [ + "41" + ] + }, + { + "id": "246", + "title": "Building apps which are private by design", + "content": "Sara N-Marandi, product manager, and Yacine Rezgui, developer relations engineer, provided guidelines and best practices on how to build apps that are private by design, covered new privacy features in Android 12 and previewed upcoming Android concepts.", + "url": "https://youtu.be/hBVwr2ErQCw", + "headerImageUrl": "https://i.ytimg.com/vi/hBVwr2ErQCw/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "11" + ], + "authors": [] + }, + { + "id": "247", + "title": "Memory Safety Tools", + "content": "Serban Constantinescu, product manager, talked about the Memory Safety Tools that became available starting in Android 11 and have continued to evolve in Android 12. These tools can help address memory bugs and improve the quality and security of your application.", + "url": "https://youtu.be/JqLcTFpXreg", + "headerImageUrl": "https://i.ytimg.com/vi/JqLcTFpXreg/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "11" + ], + "authors": [] + }, + { + "id": "248", + "title": "Increasing User Transparency with Privacy Dashboard", + "content": "Android is ever evolving in its quest to protect users’ privacy. In Android 12, the platform increases transparency by introducing Privacy Dashboard, which gives users a simple and clear timeline view of the apps that have accessed location, microphone and camera within the past 24 hours. ", + "url": "https://medium.com/androiddevelopers/increasing-user-transparency-with-privacy-dashboard-23064f2d7ff6", + "headerImageUrl": "https://miro.medium.com/max/1400/1*cgaSAY9AvPWlndLimzIIzQ.png", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11" + ], + "authors": [ + "15" + ] + }, + { + "id": "249", + "title": "The most unusual and interesting security issues addressed last year", + "content": "Lilian Young, software engineer, presented a selection of the most unusual, intricate, and interesting security issues addressed in the last year. Developers and researchers are able to contribute to the security of the Android platform by submitting to the Android Vulnerability Rewards Program.", + "url": "https://medium.com/androiddevelopers/now-in-android-50-ads-special-9934422f8dd1", + "headerImageUrl": "https://miro.medium.com/max/1400/0*6h0XYdyki_1jfImJ", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11" + ], + "authors": [ + "43" + ] + }, + { + "id": "250", + "title": "New Data Safety section in the Play Console", + "content": "The new Data safety section will give you a simple way to showcase your app’s overall safety. It gives you a place to give users deeper insight into your app’s privacy and security practices, and explain the data your app may collect and why — all before users install.", + "url": "https://youtu.be/J7TM0Yy0aTQ", + "headerImageUrl": "https://i.ytimg.com/vi/J7TM0Yy0aTQ/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "11", + "12" + ], + "authors": [] + }, + { + "id": "251", + "title": "Building Android UIs for any screen size", + "content": "Clara Bayarri, engineering manager and Daniel Jacobson, product manager, talked about the state of the ecosystem, focusing on new design guidance, APIs, and tools to help you make the most of your UI on different screen sizes.", + "url": "https://youtu.be/ir3LztqbeRI", + "headerImageUrl": "https://i.ytimg.com/vi/ir3LztqbeRI/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "252", + "title": "What's new for large screens & foldables", + "content": "Emilie Roberts, Chrome OS developer advocate and Andrii Kulian, Android software engineer, introduced new features focused specifically on making apps look great on large screens, foldables, and Chrome OS. ", + "url": "https://youtu.be/6-925K3hMHU", + "headerImageUrl": "https://i.ytimg.com/vi/6-925K3hMHU/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "253", + "title": "Enable great input support for all devices", + "content": "Users expect seamless experiences when using keyboards, mice, and stylus. Emilie Roberts taught us how to handle common keyboard and mouse input events and how to get started with more advanced support like keyboard shortcuts, low-latency styluses, MIDI, and more.", + "url": "https://youtu.be/piLEZYTc_4g", + "headerImageUrl": "https://i.ytimg.com/vi/piLEZYTc_4g/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "254", + "title": "Best practices for video apps on foldable devices", + "content": "Francesco Romano, developer advocate, and Will Chan, product manager at Zoom explored new user experiences made possible by the foldable form factor, focusing on video conferencing and media applications. ", + "url": "https://youtu.be/DBAek_P0nEw", + "headerImageUrl": "https://i.ytimg.com/vi/DBAek_P0nEw/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2", + "18" + ], + "authors": [] + }, + { + "id": "255", + "title": "Design beautiful apps on foldables and large screens", + "content": "Liam Spradlin, design advocate, and Jonathan Koren, developer relations engineer, talked about how to design and test Android applications that look and feel great across device types and screen sizes, from tablets to foldables to Chrome OS.", + "url": "https://youtu.be/DJeJIJKOUbI", + "headerImageUrl": "https://i.ytimg.com/vi/DJeJIJKOUbI/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "256", + "title": "12L and new Android APIs and tools for large screens", + "content": "Dave Burke, vice president of engineering, wrote a post covering the developer preview of 12L, an upcoming feature drop that makes Android 12 even better on large screens. ", + "url": "https://android-developers.googleblog.com/2021/10/12L-preview-large-screens.html", + "headerImageUrl": "https://1.bp.blogspot.com/-sjT5kFGiQtg/YXlpg0uByLI/AAAAAAAARJk/XHO_uo5bRJcMeQVm0Fn1wN-qe54FGI7MgCLcBGAsYHQ/w1200-h630-p-k-no-nu/12L-devices-hero.png", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2", + "13" + ], + "authors": [] + }, + { + "id": "257", + "title": "New features in ML Kit: Text Recognition V2 & Pose Detections", + "content": "Zongmin Sun, software engineer, and Valentin Bazarevsky, MediaPipe Engineer, talked about Text Recognition V2 & Pose Detection, recently-released features in ML Kit. ", + "url": "https://youtu.be/9EKQ0UC04S8", + "headerImageUrl": "https://i.ytimg.com/vi/9EKQ0UC04S8/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "8", + "2" + ], + "authors": [] + }, + { + "id": "258", + "title": "How to retain users with Android backup and restore", + "content": "In this talk, Martin Millmore, engineering manager, and Ruslan Tkhakokhov, software engineer, explored the benefits of transferring users’ data to a new device, using Backup and Restore to achieve that in a simple and secure way.", + "url": "https://youtu.be/bg2drEhz1_s", + "headerImageUrl": "https://i.ytimg.com/vi/bg2drEhz1_s/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "259", + "title": "Compatibility changes in Android 12", + "content": "Developer relations engineers Kseniia Shumelchyk and Slava Panasenko talked about new Android 12 features and changes. They shared tools and techniques to ensure that apps are compatible with the next Android release and users can take advantage of new features, along with app developer success stories.", + "url": "https://youtu.be/fCMJmV6nqGo", + "headerImageUrl": "https://i.ytimg.com/vi/fCMJmV6nqGo/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [] + }, + { + "id": "260", + "title": "Building great experiences for Novice Internet Users", + "content": "Learn the principles to help craft great experiences for the novice Internet user segment from Mrinal Sharma, UX manager, and Amrit Sanjeev, developer relations engineer. They highlight the gap between nascent and tech savvy user segments and suggest strategies in areas to improve the overall user experience. Factors like low functional literacy, being multilingual by default, being less digitally confident, and having no prior internet experience requires that we rethink the way we build apps for these users.", + "url": "https://youtu.be/Sf_TauUY4LE", + "headerImageUrl": "https://i.ytimg.com/vi/Sf_TauUY4LE/maxresdefault.jpg", + "publishDate": "2021-10-26T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "261", + "title": "Android Basics in Kotlin course 🧑‍💻", + "content": "Android Basics in Kotlin teaches people with no programming experience how to build simple Android apps. Since the first learning units were released in 2020, over 100,000 beginners have completed it! Today, we’re excited to share that the final unit has been released, and the full Android Basics in Kotlin course is now available.", + "url": "https://android-developers.googleblog.com/2021/10/announcing-android-basics-in-kotlin.html", + "headerImageUrl": "https://1.bp.blogspot.com/-BmlW7k8RhME/YWRvsOes9aI/AAAAAAAAQ_g/FpFS6_new9Y7vdzP7P4RPs_x4WHVi4yxQCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-announcing-android-basics-in-Kotlin-course-16x9.png", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "10" + ], + "authors": [ + "25" + ] + }, + { + "id": "262", + "title": "Updated Widget docs", + "content": "Widgets can make a huge impact on your user’s home screen! We updated the App Widgets documentation with the recent changes in the latest OS versions. New pages about how to create a simple widget, an advanced widget, and how to provide flexible widget layouts.", + "url": "https://developer.android.com/guide/topics/appwidgets", + "headerImageUrl": "https://www.gstatic.com/devrel-devsite/prod/vab7ee6e3641f10848d404faa598f256587df1a361a1e70cd114230c2961b73d9/android/images/lockup.svg", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "2" + ], + "authors": [] + }, + { + "id": "263", + "title": "Extend AGP by creating your own plugins", + "content": "The Android Gradle Plugin (AGP) contains extension points for plugins to control build inputs and extend its functionality. Starting in version 7.0, AGP has a set of official, stable APIs that you can rely on. We also have a new documentation page that walks you through this and explains how to create your own plugins.", + "url": "https://developer.android.com/studio/build/extend-agp", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "5", + "5" + ], + "authors": [] + }, + { + "id": "264", + "title": "Revamped Compose Basics Codelab", + "content": "If you’re planning to start learning Jetpack Compose, our modern toolkit for building native Android UI, it’s your lucky day! We just revamped the Basics Jetpack Compose codelab to help you learn the core concepts of Compose, and only with this, you’ll see how much it improves building Android UIs.", + "url": "https://developer.android.com/codelabs/jetpack-compose-basics", + "headerImageUrl": "https://i.ytimg.com/vi/k3jvNqj4m08/maxresdefault.jpg", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Codelab", + "topics": [ + "3" + ], + "authors": [] + }, + { + "id": "265", + "title": "Material components in Compose", + "content": "We added a new Material Components and layouts page that goes over the different Material components in Compose such as backdrop, app bars, modal drawers, etc.!", + "url": "https://developer.android.com/jetpack/compose/layouts/material", + "headerImageUrl": "https://developer.android.com/images/jetpack/compose/layouts/material/material_components.png", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "3", + "2" + ], + "authors": [] + }, + { + "id": "266", + "title": "How to implement a custom design system", + "content": "How to implement a custom design system in Compose", + "url": "https://developer.android.com/jetpack/compose/themes/custom", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "3", + "2" + ], + "authors": [] + }, + { + "id": "267", + "title": "The anatomy of a theme", + "content": "Understanding the anatomy of a Compose theme", + "url": "https://developer.android.com/jetpack/compose/themes/anatomy", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-10-20T23:00:00.000Z", + "type": "Docs 📑", + "topics": [ + "3" + ], + "authors": [] + }, + { + "id": "268", + "title": "Paging 📑 Displaying data and its loading state", + "content": "In the third episode of the Paging video series, TJ adds a local cache to pull from and refresh only when necessary, making use of Room . The local cache acts as the single source of truth for paging data.", + "url": "https://www.youtube.com/watch?v=OHH_FPbrjtA", + "headerImageUrl": "https://i.ytimg.com/vi/OHH_FPbrjtA/maxresdefault.jpg", + "publishDate": "2021-10-17T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "9", + "2" + ], + "authors": [ + "38" + ] + }, + { + "id": "269", + "title": "Data safety in the Play Console 🔒", + "content": "Google Play is rolling out the Data safety form in the Google Play Console. With the new Data safety section, developers will now have a transparent way to show users if and how they collect, share, and protect user data, before users install an app.\nRead the blog post to learn more about how to submit your app information in Play Console, how to get prepared, and what your users will see in your app’s store listing starting February.", + "url": "https://android-developers.googleblog.com/2021/10/launching-data-safety-in-play-console.html", + "headerImageUrl": "https://1.bp.blogspot.com/-Zde9ioLE3SY/YWh7qiquXKI/AAAAAAAARCU/m6D-qJJe6QowYPcDWUtb3-YzFGn9xIaUwCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-get-ready-to-sumbit-your-data-safety-secton-social.png", + "publishDate": "2021-10-17T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "11", + "12" + ], + "authors": [ + "10" + ] + }, + { + "id": "270", + "title": "Honor every photo - How cameras capture images", + "content": "Episode 177: Honor every photon. In this episode, Chet, Roman, and Tor have a chat with Bart Wronski from the Google Research team, discussing the camera pipeline that powers the Pixel phones. How cameras capture images, how the algorithms responsible for Pixel’s beautiful images, HDR+ or Night Sight mode works, and more!", + "url": "https://adbackstage.libsyn.com/episode-177-honor-every-photon", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2021-10-17T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "18" + ], + "authors": [ + "31" + ] + }, + { + "id": "271", + "title": "Accessibility series 🌐 - Touch targets", + "content": "The accessibility series continues on with more information on how to follow basic accessibility principles to make sure that your app can be used by as many users as possible.\nIn general, you should ensure that interactive elements have a width and height of at least 48dp! In the touch targets episode, you’ll learn about a few ways in which you can make this happen.", + "url": "https://www.youtube.com/watch?v=Dqqbe8IFBA4", + "headerImageUrl": "https://i.ytimg.com/vi/Dqqbe8IFBA4/maxresdefault.jpg", + "publishDate": "2021-10-16T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "39" + ] + }, + { + "id": "272", + "title": "Using the CameraX Exposure Compensation API", + "content": "This blog post by Wenhung Teng talks about how to use the CameraX Exposure Compensation that makes it much simpler to quickly take images with exceptional quality.", + "url": "https://medium.com/androiddevelopers/using-camerax-exposure-compensation-api-11fd75785bf", + "headerImageUrl": "https://miro.medium.com/max/1400/1*zinEvf1keSZYuZojr31ehQ.png", + "publishDate": "2021-10-12T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [ + "44" + ] + }, + { + "id": "273", + "title": "Compose for Wear OS in Developer preview ⌚", + "content": "We’re bringing the best of Compose to Wear OS as well, with built-in support for Material You to help you create beautiful apps with less code. Read the following article to review the main composables for Wear OS we’ve built and point you towards resources to get started using them.", + "url": "https://android-developers.googleblog.com/2021/10/compose-for-wear-os-now-in-developer.html", + "headerImageUrl": "https://1.bp.blogspot.com/-RkL3Yokn3XE/YWWmbuX8E7I/AAAAAAAAQ_o/CEmNJ5_mfq0kScxkFGoMpf1BlU5-uBHjACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-compose-for-wear-os-now-in-dev-review-header-dark.png", + "publishDate": "2021-10-11T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "19", + "3" + ], + "authors": [ + "41" + ] + }, + { + "id": "274", + "title": "Paging 📑 How to fetch data and bind the PagingData to the UI", + "content": "The series on Paging continues on with more content! In the second episode, TJ shows how to fetch data and bind the PagingData to the UI, including headers and footers.", + "url": "https://www.youtube.com/watch?v=C0H54K63Lww", + "headerImageUrl": "https://i.ytimg.com/vi/C0H54K63Lww/maxresdefault.jpg", + "publishDate": "2021-10-10T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [ + "38" + ] + }, + { + "id": "275", + "title": "Room adds support for Kotlin Symbol Processing", + "content": "Yigit Boyar wrote the story about how Room added support for Kotlin Symbol Processing (KSP). Spoiler: it wasn’t easy, but it was definitely worth it.", + "url": "https://medium.com/androiddevelopers/room-kotlin-symbol-processing-24808528a28e", + "headerImageUrl": "https://miro.medium.com/max/1400/1*yM7Lf4dC_hwse6YmoCO4uQ.png", + "publishDate": "2021-10-09T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9" + ], + "authors": [ + "34" + ] + }, + { + "id": "276", + "title": "Apply special effects to images with the CameraX Extensions API", + "content": "Have you ever wanted to apply special effects such as HDR or Night mode when taking pictures from your app? CameraX is here to help you! In this article by Charcoal Chen, learn how to do that using the new ExtensionsManager available in the camera-extensions Jetpack library. ", + "url": "https://medium.com/androiddevelopers/apply-special-effects-to-images-with-the-camerax-extensions-api-d1a169b803d3", + "headerImageUrl": "https://miro.medium.com/max/1400/1*GZmhCFMCrG4L_mOtwSb0zA.png", + "publishDate": "2021-10-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "18" + ], + "authors": [ + "45" + ] + }, + { + "id": "277", + "title": "Wear OS Jetpack libraries now in stable", + "content": "The Wear OS Jetpack libraries are now in stable.", + "url": "https://android-developers.googleblog.com/2021/09/wear-os-jetpack-libraries-now-in-stable.html", + "headerImageUrl": "https://1.bp.blogspot.com/-9zeEGNCG_As/YUD1UO_3kkI/AAAAAAAAQ8k/tCFBpTCwU4MEQHQNB9XzTOXSf6hd9TkQQCLcBGAsYHQ/w1200-h630-p-k-no-nu/image1.png", + "publishDate": "2021-09-14T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8", + "19" + ], + "authors": [ + "41" + ] + }, + { + "id": "278", + "title": "Android Dev Summit returns on October 27-28, 2021! 📆", + "content": "Join us October 27–28 for Android Dev Summit 2021! The show kicks off at 10 AM PST on October 27 with The Android Show: a technical keynote where you’ll hear all the latest developer news and updates. From there, we have over 30 sessions on a range of technical Android development topics, and we’ll be answering your #AskAndroid questions live.", + "url": "https://developer.android.com/dev-summit", + "headerImageUrl": "https://developer.android.com/dev-summit/images/android-dev-summit-2021.png", + "publishDate": "2021-10-05T23:00:00.000Z", + "type": "Event 📆", + "topics": [ + "1" + ], + "authors": [] + }, + { + "id": "279", + "title": "Android 12 is live in AOSP! 🤖", + "content": "We released Android 12 and pushed it to the Android Open Source Project (AOSP). It will be coming to devices later on this year. Thank you for your feedback during the beta.\nAndroid 12 introduces a new design language called Material You along with redesigned widgets, notification UI updates, stretch overscroll, and app launch splash screens. We reduced the CPU time used by core system services, added performance class device capabilities, made ML accelerator drivers updatable outside of platform releases, and prevented apps from launching foreground services from the background and using notification trampolines to improve performance. The new Privacy Dashboard, approximate location, microphone and camera indicators/toggles, and nearby device permissions give users more insight into and control over privacy. We improved the user experience with a unified API for rich content insertion, compatible media transcoding, easier blurs and effects, AVIF image support, enhanced haptics, new camera effects/capabilities, improved native crash debugging, support for rounded screen corners, Play as you download, and Game Mode APIs.", + "url": "https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html", + "headerImageUrl": "https://1.bp.blogspot.com/-7dVmEfR3mJs/YVst2TdY16I/AAAAAAAAK3I/pLnt0r5S-pIaJwcSNsNBqT8w2Y4Ej0yaQCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android%2B12.jpeg", + "publishDate": "2021-10-03T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "280", + "title": "Improved Google Play Console user management 🧑‍💼", + "content": "The user and permission tools in Play Console have a new, decluttered interface and new team management features, making it easier to make sure every team member has the right set of permissions to fulfill their responsibilities without overexposing unrelated business data.\nWe’ve rewritten permission names and descriptions, clarified differentiation between account and app-level permissions, added new search, filtering, and batch-editing capabilities, and added the ability to export this information to a CSV file. In addition, Play Console users can request access to actions with a justification, and we’ve introduced permission groups to make it easier to assign multiple permissions at once to users that share the same or similar roles.", + "url": "https://android-developers.googleblog.com/2021/09/improved-google-play-console-user.html", + "headerImageUrl": "https://1.bp.blogspot.com/-vw3eaKdwzVU/YUjvyJ6zy2I/AAAAAAAAQ9s/m39byf56P8Icog5e5TgCbu9et0VCZh1iACLcBGAsYHQ/w1200-h630-p-k-no-nu/PlayConsole-revamped-user-management-01.png", + "publishDate": "2021-09-20T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [ + "46" + ] + }, + { + "id": "281", + "title": "Making Permissions auto-reset available to billions more devices 🔐", + "content": "Android 11 introduced permission auto-reset, automatically resetting an app’s runtime permissions when it isn’t used for a few months. In December 2021, we are starting to roll this feature out to devices with Google Play services running Android 6.0 (API level 23) or higher for apps targeting Android 11 (API level 30) or higher. Users can manually enable permission auto-reset for apps targeting API levels 23 to 29.\nSome apps and permissions are automatically exempted from revocation, like active Device Administrator apps used by enterprises, and permissions fixed by enterprise policy. If your app is expected to work primarily in the background without user interaction, you can ask the user to prevent the system from resetting your app’s permissions.", + "url": "https://android-developers.googleblog.com/2021/09/making-permissions-auto-reset-available.html", + "headerImageUrl": "https://1.bp.blogspot.com/-W3UAh-gyf3Y/YUJehjKWQjI/AAAAAAAAQ84/zkURLgqMRa4VZK3Is3ENNYG_OjXJxx2pgCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-making-permissions-auto-reset-social-v2.png", + "publishDate": "2021-09-16T23:00:00.000Z", + "type": "DAC - Android version features", + "topics": [ + "11" + ], + "authors": [ + "47" + ] + }, + { + "id": "282", + "title": "Migrating from Dagger to Hilt", + "content": "While you will eventually want to migrate all your existing Dagger modules over to Hilt’s built in components, you can start by migrating application-wide components to Hilt’s singleton component. This episode explains how.", + "url": "https://www.youtube.com/watch?v=Xt1_3Nq4lD0&t=15s", + "headerImageUrl": "https://i.ytimg.com/vi/Xt1_3Nq4lD0/hqdefault.jpg", + "publishDate": "2021-09-19T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "4" + ], + "authors": [ + "48" + ] + }, + { + "id": "283", + "title": "ADB Podcast Episode 175: Creating delightful user experiences with Lottie animations", + "content": "In this episode, Chet, Romain and Tor have a chat with Gabriel Peal from Tonal, well known for his contributions to the Android community on projects such as Mavericks and Lottie. They talked about Lottie and how it helps designers and developers deliver more delightful user experiences by taking complex animations designed in specialized authoring tools such as After Effects, and rendering them efficiently on mobile devices. They also explored the challenges of designing and implementing a rendering engine such as Lottie.", + "url": "http://adbackstage.libsyn.com/episode-175-lottie", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2021-09-13T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "2" + ], + "authors": [ + "31" + ] + }, + { + "id": "284", + "title": "Hilt extensions", + "content": "This episode explains how to write your own Hilt Extensions. Hilt Extensions allow you to extend Hilt support to new libraries. Extensions can be created for common patterns in projects, to support non-standard member injection, mirroring bindings, and more.", + "url": "https://medium.com/androiddevelopers/hilt-extensions-in-the-mad-skills-series-f2ed6fcba5fe", + "headerImageUrl": "https://miro.medium.com/max/1400/1*a_ZJwMHs17SmEFr3uEbxDg.png", + "publishDate": "2021-09-12T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "49" + ] + }, + { + "id": "285", + "title": "Labeling images for Accessibility", + "content": "This Accessibilities series episode covers labeling images for accessibility, such as content descriptions for ImageViews and ImageButtons.", + "url": "https://youtu.be/O2DeSITnzFk", + "headerImageUrl": "https://i.ytimg.com/vi/O2DeSITnzFk/maxresdefault.jpg", + "publishDate": "2021-09-09T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "28" + ] + }, + { + "id": "286", + "title": "ADB Podcast Episode 174: Compose in Android Studio", + "content": "In this episode, Tor and Nick are joined by Chris Sinco, Diego Perez and Nicolas Roard to discuss the features added to Android Studio for Jetpack Compose. Tune in as they discuss the Compose preview, interactive preview, animation inspector, and additions to the Layout inspector along with their approach to creating tooling to support Compose’s code-centric system.", + "url": "http://adbackstage.libsyn.com/episode-174-compose-tooling", + "headerImageUrl": "http://assets.libsyn.com/content/110962067?height=250&width=250&overlay=true", + "publishDate": "2021-09-08T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "5", + "3" + ], + "authors": [ + "32" + ] + }, + { + "id": "287", + "title": "Hilt under the hood", + "content": "This episode dives into how the Hilt annotation processors generate code, and how the Hilt Gradle plugin works behind the scenes to improve the overall experience when using Hilt with Gradle.", + "url": "https://medium.com/androiddevelopers/mad-skills-series-hilt-under-the-hood-9d89ee227059", + "headerImageUrl": "https://miro.medium.com/max/1400/1*a_ZJwMHs17SmEFr3uEbxDg.png", + "publishDate": "2021-09-07T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "50" + ] + }, + { + "id": "288", + "title": "Trackr comes to the Big Screen", + "content": "A blog post on Trackr, a sample task management app where we showcase Modern Android Development best practices. This post takes you through how applying Material Design and responsive patterns produced a more refined and intuitive user experience on large screen devices.", + "url": "https://medium.com/androiddevelopers/trackr-comes-to-the-big-screen-9f13c6f927bf", + "headerImageUrl": "https://miro.medium.com/max/1400/1*678DlYtu4G7wFrq30FQ7Mw.png", + "publishDate": "2021-09-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "51" + ] + }, + { + "id": "289", + "title": "Accessibility services and the Android Accessibility model", + "content": "This Accessibilities series episode covers accessibility services like TalkBack, Switch Access and Voice Access and how they help users interact with your apps. Android’s accessibility framework allows you to write one app and the framework takes care of providing the information needed by different accessibility services.", + "url": "https://youtu.be/LxKat_m7mHk", + "headerImageUrl": "https://i.ytimg.com/vi/LxKat_m7mHk/maxresdefault.jpg", + "publishDate": "2021-09-02T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "14" + ], + "authors": [ + "39" + ] + }, + { + "id": "290", + "title": "New Accessibility Pathway", + "content": "Want even more accessibility? You are in luck, check out this entire new learning pathway aimed at teaching you how to make your app more accessible.", + "url": "https://developer.android.com/courses/pathways/make-your-android-app-accessible", + "headerImageUrl": "https://developers.google.com/profile/badges/playlists/make-your-android-app-accessible/badge.svg", + "publishDate": "2021-08-31T23:00:00.000Z", + "type": "", + "topics": [ + "14" + ], + "authors": [] + }, + { + "id": "291", + "title": "Jetpack Compose 1.0 stable is released", + "content": "Jetpack Compose, Android’s modern, native UI toolkit is now stable and ready for you to adopt in production. It interoperates with your existing app, integrates with existing Jetpack libraries, implements Material Design with straightforward theming, supports lists with Lazy components using minimal boilerplate, and has a powerful, extensible animation system.", + "url": "https://android-developers.googleblog.com/2021/07/jetpack-compose-announcement.html", + "headerImageUrl": "https://1.bp.blogspot.com/-9MiK78CFMLM/YQFurOq9AII/AAAAAAAAQ1A/lKj5GiDnO_MkPLb72XqgnvD5uxOsHO-eACLcBGAsYHQ/w1200-h630-p-k-no-nu/Android-Compose-1.0-header-v2.png", + "publishDate": "2021-07-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "3" + ], + "authors": [ + "52" + ] + }, + { + "id": "292", + "title": "Android Studio Artic Fox stable is released", + "content": "Android Studio Arctic Fox is now available in the stable release channel. Arctic Fox brings Jetpack Compose to life with Compose Preview, Deploy Preview, Compose support in the Layout Inspector, and Live Editing of literals. Compose Preview works with the @Preview annotation to let you instantly see the impact of changes across multiple themes, screen sizes, font sizes, and more. Deploy Preview deploys snippets of your Compose code to a device or emulator for quick testing. Layout inspector now works with apps written fully in Compose as well as apps that have Compose alongside Views, allowing you to explore your layouts and troubleshoot. With Live Edit of literals, you can edit literals such as strings, numbers, booleans, etc. and see the immediate results change in previews, the emulator, or on a physical device — all without having to compile.\n", + "url": "https://android-developers.googleblog.com/2021/07/android-studio-arctic-fox-202031-stable.html", + "headerImageUrl": "https://1.bp.blogspot.com/-cmcRT5BGOTY/YQBKC6asA0I/AAAAAAAAQzg/hZrde9Sgx881Wdf-c__VMkTvsKoVjOwsACLcBGAsYHQ/w1200-h630-p-k-no-nu/Arctic_Fox_Splash_2x%2B%25281%2529.png", + "publishDate": "2021-07-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "5", + "3" + ], + "authors": [ + "53" + ] + }, + { + "id": "293", + "title": "User control, privacy, security, and safety", + "content": "Play announced new updates to bolster user control, privacy, and security. The post covered advertising ID updates, including zeroing out the advertising ID when users opt out of interest-based advertising or ads personalization, the developer preview of the app set ID, enhanced protection for kids, and policy updates around dormant accounts and users of the AccessibilityService API.", + "url": "https://android-developers.googleblog.com/2021/07/announcing-policy-updates-to-bolster.html", + "headerImageUrl": "https://1.bp.blogspot.com/-pWCVY7BR-z8/YQAzb9zCZsI/AAAAAAAAQzY/2-OetxLvjOUYhHlTFJNw5JSm_BVjkI0VwCLcBGAsYHQ/s0/Untitled.png", + "publishDate": "2021-07-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12" + ], + "authors": [ + "10" + ] + }, + { + "id": "294", + "title": "Identify performance bottlenecks using system trace", + "content": "System trace profiling within Android Studio with a detailed walkthrough of app startup performance.", + "url": "https://www.youtube.com/watch?v=aUrqx9AnDUg", + "headerImageUrl": "https://i.ytimg.com/vi/aUrqx9AnDUg/hqdefault.jpg", + "publishDate": "2021-07-25T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "7", + "5" + ], + "authors": [ + "37" + ] + }, + { + "id": "295", + "title": "Testing in Compose", + "content": "ADB released episode #171, part of our continuing series on Jetpack Compose. In this episode, Nick and Romain are joined by Filip Pavlis, Jelle Fresen & Jose Alcérreca to talk about Testing in Compose. They discuss how Compose’s testing APIs were developed hand-in-hand with the UI toolkit, making them more deterministic and opening up new possibilities like manipulating time. They go on to discuss the semantics tree, interop testing, screenshot testing and the possibilities for host-side testing.", + "url": "https://adbackstage.libsyn.com/episode-171-compose-testing", + "headerImageUrl": "http://assets.libsyn.com/content/108505820?height=250&width=250&overlay=true", + "publishDate": "2021-06-29T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "3", + "6" + ], + "authors": [ + "54" + ] + }, + { + "id": "296", + "title": "DataStore reached release candidate status", + "content": "DataStore has reached release candidate status meaning the 1.0 stable release is right around the corner!", + "url": "https://developer.android.com/topic/libraries/architecture/datastore", + "headerImageUrl": "https://developer.android.com/images/social/android-developers.png", + "publishDate": "2021-06-29T23:00:00.000Z", + "type": "Jetpack release 🚀", + "topics": [ + "9" + ], + "authors": [] + }, + { + "id": "297", + "title": "Scope Storage Myths", + "content": "Apps will be required to update their targetSdkVersion to API 30 in the second half of the year. That means your app will be required to work with Scoped Storage. In this blog post, Nicole Borrelli busts some Scope storage myths in a Q&A format.", + "url": "https://medium.com/androiddevelopers/scope-storage-myths-ca6a97d7ff37", + "headerImageUrl": "https://miro.medium.com/max/1200/1*csWzYUmYq_1HQsqBWk3OTA.jpeg", + "publishDate": "2021-06-27T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "9", + "11" + ], + "authors": [ + "55" + ] + }, + { + "id": "298", + "title": "Navigation with Multiple back stacks", + "content": "As part of the rercommended Material pattern for bottom-navigation, the Jetpack Navigation library makes it easy to implement navigation with multiple back-stacks", + "url": "https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f", + "headerImageUrl": "https://miro.medium.com/max/1400/1*v7S7LKg4TlrMRlneeP224Q.jpeg", + "publishDate": "2021-06-14T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "25" + ] + }, + { + "id": "299", + "title": "Build sophisticated search features with AppSearch", + "content": "AppSearch is an on-device search library which provides high performance and feature-rich full-text search functionality. Learn how to use the new Jetpack AppSearch library for doing high-performance on-device full text searches.", + "url": "https://android-developers.googleblog.com/2021/06/sophisticated-search-with-appsearch-in-jetpack.html", + "headerImageUrl": "https://1.bp.blogspot.com/-PmN4MS50wvo/YMj-HmY4N2I/AAAAAAAAQoQ/5eCx8CU1HgAlFQnQ55IOb_CCVRhe8eGewCLcBGAsYHQ/w1200-h630-p-k-no-nu/AppSearch.jpg", + "publishDate": "2021-06-13T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "8", + "2" + ], + "authors": [ + "56" + ] + }, + { + "id": "300", + "title": "ADB Podcast Episode 167: Jetpack Compose Layout", + "content": "In this second episode of our mini-series on Jetpack Compose (AD/BC), Nick and Romain are joined by Anastasia Soboleva, George Mount and Mihai Popa to talk about Compose’s layout system. They explain how the Compose layout model works and its benefits, introduce common layout composables, discuss how writing your own layout is far simpler than Views, and how you can even animate layout.", + "url": "https://adbackstage.libsyn.com/episode-167-jetpack-compose-layout", + "headerImageUrl": "http://assets.libsyn.com/content/105399023?height=250&width=250&overlay=true", + "publishDate": "2021-06-13T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "3" + ], + "authors": [ + "57" + ] + }, + { + "id": "301", + "title": "Create an application CoroutineScope using Hilt", + "content": "Learn how to create an applicatioon-scoped CoroutineScope using Hilt, and how to inject it as a dependency.", + "url": "https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528", + "headerImageUrl": "https://miro.medium.com/max/1400/1*MgDtM-AJmc2m2hg5chkflg.png", + "publishDate": "2021-06-09T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "4" + ], + "authors": [ + "23" + ] + }, + { + "id": "302", + "title": "Android 12 Beta 2 Update", + "content": "The second Beta of Android 12 has just been released for you to try. Beta 2 adds new privacy features like the Privacy Dashboard and continues our work of refining the release.", + "url": "https://android-developers.googleblog.com/2021/06/android-12-beta-2-update.html", + "headerImageUrl": "https://1.bp.blogspot.com/-tLt-TVPqpjA/YKMRwRPMfjI/AAAAAAAAQik/JNtMesFZ2i87RyBACHAVEC14CvcU7G__wCLcBGAsYHQ/w1200-h630-p-k-no-nu/Screen%2BShot%2B2021-05-17%2Bat%2B9.00.30%2BPM.png", + "publishDate": "2021-06-08T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13" + ], + "authors": [ + "14" + ] + }, + { + "id": "303", + "title": "Top 3 things in Android 12 | Android @ Google I/O '21", + "content": "Did you miss the latest in Android 12 at Google I/O 2021? Android Software Engineer Chet Haase will recap the top three themes in Android 12 from this year’s Google I/O!", + "url": "https://www.youtube.com/watch?v=tvf1wmD5H0M", + "headerImageUrl": "https://i.ytimg.com/vi/tvf1wmD5H0M/maxresdefault.jpg", + "publishDate": "2021-06-08T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "13" + ], + "authors": [ + "31" + ] + }, + { + "id": "304", + "title": "ADB Podcast Episode 166: Security Deposit", + "content": "In this episode, Chad and Jeff from the Android Security team join Tor and Romain to talk about… security. They explain what the platform does to help preserve user trust and device integrity, why it sometimes means restricting existing APIs, and touch on what apps can do or should worry about.", + "url": "http://adbackstage.libsyn.com/episode-166-security-deposit", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2021-06-07T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "11" + ], + "authors": [ + "32" + ] + }, + { + "id": "305", + "title": "Multiple Back Stacks", + "content": "A deep dive into multiple back stacks and some of the work it took to make this feature happen in Fragments and Navigation", + "url": "https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134", + "headerImageUrl": "https://miro.medium.com/max/1400/1*5-lbc-YBJlZnxVFPvNMPAQ.png", + "publishDate": "2021-06-06T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "58" + ] + }, + { + "id": "306", + "title": "Building across devices | Android @ Google I/O '21", + "content": "Did you miss the latest in Building across screens at Google I/O 2021? Product Manager Diana Wong will recap the top three announcements from this year’s Google I/O!", + "url": "https://www.youtube.com/watch?v=O5oRiIUk_F4", + "headerImageUrl": "https://i.ytimg.com/vi/O5oRiIUk_F4/maxresdefault.jpg", + "publishDate": "2021-06-02T23:00:00.000Z", + "type": "Video 📺", + "topics": [ + "2" + ], + "authors": [ + "59" + ] + }, + { + "id": "307", + "title": "Navigation in Feature Modules", + "content": "Feature modules delivered with Play Feature delivery at not downloadedd at install time, but only when the app requestss them. Learn how to use the dynamic features navigation library to include the graph from the feature module.", + "url": "https://medium.com/androiddevelopers/navigation-in-feature-modules-322ac3d79334", + "headerImageUrl": "https://miro.medium.com/max/1400/1*v7S7LKg4TlrMRlneeP224Q.jpeg", + "publishDate": "2021-06-01T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "2" + ], + "authors": [ + "25" + ] + }, + { + "id": "308", + "title": "ADB Podcast Episode 165: Material Witnesses", + "content": "In this episode, Chet and Romain chattedd with Hunter and Nick from the Material Design team about recent additions and improvements to the Material Design Component libraries: transitions, motion theming, Compose, large screens support and guidance, etc.", + "url": "http://adbackstage.libsyn.com/episode-165-material-witnesses", + "headerImageUrl": "http://assets.libsyn.com/show/332855?height=250&width=250&overlay=true", + "publishDate": "2021-06-01T23:00:00.000Z", + "type": "Podcast 🎙", + "topics": [ + "2" + ], + "authors": [ + "31" + ] + }, + { + "id": "309", + "title": "Grow Your Indie Game with Help From Google Play", + "content": "Google Play is opening submissions for two of our annual developer programs - the Indie Games Accelerator and the Indie Games Festival. These programs are designed to help small games studios grow on Google Play, no matter what stage they are in", + "url": "https://developers.googleblog.com/2021/06/grow-your-indie-game-with-help-from-google-play.html", + "headerImageUrl": "https://1.bp.blogspot.com/-MNEblg7_8fA/YK7lludSxJI/AAAAAAAAKQM/_YIT15giTk42oPXWIhK6l2FBVt5PCFKTwCLcBGAsYHQ/w1200-h630-p-k-no-nu/Joint_Announcement_Android%2BDevelopers%2BBlog_Header_1200x600%2B%25282%2529.png", + "publishDate": "2021-05-31T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "12", + "17" + ], + "authors": [ + "60" + ] + }, + { + "id": "310", + "title": "Untrusted Touch Events in Android", + "content": "Android 12 prevents touch events from being deliverred if these touches first pass through a window from a different app to ensure users can see what they are interacting with. Learn about alternatives, to see if your app will be affected and how you can test to see if your app will be impacted.", + "url": "https://medium.com/androiddevelopers/untrusted-touch-events-2c0e0b9c374c", + "headerImageUrl": "https://miro.medium.com/max/1400/1*lvwe7v_bcNsNXI_7ltFkJA.jpeg", + "publishDate": "2021-05-25T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "13" + ], + "authors": [ + "15" + ] + }, + { + "id": "311", + "title": "Android @ Google I/O: 3 things to know in Modern Android Development", + "content": "This year’s Google I/O brought lots of updates for Modern Android Development. Learn about the top 3 things you should know.", + "url": "https://android-developers.googleblog.com/2021/05/mad-spotlight.html", + "headerImageUrl": "https://1.bp.blogspot.com/-8cqMFObMeko/YK5RbJ7Yr_I/AAAAAAAAQkw/Iw4_hRZwa7QD1CmVGnZUZ4NjYowXZadTgCLcBGAsYHQ/w1200-h630-p-k-no-nu/Android_PostIO_blog-MAD.png", + "publishDate": "2021-05-24T23:00:00.000Z", + "type": "Article 📚", + "topics": [ + "1" + ], + "authors": [ + "61" + ] + } + ] +""".trimIndent() \ No newline at end of file From 8aaa37d4d6293e58a9ceae15589f07240735f5b1 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 15:29:30 -0800 Subject: [PATCH 26/62] Use Naiper as the logging library --- core/common/build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index cd2b1fbce..c238bfcf2 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -26,6 +26,7 @@ android { kotlin { sourceSets { commonMain.dependencies { + api(libs.naiper) implementation(libs.kotlinx.coroutines.core) } commonTest.dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7c3190f05..6d5292a72 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,6 +73,7 @@ junit = "4.13.2" sqldelight = "2.0.1" kotlinInject = '0.6.3' multiplatform-settings = "1.1.1" +naiper = "2.7.1" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -176,6 +177,7 @@ multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settin multiplatform-settings-test = { group = "com.russhwolf", name = "multiplatform-settings-test", version.ref = "multiplatform-settings" } multiplatform-settings-serialization = { group = "com.russhwolf", name = "multiplatform-settings-serialization", version.ref = "multiplatform-settings" } multiplatform-settings-coroutines = { group = "com.russhwolf", name = "multiplatform-settings-coroutines", version.ref = "multiplatform-settings" } +naiper = { group = "io.github.aakira", name = "naiper", version.ref = "naiper" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From 234ad1e6cc08095db388ef6c698b83ea09aa2617 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 15:40:20 -0800 Subject: [PATCH 27/62] Change js to wasmJs --- .../apps/nowinandroid/KotlinMultiplatform.kt | 19 ++++++++++--------- .../core/di/DispatchersComponent.kt | 0 .../core/database/DriverModule.kt | 0 .../nowinandroid/core/database/BaseTest.js.kt | 0 4 files changed, 10 insertions(+), 9 deletions(-) rename core/common/src/{jsMain => wasmJsMain}/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt (100%) rename core/database/src/{jsMain => wasmJsMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt (100%) rename core/database/src/{jsTest => wasmJsTest}/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt (100%) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index f3bf46fc2..cb511ed06 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -21,6 +21,8 @@ import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.konan.target.HostManager @@ -28,6 +30,7 @@ import org.jetbrains.kotlin.konan.target.HostManager * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. * https://github.com/cashapp/sqldelight/blob/master/buildLogic/multiplatform-convention/src/main/kotlin/app/cash/sqldelight/multiplatform/MultiplatformConventions.kt */ +@OptIn(ExperimentalWasmDsl::class) internal fun Project.configureKotlinMultiplatform() { extensions.configure { // Enable native group by default @@ -37,19 +40,17 @@ internal fun Project.configureKotlinMultiplatform() { jvm() androidTarget() - js { + wasmJs { browser { - testTask { - useKarma { - useChromeHeadless() + commonWebpackConfig { + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(project.projectDir.path) + } } } } - compilations.configureEach { - kotlinOptions { - moduleKind = "umd" - } - } } // tier 1 diff --git a/core/common/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt b/core/common/src/wasmJsMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt similarity index 100% rename from core/common/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt rename to core/common/src/wasmJsMain/kotlin/com/google/samples/apps/nowinandroid/core/di/DispatchersComponent.kt diff --git a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/wasmJsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 100% rename from core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt rename to core/database/src/wasmJsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt diff --git a/core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt b/core/database/src/wasmJsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt similarity index 100% rename from core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt rename to core/database/src/wasmJsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt From 25934d45c928083520cbcdf8bed4b8fdcacbf989 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 16:07:46 -0800 Subject: [PATCH 28/62] Remove support for wasm --- .../kotlin/KotlinInjectConventionPlugin.kt | 5 ++++ .../apps/nowinandroid/KotlinMultiplatform.kt | 26 ++++++++++--------- core/database/build.gradle.kts | 13 +++++----- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt index 61e491ff1..692c8cb02 100644 --- a/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt @@ -34,6 +34,11 @@ class KotlinInjectConventionPlugin: Plugin { add("kspIosX64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) add("kspIosArm64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) add("kspIosSimulatorArm64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) +// add("kspWasmJs", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspAndroid", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspJvm", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspMacosX64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspMacosArm64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index cb511ed06..9e77a6f91 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -39,19 +39,21 @@ internal fun Project.configureKotlinMultiplatform() { jvm() androidTarget() +// SqlDelight does not support wasm yet +// https://github.com/cashapp/sqldelight/pull/4965/files - wasmJs { - browser { - commonWebpackConfig { - devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { - static = (static ?: mutableListOf()).apply { - // Serve sources to debug inside browser - add(project.projectDir.path) - } - } - } - } - } +// wasmJs { +// browser { +// commonWebpackConfig { +// devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { +// static = (static ?: mutableListOf()).apply { +// // Serve sources to debug inside browser +// add(project.projectDir.path) +// } +// } +// } +// } +// } // tier 1 // :core:datastore:linuxMain: Could not resolve com.russhwolf:multiplatform-settings-no-arg:1.1.1. diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index ac81f18d7..faf1fc3c3 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -50,12 +50,13 @@ kotlin { jvmMain.dependencies { implementation(libs.sqldelight.sqlite.driver) } - jsMain.dependencies { - implementation(libs.sqldelight.webworker.driver) - implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.0.1")) - implementation(npm("sql.js", "1.8.0")) - implementation(devNpm("copy-webpack-plugin", "9.1.0")) - } + // https://github.com/cashapp/sqldelight/pull/4965/files +// wasmJsMain.dependencies { +// implementation(libs.sqldelight.webworker.driver) +// implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.0.1")) +// implementation(npm("sql.js", "1.8.0")) +// implementation(devNpm("copy-webpack-plugin", "9.1.0")) +// } commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) From 6643cadf8b43df687d4fddf950019fc38b9e76ad Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 16:10:30 -0800 Subject: [PATCH 29/62] Use Kermit as the logging framework --- core/common/build.gradle.kts | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index c238bfcf2..56a6fd958 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -26,7 +26,7 @@ android { kotlin { sourceSets { commonMain.dependencies { - api(libs.naiper) + api(libs.logging) implementation(libs.kotlinx.coroutines.core) } commonTest.dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d5292a72..6cf1f0194 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ junit = "4.13.2" sqldelight = "2.0.1" kotlinInject = '0.6.3' multiplatform-settings = "1.1.1" -naiper = "2.7.1" +kermit = "2.0.3" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -177,7 +177,7 @@ multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settin multiplatform-settings-test = { group = "com.russhwolf", name = "multiplatform-settings-test", version.ref = "multiplatform-settings" } multiplatform-settings-serialization = { group = "com.russhwolf", name = "multiplatform-settings-serialization", version.ref = "multiplatform-settings" } multiplatform-settings-coroutines = { group = "com.russhwolf", name = "multiplatform-settings-coroutines", version.ref = "multiplatform-settings" } -naiper = { group = "io.github.aakira", name = "naiper", version.ref = "naiper" } +logging = { group = "co.touchlab", name = "kermit", version.ref = "kermit" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From bd2059ba88c38365cef7b7233aa498aa78ae9078 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 21:19:13 -0800 Subject: [PATCH 30/62] Implement network module --- app/build.gradle.kts | 2 +- core/designsystem/build.gradle.kts | 2 +- core/network/build.gradle.kts | 22 ++++-- .../kotlin/JvmUnitTestFakeAssetManager.kt | 42 ----------- .../DiskCacheComponent.kt} | 14 ++-- .../core/network/di/FlavoredNetworkModule.kt | 7 -- .../core/network/di/ImageLoaderComponent.kt | 63 +++++++++++++++++ .../core/network/di/NetworkModule.kt | 69 +++++++------------ .../network/fake/FakeNiaNetworkDataSource.kt | 15 ++-- .../network/retrofit/RetrofitNiaNetwork.kt | 36 ++-------- .../fake/FakeNiaNetworkDataSourceTest.kt | 5 +- core/ui/build.gradle.kts | 4 +- gradle/libs.versions.toml | 9 +-- 13 files changed, 133 insertions(+), 157 deletions(-) delete mode 100644 core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt rename core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/{fake/FakeAssetManager.kt => di/DiskCacheComponent.kt} (67%) create mode 100644 core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 520baa134..11f802f3c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -97,7 +97,7 @@ dependencies { implementation(libs.androidx.navigation.compose) implementation(libs.androidx.profileinstaller) implementation(libs.kotlinx.coroutines.guava) - implementation(libs.coil.kt) + implementation(libs.coil) debugImplementation(libs.androidx.compose.ui.testManifest) debugImplementation(projects.uiTestHiltManifest) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index d68117d06..5d0539ce3 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -40,7 +40,7 @@ dependencies { debugApi(libs.androidx.compose.ui.tooling) - implementation(libs.coil.kt.compose) + implementation(libs.coil.compose) testImplementation(libs.androidx.compose.ui.test) testImplementation(libs.accompanist.testharness) diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 8edda61fe..80650b276 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -46,8 +46,10 @@ kotlin { api(libs.kotlinx.datetime) api(projects.core.common) api(projects.core.model) - implementation(libs.coil.kt) - implementation(libs.coil.kt.svg) + implementation(libs.coil) + implementation(libs.coil.core) + implementation(libs.coil.svg) + implementation(libs.coil.network.ktor) implementation(libs.kotlinx.serialization.json) implementation(libs.ktor.client.core) implementation(libs.ktor.client.json) @@ -56,9 +58,9 @@ kotlin { implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktorfit.lib) - implementation(libs.ktorfit.ksp) } commonTest.dependencies { + implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) } androidMain.dependencies { @@ -67,9 +69,9 @@ kotlin { appleMain.dependencies { implementation(libs.ktor.client.darwin) } - jsMain.dependencies { - implementation(libs.ktor.client.js) - } +// wasmJsMain.dependencies { +// implementation(libs.ktor.client.js) +// } jvmMain.dependencies { implementation(libs.ktor.client.java) } @@ -81,4 +83,12 @@ kotlin { dependencies { add("kspCommonMainMetadata", libs.ktorfit.ksp) + add("kspAndroid", libs.ktorfit.ksp) +// add("kspWasmJs", libs.ktorfit.ksp) + add("kspJvm", libs.ktorfit.ksp) + add("kspIosX64", libs.ktorfit.ksp) + add("kspIosArm64", libs.ktorfit.ksp) + add("kspIosSimulatorArm64", libs.ktorfit.ksp) + add("kspMacosX64", libs.ktorfit.ksp) + add("kspMacosArm64", libs.ktorfit.ksp) } diff --git a/core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt b/core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt deleted file mode 100644 index 79370d5a8..000000000 --- a/core/network/src/commonMain/kotlin/JvmUnitTestFakeAssetManager.kt +++ /dev/null @@ -1,42 +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. - */ - -import androidx.annotation.VisibleForTesting -import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager -import java.io.File -import java.io.InputStream -import java.util.Properties - -/** - * This class helps with loading Android `/assets` files, especially when running JVM unit tests. - * It must remain on the root package for an easier [Class.getResource] with relative paths. - * @see UnitTestOptions - */ -@VisibleForTesting -internal object JvmUnitTestFakeAssetManager : FakeAssetManager { - private val config = - requireNotNull(javaClass.getResource("com/android/tools/test_config.properties")) { - """ - Missing Android resources properties file. - Did you forget to enable the feature in the gradle build file? - android.testOptions.unitTests.isIncludeAndroidResources = true - """.trimIndent() - } - private val properties = Properties().apply { config.openStream().use(::load) } - private val assets = File(properties["android_merged_assets"].toString()) - - override fun open(fileName: String): InputStream = File(assets, fileName).inputStream() -} diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt similarity index 67% rename from core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt index 53ad7d48d..e954315fd 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeAssetManager.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.network.fake +package com.google.samples.apps.nowinandroid.core.network.di -import java.io.InputStream +import coil3.disk.DiskCache +import me.tatarka.inject.annotations.Provides -fun interface FakeAssetManager { - fun open(fileName: String): InputStream -} +expect class DiskCacheComponent { + @Provides + internal fun newDiskCache(): DiskCache? +} \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt index 60744eeab..a6aff8b95 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt @@ -18,15 +18,8 @@ package com.google.samples.apps.nowinandroid.core.network.di import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -@Module -@InstallIn(SingletonComponent::class) internal interface FlavoredNetworkModule { - @Binds fun binds(impl: FakeNiaNetworkDataSource): NiaNetworkDataSource } diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt new file mode 100644 index 000000000..cfae7527f --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt @@ -0,0 +1,63 @@ +/* + * 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.network.di + +import coil3.ImageLoader +import coil3.PlatformContext +import coil3.disk.DiskCache +import coil3.memory.MemoryCache +import coil3.request.crossfade +import coil3.util.DebugLogger +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides + +@Component +abstract class ImageLoaderComponent { + /** + * Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this + * format. During Coil's initialization it will call `applicationContext.newImageLoader()` to + * obtain an ImageLoader. + * + * @see Coil + */ + @Provides + fun provideImageLoader( + context: PlatformContext, + diskCache: DiskCache?, + debug: Boolean, + ): ImageLoader { + return ImageLoader.Builder(context) + .memoryCache { + MemoryCache.Builder() + // Set the max size to 25% of the app's available memory. + .maxSizePercent(context, percent = 0.25) + .build() + } + .diskCache { + diskCache + } + // Show a short crossfade when loading images asynchronously. + .crossfade(true) + // Enable logging if this is a debug build. + .apply { + if (debug) { + logger(DebugLogger()) + } + } + .build() + } +} diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index 7a54b0fc9..4408885c5 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -16,61 +16,38 @@ package com.google.samples.apps.nowinandroid.core.network.di -import com.google.samples.apps.nowinandroid.core.network.fake.FakeAssetManager -import de.jensklingenberg.ktorfit.Call +import de.jensklingenberg.ktorfit.Ktorfit +import de.jensklingenberg.ktorfit.converter.builtin.CallConverterFactory +import de.jensklingenberg.ktorfit.converter.builtin.FlowConverterFactory +import de.jensklingenberg.ktorfit.ktorfit +import io.ktor.client.HttpClient +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json +import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides -internal object NetworkModule { +@Component +internal abstract class NetworkModule { + @Provides fun providesNetworkJson(): Json = Json { ignoreUnknownKeys = true } - fun providesFakeAssetManager( - @ApplicationContext context: Context, - ): FakeAssetManager = FakeAssetManager(context.assets::open) - - @Provides - @Singleton - fun okHttpCallFactory(): Call.Factory = trace("NiaOkHttpClient") { - OkHttpClient.Builder() - .addInterceptor( - HttpLoggingInterceptor() - .apply { - if (BuildConfig.DEBUG) { - setLevel(HttpLoggingInterceptor.Level.BODY) - } - }, - ) - .build() - } - - /** - * Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this - * format. During Coil's initialization it will call `applicationContext.newImageLoader()` to - * obtain an ImageLoader. - * - * @see Coil - */ @Provides - @Singleton - fun imageLoader( - // We specifically request dagger.Lazy here, so that it's not instantiated from Dagger. - okHttpCallFactory: dagger.Lazy, - @ApplicationContext application: Context, - ): ImageLoader = trace("NiaImageLoader") { - ImageLoader.Builder(application) - .callFactory { okHttpCallFactory.get() } - .components { add(SvgDecoder.Factory()) } - // Assume most content images are versioned urls - // but some problematic images are fetching each time - .respectCacheHeaders(false) - .apply { - if (BuildConfig.DEBUG) { - logger(DebugLogger()) + fun provideKtorfit(json: Json): Ktorfit = ktorfit { + baseUrl(BuildConfig.BACKEND_URL) + httpClient( + HttpClient { + install(ContentNegotiation) { + json(json) } - } - .build() + }, + ) + converterFactories( + FlowConverterFactory(), + CallConverterFactory(), + ) } } diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt index 6ef90ecff..8b4b235ff 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt @@ -16,39 +16,34 @@ package com.google.samples.apps.nowinandroid.core.network.fake -import JvmUnitTestFakeAssetManager -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.di.IODispatcher import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject /** * [NiaNetworkDataSource] implementation that provides static news resources to aid development */ class FakeNiaNetworkDataSource @Inject constructor( - @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, + private val ioDispatcher: IODispatcher, private val networkJson: Json, - private val assets: FakeAssetManager = JvmUnitTestFakeAssetManager, ) : NiaNetworkDataSource { @OptIn(ExperimentalSerializationApi::class) override suspend fun getTopics(ids: List?): List = withContext(ioDispatcher) { - assets.open(TOPICS_ASSET).use(networkJson::decodeFromStream) + networkJson.decodeFromString(TOPICS_ASSET) } @OptIn(ExperimentalSerializationApi::class) override suspend fun getNewsResources(ids: List?): List = withContext(ioDispatcher) { - assets.open(NEWS_ASSET).use(networkJson::decodeFromStream) + networkJson.decodeFromString(NEWS_ASSET) } override suspend fun getTopicChangeList(after: Int?): List = diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index e9fe99d9e..cac997563 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -16,22 +16,15 @@ package com.google.samples.apps.nowinandroid.core.network.retrofit -import androidx.tracing.trace -import com.google.samples.apps.nowinandroid.core.network.BuildConfig import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import de.jensklingenberg.ktorfit.Ktorfit +import de.jensklingenberg.ktorfit.http.GET +import de.jensklingenberg.ktorfit.http.Query import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import okhttp3.Call -import okhttp3.MediaType.Companion.toMediaType -import retrofit2.Retrofit -import retrofit2.http.GET -import retrofit2.http.Query -import javax.inject.Inject -import javax.inject.Singleton +import me.tatarka.inject.annotations.Inject /** * Retrofit API declaration for NIA Network API @@ -58,8 +51,6 @@ private interface RetrofitNiaNetworkApi { ): List } -private const val NIA_BASE_URL = BuildConfig.BACKEND_URL - /** * Wrapper for data provided from the [NIA_BASE_URL] */ @@ -69,26 +60,13 @@ private data class NetworkResponse( ) /** - * [Retrofit] backed [NiaNetworkDataSource] + * [Ktrofit] backed [NiaNetworkDataSource] */ -@Singleton internal class RetrofitNiaNetwork @Inject constructor( - networkJson: Json, - okhttpCallFactory: dagger.Lazy, + ktorfit: Ktorfit, ) : NiaNetworkDataSource { - private val networkApi = trace("RetrofitNiaNetwork") { - Retrofit.Builder() - .baseUrl(NIA_BASE_URL) - // We use callFactory lambda here with dagger.Lazy - // to prevent initializing OkHttp on the main thread. - .callFactory { okhttpCallFactory.get().newCall(it) } - .addConverterFactory( - networkJson.asConverterFactory("application/json".toMediaType()), - ) - .build() - .create(RetrofitNiaNetworkApi::class.java) - } + private val networkApi = ktorfit.create() override suspend fun getTopics(ids: List?): List = networkApi.getTopics(ids = ids).data diff --git a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt b/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt index a0c60fdcb..b64c06e52 100644 --- a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt +++ b/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.network.fake -import JvmUnitTestFakeAssetManager import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import kotlinx.coroutines.test.StandardTestDispatcher @@ -27,6 +26,7 @@ import kotlinx.datetime.toInstant import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Test +import kotlin.test.BeforeTest import kotlin.test.assertEquals class FakeNiaNetworkDataSourceTest { @@ -35,12 +35,11 @@ class FakeNiaNetworkDataSourceTest { private val testDispatcher = StandardTestDispatcher() - @Before + @BeforeTest fun setUp() { subject = FakeNiaNetworkDataSource( ioDispatcher = testDispatcher, networkJson = Json { ignoreUnknownKeys = true }, - assets = JvmUnitTestFakeAssetManager, ) } diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 5d8a65d44..084700bf2 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -33,8 +33,8 @@ dependencies { api(projects.core.model) implementation(libs.androidx.browser) - implementation(libs.coil.kt) - implementation(libs.coil.kt.compose) + implementation(libs.coil) + implementation(libs.coil.compose) androidTestImplementation(projects.core.testing) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ec63b6ad..f9821a38f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -119,10 +119,11 @@ androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiaut androidx-tracing-ktx = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "androidxTracing" } androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidxWork" } -coil-kt = { group = "io.coil-kt", name = "coil3", version.ref = "coil" } -coil-core = { group = "io.coil-kt", name = "coil-core", version.ref = "coil" } -coil-kt-compose = { group = "io.coil-kt", name = "coil-compose-core", version.ref = "coil" } -coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" } +coil = { group = "io.coil-kt.coil3", name = "coil", version.ref = "coil" } +coil-core = { group = "io.coil-kt.coil3", name = "coil-core", version.ref = "coil" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose-core", version.ref = "coil" } +coil-svg = { group = "io.coil-kt.coil3", name = "coil-svg", version.ref = "coil" } +coil-network-ktor = { group = "io.coil-kt.coil3", name = "coil-network-ktor", version.ref = "coil" } firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } firebase-cloud-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" } From a6b0c1f0a3a8cd40d1868a5d8763210f8cf10732 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 1 Mar 2024 21:43:22 -0800 Subject: [PATCH 31/62] WIP: Add build konfig --- build.gradle.kts | 2 ++ core/network/build.gradle.kts | 11 ++++++++--- gradle/libs.versions.toml | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6e0f9ab17..0f84b5118 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,6 +26,7 @@ buildscript { classpath(libs.google.oss.licenses.plugin) { exclude(group = "com.google.protobuf") } + classpath(libs.buildkonfig.gradlePlugin) } } @@ -50,4 +51,5 @@ plugins { alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.sqldelight.gradle.plugin) apply false alias(libs.plugins.ktrofit) apply false + alias(libs.plugins.buildkonfig) apply false } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 80650b276..876dfc20d 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -19,15 +19,13 @@ plugins { alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.nowinandroid.kotlin.inject) alias(libs.plugins.ktrofit) + alias(libs.plugins.buildkonfig) id("kotlinx-serialization") id("com.google.devtools.ksp") id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") } android { - buildFeatures { - buildConfig = true - } namespace = "com.google.samples.apps.nowinandroid.core.network" testOptions { unitTests { @@ -40,6 +38,13 @@ secrets { defaultPropertiesFileName = "secrets.defaults.properties" } +buildKonfig { + packageName = "com.google.samples.apps.nowinandroid.core.network" + defaultConfigs { + buildConfigField(STRING, "BACKEND_URL", "\"https://www.example.com\"") + } +} + kotlin { sourceSets { commonMain.dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9821a38f..3f0ec8b5b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,6 +76,7 @@ multiplatform-settings = "1.1.1" kermit = "2.0.3" ktor = "3.0.0-beta-1" ktrofit = "1.12.0" +buildKonfig = "0.15.1" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -195,6 +196,7 @@ ktor-client-winhttp = { group = "io.ktor", name = "ktor-client-winhttp", version ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } ktorfit-ksp = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-ksp", version.ref = "ktrofit" } ktorfit-lib = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-lib", version.ref = "ktrofit" } +buildkonfig-gradlePlugin = { group = "com.codingfeline.buildkonfig", name = "buildkonfig-gradle-plugin", version.ref = "buildKonfig" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -227,6 +229,7 @@ jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "compose-plugi kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } sqldelight-gradle-plugin = { id = "app.cash.sqldelight", version.ref = "sqldelight" } ktrofit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktrofit" } +buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From 9b488bf70ab1b3ceded4f7a3c6071ac9e2748919 Mon Sep 17 00:00:00 2001 From: Jaehwa Noh Date: Sun, 3 Mar 2024 13:57:03 +0900 Subject: [PATCH 32/62] 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 33/62] 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 34/62] 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 35/62] =?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 36/62] 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 37/62] =?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 38/62] 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 39/62] 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 40/62] 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 41/62] 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 42/62] 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 43/62] 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 44/62] 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 45/62] 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 46/62] 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, From abe798056e32f3f40fa3614670d6d24bf2aa71f9 Mon Sep 17 00:00:00 2001 From: Jaehwa Noh Date: Thu, 7 Mar 2024 08:29:59 +0900 Subject: [PATCH 47/62] Add @After to guarantee DB was closed. (#1240) * Add @After to guarantee DB was closed. Change-Id: Ic29b7d12125853c71ddaef1a997ec123bb2dfc8b * Fix spotless - style Co-authored-by: Simon Marquis --------- Co-authored-by: Simon Marquis --- .../nowinandroid/core/database/dao/NewsResourceDaoTest.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt b/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt index 55e817618..535ab61a7 100644 --- a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt +++ b/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt @@ -27,6 +27,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant +import org.junit.After import org.junit.Before import org.junit.Test import kotlin.test.assertEquals @@ -48,6 +49,9 @@ class NewsResourceDaoTest { topicDao = db.topicDao() } + @After + fun closeDb() = db.close() + @Test fun newsResourceDao_fetches_items_by_descending_publish_date() = runTest { val newsResourceEntities = listOf( From 19f6f9e09ac387a8456ff59102b9666a7a7ac9e3 Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Thu, 7 Mar 2024 13:22:50 -0800 Subject: [PATCH 48/62] 2 pane support in Interests screen (#1234) * Add dependency on material3 adaptive Change-Id: Ic49934112a4bdbf15a68c694fbc6b0f23de960a6 * Add InterestsListDetailScreen composable Change-Id: I27e1f6d2e0eeac781baf2b671fa51a864ea5a971 * Store selectedTopicId in InterestsViewModel Change-Id: Id93704335686f171fbf80bdb54865d0f32dc36ce * Pass detail pane composable down Change-Id: I82752d8cfbb3519395f37748fb5f64b769c0c293 * Navigate to initial topic if provided Change-Id: I8998a55a29cdaf90577fa730d55c4ac2f54d6e5b * Lift LDPS up to app module Change-Id: Ibc6e8e598cd0cb62f804f11b2e48d8ae3a81df85 * Fix some navigation behavior Change-Id: Ib6c16aff692b9ce997747a30f2863303cc82fd8b * Navigate to initial topic if provided Change-Id: Iaafe4f876655d51243d7b99be985e9440fe2d4ed * Remove dependency in interests feature module Change-Id: Id517c95e11f93e1c7e17d749a7af0cfdf6085a1f * Hide back arrow when the topics list is visible Change-Id: I8901c3f79b11d35568f0ae779f97fab90e574aa8 * Update interests tests Change-Id: Ie5daf55985fdb53570397cb652abe31bad78f5cd * Highlight selected topic when displaying 2 panes Change-Id: Ifef9fb599f828f58390374b11eacc8be6c280415 * update dependency baselines Change-Id: I90dc21df3337865f4c5368634d3d45fcb0eccc00 * run spotless apply Change-Id: Ib5fb1b7fc26a62bd5e271c2a3721f1c13173f7f8 * Convert isListPaneHidden to isListPaneVisible Change-Id: I6e54f710df7db5ed6f3ec1cb284bc29f2763a657 * Set semantics for selected state Change-Id: I31f27d5036d07c9607909c09ac52a72391f899ca * Use scaffold roles when determining visibility Change-Id: Ib5fe236f182a5eeab20b61692a1cd53c17b68648 * Update multipleBackStackInterests test Change-Id: I1e372f7989817151a6765205291b13b561187fa8 --- .../dependencies/releaseRuntimeClasspath.txt | 66 ++++----- app/build.gradle.kts | 10 +- .../prodReleaseRuntimeClasspath.txt | 71 ++++----- .../apps/nowinandroid/ui/NavigationTest.kt | 10 +- .../nowinandroid/navigation/NiaNavHost.kt | 21 +-- .../apps/nowinandroid/ui/NiaAppState.kt | 4 +- .../interests2pane/Interests2PaneViewModel.kt | 35 +++++ .../InterestsListDetailScreen.kt | 136 ++++++++++++++++++ feature/interests/build.gradle.kts | 2 +- .../interests/InterestsScreenTest.kt | 5 +- .../feature/interests/InterestsItem.kt | 30 +++- .../feature/interests/InterestsScreen.kt | 15 +- .../feature/interests/InterestsViewModel.kt | 29 ++-- .../feature/interests/TabContent.kt | 4 + .../navigation/InterestsNavigation.kt | 37 +++-- .../interests/InterestsViewModelTest.kt | 13 +- .../feature/topic/TopicScreenTest.kt | 4 + .../nowinandroid/feature/topic/TopicScreen.kt | 30 ++-- .../topic/navigation/TopicNavigation.kt | 16 ++- gradle/libs.versions.toml | 4 +- 20 files changed, 407 insertions(+), 135 deletions(-) create mode 100644 app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt create mode 100644 app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 5a619eb1b..ca4bf99a3 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -12,41 +12,41 @@ androidx.browser:browser:1.6.0 androidx.collection:collection-jvm:1.4.0 androidx.collection:collection-ktx:1.4.0 androidx.collection:collection:1.4.0 -androidx.compose.animation:animation-android:1.6.1 -androidx.compose.animation:animation-core-android:1.6.1 -androidx.compose.animation:animation-core:1.6.1 -androidx.compose.animation:animation:1.6.1 -androidx.compose.foundation:foundation-android:1.6.1 -androidx.compose.foundation:foundation-layout-android:1.6.1 -androidx.compose.foundation:foundation-layout:1.6.1 -androidx.compose.foundation:foundation:1.6.1 +androidx.compose.animation:animation-android:1.6.2 +androidx.compose.animation:animation-core-android:1.6.2 +androidx.compose.animation:animation-core:1.6.2 +androidx.compose.animation:animation:1.6.2 +androidx.compose.foundation:foundation-android:1.6.2 +androidx.compose.foundation:foundation-layout-android:1.6.2 +androidx.compose.foundation:foundation-layout:1.6.2 +androidx.compose.foundation:foundation:1.6.2 androidx.compose.material3:material3-android:1.2.0 androidx.compose.material3:material3:1.2.0 -androidx.compose.material:material-icons-core-android:1.6.1 -androidx.compose.material:material-icons-core:1.6.1 -androidx.compose.material:material-icons-extended-android:1.6.1 -androidx.compose.material:material-icons-extended:1.6.1 -androidx.compose.material:material-ripple-android:1.6.1 -androidx.compose.material:material-ripple:1.6.1 -androidx.compose.runtime:runtime-android:1.6.1 -androidx.compose.runtime:runtime-saveable-android:1.6.1 -androidx.compose.runtime:runtime-saveable:1.6.1 -androidx.compose.runtime:runtime:1.6.1 -androidx.compose.ui:ui-android:1.6.1 -androidx.compose.ui:ui-geometry-android:1.6.1 -androidx.compose.ui:ui-geometry:1.6.1 -androidx.compose.ui:ui-graphics-android:1.6.1 -androidx.compose.ui:ui-graphics:1.6.1 -androidx.compose.ui:ui-text-android:1.6.1 -androidx.compose.ui:ui-text:1.6.1 -androidx.compose.ui:ui-tooling-preview-android:1.6.1 -androidx.compose.ui:ui-tooling-preview:1.6.1 -androidx.compose.ui:ui-unit-android:1.6.1 -androidx.compose.ui:ui-unit:1.6.1 -androidx.compose.ui:ui-util-android:1.6.1 -androidx.compose.ui:ui-util:1.6.1 -androidx.compose.ui:ui:1.6.1 -androidx.compose:compose-bom:2024.02.00 +androidx.compose.material:material-icons-core-android:1.6.2 +androidx.compose.material:material-icons-core:1.6.2 +androidx.compose.material:material-icons-extended-android:1.6.2 +androidx.compose.material:material-icons-extended:1.6.2 +androidx.compose.material:material-ripple-android:1.6.2 +androidx.compose.material:material-ripple:1.6.2 +androidx.compose.runtime:runtime-android:1.6.2 +androidx.compose.runtime:runtime-saveable-android:1.6.2 +androidx.compose.runtime:runtime-saveable:1.6.2 +androidx.compose.runtime:runtime:1.6.2 +androidx.compose.ui:ui-android:1.6.2 +androidx.compose.ui:ui-geometry-android:1.6.2 +androidx.compose.ui:ui-geometry:1.6.2 +androidx.compose.ui:ui-graphics-android:1.6.2 +androidx.compose.ui:ui-graphics:1.6.2 +androidx.compose.ui:ui-text-android:1.6.2 +androidx.compose.ui:ui-text:1.6.2 +androidx.compose.ui:ui-tooling-preview-android:1.6.2 +androidx.compose.ui:ui-tooling-preview:1.6.2 +androidx.compose.ui:ui-unit-android:1.6.2 +androidx.compose.ui:ui-unit:1.6.2 +androidx.compose.ui:ui-util-android:1.6.2 +androidx.compose.ui:ui-util:1.6.2 +androidx.compose.ui:ui:1.6.2 +androidx.compose:compose-bom:2024.02.01 androidx.concurrent:concurrent-futures:1.1.0 androidx.core:core-ktx:1.12.0 androidx.core:core:1.12.0 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a7cd78f75..12ac3ded3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -89,17 +89,21 @@ dependencies { implementation(projects.sync.work) implementation(libs.androidx.activity.compose) + implementation(libs.androidx.compose.material3.adaptive) + implementation(libs.androidx.compose.material3.windowSizeClass) + implementation(libs.androidx.compose.runtime.tracing) implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.splashscreen) - implementation(libs.androidx.tracing.ktx) + implementation(libs.androidx.hilt.navigation.compose) 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) + implementation(libs.androidx.tracing.ktx) implementation(libs.kotlinx.coroutines.guava) implementation(libs.coil.kt) + ksp(libs.hilt.compiler) + debugImplementation(libs.androidx.compose.ui.testManifest) debugImplementation(projects.uiTestHiltManifest) diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index f354c6fee..b9da05522 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -13,44 +13,46 @@ androidx.browser:browser:1.6.0 androidx.collection:collection-jvm:1.4.0 androidx.collection:collection-ktx:1.4.0 androidx.collection:collection:1.4.0 -androidx.compose.animation:animation-android:1.6.1 -androidx.compose.animation:animation-core-android:1.6.1 -androidx.compose.animation:animation-core:1.6.1 -androidx.compose.animation:animation:1.6.1 -androidx.compose.foundation:foundation-android:1.6.1 -androidx.compose.foundation:foundation-layout-android:1.6.1 -androidx.compose.foundation:foundation-layout:1.6.1 -androidx.compose.foundation:foundation:1.6.1 +androidx.compose.animation:animation-android:1.6.2 +androidx.compose.animation:animation-core-android:1.6.2 +androidx.compose.animation:animation-core:1.6.2 +androidx.compose.animation:animation:1.6.2 +androidx.compose.foundation:foundation-android:1.6.2 +androidx.compose.foundation:foundation-layout-android:1.6.2 +androidx.compose.foundation:foundation-layout:1.6.2 +androidx.compose.foundation:foundation:1.6.2 +androidx.compose.material3:material3-adaptive-android:1.0.0-alpha06 +androidx.compose.material3:material3-adaptive:1.0.0-alpha06 androidx.compose.material3:material3-android:1.2.0 androidx.compose.material3:material3-window-size-class-android:1.2.0 androidx.compose.material3:material3-window-size-class:1.2.0 androidx.compose.material3:material3:1.2.0 -androidx.compose.material:material-icons-core-android:1.6.1 -androidx.compose.material:material-icons-core:1.6.1 -androidx.compose.material:material-icons-extended-android:1.6.1 -androidx.compose.material:material-icons-extended:1.6.1 -androidx.compose.material:material-ripple-android:1.6.1 -androidx.compose.material:material-ripple:1.6.1 -androidx.compose.runtime:runtime-android:1.6.1 -androidx.compose.runtime:runtime-saveable-android:1.6.1 -androidx.compose.runtime:runtime-saveable:1.6.1 +androidx.compose.material:material-icons-core-android:1.6.2 +androidx.compose.material:material-icons-core:1.6.2 +androidx.compose.material:material-icons-extended-android:1.6.2 +androidx.compose.material:material-icons-extended:1.6.2 +androidx.compose.material:material-ripple-android:1.6.2 +androidx.compose.material:material-ripple:1.6.2 +androidx.compose.runtime:runtime-android:1.6.2 +androidx.compose.runtime:runtime-saveable-android:1.6.2 +androidx.compose.runtime:runtime-saveable:1.6.2 androidx.compose.runtime:runtime-tracing:1.0.0-beta01 -androidx.compose.runtime:runtime:1.6.1 -androidx.compose.ui:ui-android:1.6.1 -androidx.compose.ui:ui-geometry-android:1.6.1 -androidx.compose.ui:ui-geometry:1.6.1 -androidx.compose.ui:ui-graphics-android:1.6.1 -androidx.compose.ui:ui-graphics:1.6.1 -androidx.compose.ui:ui-text-android:1.6.1 -androidx.compose.ui:ui-text:1.6.1 -androidx.compose.ui:ui-tooling-preview-android:1.6.1 -androidx.compose.ui:ui-tooling-preview:1.6.1 -androidx.compose.ui:ui-unit-android:1.6.1 -androidx.compose.ui:ui-unit:1.6.1 -androidx.compose.ui:ui-util-android:1.6.1 -androidx.compose.ui:ui-util:1.6.1 -androidx.compose.ui:ui:1.6.1 -androidx.compose:compose-bom:2024.02.00 +androidx.compose.runtime:runtime:1.6.2 +androidx.compose.ui:ui-android:1.6.2 +androidx.compose.ui:ui-geometry-android:1.6.2 +androidx.compose.ui:ui-geometry:1.6.2 +androidx.compose.ui:ui-graphics-android:1.6.2 +androidx.compose.ui:ui-graphics:1.6.2 +androidx.compose.ui:ui-text-android:1.6.2 +androidx.compose.ui:ui-text:1.6.2 +androidx.compose.ui:ui-tooling-preview-android:1.6.2 +androidx.compose.ui:ui-tooling-preview:1.6.2 +androidx.compose.ui:ui-unit-android:1.6.2 +androidx.compose.ui:ui-unit:1.6.2 +androidx.compose.ui:ui-util-android:1.6.2 +androidx.compose.ui:ui-util:1.6.2 +androidx.compose.ui:ui:1.6.2 +androidx.compose:compose-bom:2024.02.01 androidx.concurrent:concurrent-futures:1.1.0 androidx.core:core-ktx:1.12.0 androidx.core:core-splashscreen:1.0.1 @@ -116,7 +118,8 @@ androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 -androidx.window:window:1.0.0 +androidx.window.extensions.core:core:1.0.0 +androidx.window:window:1.2.0 androidx.work:work-runtime-ktx:2.9.0 androidx.work:work-runtime:2.9.0 com.caverock:androidsvg-aar:1.4 diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index 13f11c3b9..c9cc64120 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -274,10 +274,10 @@ class NavigationTest { // Select the last topic val topic = runBlocking { - topicsRepository.getTopics().first().sortedBy(Topic::name).last().name + topicsRepository.getTopics().first().sortedBy(Topic::name).last() } - onNodeWithTag("interests:topics").performScrollToNode(hasText(topic)) - onNodeWithText(topic).performClick() + onNodeWithTag("interests:topics").performScrollToNode(hasText(topic.name)) + onNodeWithText(topic.name).performClick() // Switch tab onNodeWithText(forYou).performClick() @@ -285,8 +285,8 @@ class NavigationTest { // Come back to Interests onNodeWithText(interests).performClick() - // Verify we're not in the list of interests - onNodeWithTag("interests:topics").assertDoesNotExist() + // Verify the topic is still shown + onNodeWithTag("topic:${topic.id}").assertExists() } } } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt index 6167b0b59..39bc03de7 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt @@ -22,12 +22,11 @@ import androidx.navigation.compose.NavHost import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksScreen import com.google.samples.apps.nowinandroid.feature.foryou.navigation.FOR_YOU_ROUTE import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouScreen -import com.google.samples.apps.nowinandroid.feature.interests.navigation.interestsGraph +import com.google.samples.apps.nowinandroid.feature.interests.navigation.navigateToInterests import com.google.samples.apps.nowinandroid.feature.search.navigation.searchScreen -import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic -import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS import com.google.samples.apps.nowinandroid.ui.NiaAppState +import com.google.samples.apps.nowinandroid.ui.interests2pane.interestsListDetailScreen /** * Top-level navigation graph. Navigation is organized as explained at @@ -49,24 +48,16 @@ fun NiaNavHost( startDestination = startDestination, modifier = modifier, ) { - forYouScreen(onTopicClick = navController::navigateToTopic) + forYouScreen(onTopicClick = navController::navigateToInterests) bookmarksScreen( - onTopicClick = navController::navigateToTopic, + onTopicClick = navController::navigateToInterests, onShowSnackbar = onShowSnackbar, ) searchScreen( onBackClick = navController::popBackStack, onInterestsClick = { appState.navigateToTopLevelDestination(INTERESTS) }, - onTopicClick = navController::navigateToTopic, - ) - interestsGraph( - onTopicClick = navController::navigateToTopic, - nestedGraphs = { - topicScreen( - onBackClick = navController::popBackStack, - onTopicClick = navController::navigateToTopic, - ) - }, + onTopicClick = navController::navigateToInterests, ) + interestsListDetailScreen() } } 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 d423adfbf..b653d8910 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 @@ -39,7 +39,7 @@ import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.navigat import com.google.samples.apps.nowinandroid.feature.foryou.navigation.FOR_YOU_ROUTE import com.google.samples.apps.nowinandroid.feature.foryou.navigation.navigateToForYou import com.google.samples.apps.nowinandroid.feature.interests.navigation.INTERESTS_ROUTE -import com.google.samples.apps.nowinandroid.feature.interests.navigation.navigateToInterestsGraph +import com.google.samples.apps.nowinandroid.feature.interests.navigation.navigateToInterests import com.google.samples.apps.nowinandroid.feature.search.navigation.navigateToSearch import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.BOOKMARKS @@ -173,7 +173,7 @@ class NiaAppState( when (topLevelDestination) { FOR_YOU -> navController.navigateToForYou(topLevelNavOptions) BOOKMARKS -> navController.navigateToBookmarks(topLevelNavOptions) - INTERESTS -> navController.navigateToInterestsGraph(topLevelNavOptions) + INTERESTS -> navController.navigateToInterests(null, topLevelNavOptions) } } } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt new file mode 100644 index 000000000..d618c2d47 --- /dev/null +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt @@ -0,0 +1,35 @@ +/* + * 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.ui.interests2pane + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@HiltViewModel +class Interests2PaneViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, +) : ViewModel() { + val selectedTopicId: StateFlow = savedStateHandle.getStateFlow(TOPIC_ID_ARG, null) + + fun onTopicClick(topicId: String?) { + savedStateHandle[TOPIC_ID_ARG] = topicId + } +} diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt new file mode 100644 index 000000000..98327923f --- /dev/null +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt @@ -0,0 +1,136 @@ +/* + * 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.ui.interests2pane + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole +import androidx.compose.material3.adaptive.PaneAdaptedValue +import androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator +import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.interests.navigation.INTERESTS_ROUTE +import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG +import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ROUTE +import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic +import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen + +private const val DETAIL_PANE_NAVHOST_ROUTE = "detail_pane_route" + +fun NavGraphBuilder.interestsListDetailScreen() { + composable( + route = INTERESTS_ROUTE, + arguments = listOf( + navArgument(TOPIC_ID_ARG) { + type = NavType.StringType + defaultValue = null + nullable = true + }, + ), + ) { + InterestsListDetailScreen() + } +} + +@Composable +internal fun InterestsListDetailScreen( + viewModel: Interests2PaneViewModel = hiltViewModel(), +) { + val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle() + InterestsListDetailScreen( + selectedTopicId = selectedTopicId, + onTopicClick = viewModel::onTopicClick, + ) +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +internal fun InterestsListDetailScreen( + selectedTopicId: String?, + onTopicClick: (String) -> Unit, +) { + val listDetailNavigator = rememberListDetailPaneScaffoldNavigator() + BackHandler(listDetailNavigator.canNavigateBack()) { + listDetailNavigator.navigateBack() + } + + val nestedNavController = rememberNavController() + + fun onTopicClickShowDetailPane(topicId: String) { + onTopicClick(topicId) + nestedNavController.navigateToTopic(topicId) { + popUpTo(DETAIL_PANE_NAVHOST_ROUTE) + } + listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail) + } + + ListDetailPaneScaffold( + scaffoldState = listDetailNavigator.scaffoldState, + listPane = { + InterestsRoute( + onTopicClick = ::onTopicClickShowDetailPane, + highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(), + ) + }, + detailPane = { + NavHost( + navController = nestedNavController, + startDestination = TOPIC_ROUTE, + route = DETAIL_PANE_NAVHOST_ROUTE, + ) { + topicScreen( + showBackButton = !listDetailNavigator.isListPaneVisible(), + onBackClick = listDetailNavigator::navigateBack, + onTopicClick = ::onTopicClickShowDetailPane, + ) + composable(route = TOPIC_ROUTE) { + Box { + Text("Placeholder") + } + } + } + }, + ) + LaunchedEffect(Unit) { + if (selectedTopicId != null) { + // Initial topic ID was provided when navigating to Interests, so show its details. + onTopicClickShowDetailPane(selectedTopicId) + } + } +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +private fun ThreePaneScaffoldNavigator.isListPaneVisible(): Boolean = + scaffoldState.scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +private fun ThreePaneScaffoldNavigator.isDetailPaneVisible(): Boolean = + scaffoldState.scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index f0bca9729..ee6aaf122 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -30,4 +30,4 @@ dependencies { testImplementation(projects.core.testing) androidTestImplementation(projects.core.testing) -} \ No newline at end of file +} diff --git a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index 4f9cbcc04..1584662b8 100644 --- a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -74,7 +74,10 @@ class InterestsScreenTest { fun interestsWithTopics_whenTopicsFollowed_showFollowedAndUnfollowedTopicsWithInfo() { composeTestRule.setContent { InterestsScreen( - uiState = InterestsUiState.Interests(topics = followableTopicTestData), + uiState = InterestsUiState.Interests( + topics = followableTopicTestData, + selectedTopicId = null, + ), ) } diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt index b055a3a14..6ac0340ee 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.selected import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -49,6 +50,7 @@ fun InterestsItem( modifier: Modifier = Modifier, iconModifier: Modifier = Modifier, description: String = "", + isSelected: Boolean = false, ) { ListItem( leadingContent = { @@ -83,10 +85,16 @@ fun InterestsItem( ) }, colors = ListItemDefaults.colors( - containerColor = Color.Transparent, + containerColor = if (isSelected) { + MaterialTheme.colorScheme.surfaceVariant + } else { + Color.Transparent + }, ), modifier = modifier - .semantics(mergeDescendants = true) { /* no-op */ } + .semantics(mergeDescendants = true) { + selected = isSelected + } .clickable(enabled = true, onClick = onClick), ) } @@ -179,3 +187,21 @@ private fun InterestsCardWithEmptyDescriptionPreview() { } } } + +@Preview +@Composable +private fun InterestsCardSelectedPreview() { + NiaTheme { + Surface { + InterestsItem( + name = "Compose", + description = "", + following = true, + topicImageUrl = "", + onClick = { }, + onFollowButtonClick = { }, + isSelected = true, + ) + } + } +} diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt index 5944b8631..468550878 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt @@ -35,9 +35,10 @@ import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParame import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent @Composable -internal fun InterestsRoute( +fun InterestsRoute( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, + highlightSelectedTopic: Boolean = false, viewModel: InterestsViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -45,7 +46,11 @@ internal fun InterestsRoute( InterestsScreen( uiState = uiState, followTopic = viewModel::followTopic, - onTopicClick = onTopicClick, + onTopicClick = { + viewModel.onTopicClick(it) + onTopicClick(it) + }, + highlightSelectedTopic = highlightSelectedTopic, modifier = modifier, ) } @@ -56,6 +61,7 @@ internal fun InterestsScreen( followTopic: (String, Boolean) -> Unit, onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, + highlightSelectedTopic: Boolean = false, ) { Column( modifier = modifier, @@ -67,13 +73,17 @@ internal fun InterestsScreen( modifier = modifier, contentDesc = stringResource(id = R.string.feature_interests_loading), ) + is InterestsUiState.Interests -> TopicsTabContent( topics = uiState.topics, onTopicClick = onTopicClick, onFollowButtonClick = followTopic, + selectedTopicId = uiState.selectedTopicId, + highlightSelectedTopic = highlightSelectedTopic, modifier = modifier, ) + is InterestsUiState.Empty -> InterestsEmptyScreen() } } @@ -95,6 +105,7 @@ fun InterestsScreenPopulated( NiaBackground { InterestsScreen( uiState = InterestsUiState.Interests( + selectedTopicId = null, topics = followableTopics, ), followTopic = { _, _ -> }, diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt index 6d905a6d5..b369ac5ab 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt @@ -16,46 +16,57 @@ package com.google.samples.apps.nowinandroid.feature.interests +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.TopicSortField import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic +import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class InterestsViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, val userDataRepository: UserDataRepository, getFollowableTopics: GetFollowableTopicsUseCase, ) : ViewModel() { - val uiState: StateFlow = - getFollowableTopics(sortBy = TopicSortField.NAME).map( - InterestsUiState::Interests, - ).stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000), - initialValue = InterestsUiState.Loading, - ) + val selectedTopicId: StateFlow = savedStateHandle.getStateFlow(TOPIC_ID_ARG, null) + + val uiState: StateFlow = combine( + selectedTopicId, + getFollowableTopics(sortBy = TopicSortField.NAME), + InterestsUiState::Interests, + ).stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = InterestsUiState.Loading, + ) fun followTopic(followedTopicId: String, followed: Boolean) { viewModelScope.launch { userDataRepository.setTopicIdFollowed(followedTopicId, followed) } } + + fun onTopicClick(topicId: String?) { + savedStateHandle[TOPIC_ID_ARG] = topicId + } } sealed interface InterestsUiState { data object Loading : InterestsUiState data class Interests( + val selectedTopicId: String?, val topics: List, ) : InterestsUiState diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt index d865f5e1a..4a48645c5 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt @@ -47,6 +47,8 @@ fun TopicsTabContent( onFollowButtonClick: (String, Boolean) -> Unit, modifier: Modifier = Modifier, withBottomSpacer: Boolean = true, + selectedTopicId: String? = null, + highlightSelectedTopic: Boolean = false, ) { Box( modifier = modifier @@ -63,6 +65,7 @@ fun TopicsTabContent( topics.forEach { followableTopic -> val topicId = followableTopic.topic.id item(key = topicId) { + val isSelected = highlightSelectedTopic && topicId == selectedTopicId InterestsItem( name = followableTopic.topic.name, following = followableTopic.isFollowed, @@ -70,6 +73,7 @@ fun TopicsTabContent( topicImageUrl = followableTopic.topic.imageUrl, onClick = { onTopicClick(topicId) }, onFollowButtonClick = { onFollowButtonClick(topicId, it) }, + isSelected = isSelected, ) } } diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt index 2ad7c560b..8a0f2d130 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt @@ -19,26 +19,37 @@ package com.google.samples.apps.nowinandroid.feature.interests.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import androidx.navigation.NavType import androidx.navigation.compose.composable -import androidx.navigation.navigation +import androidx.navigation.navArgument import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute -private const val INTERESTS_GRAPH_ROUTE_PATTERN = "interests_graph" -const val INTERESTS_ROUTE = "interests_route" +const val TOPIC_ID_ARG = "topicId" +const val INTERESTS_ROUTE_BASE = "interests_route" +const val INTERESTS_ROUTE = "$INTERESTS_ROUTE_BASE?$TOPIC_ID_ARG={$TOPIC_ID_ARG}" -fun NavController.navigateToInterestsGraph(navOptions: NavOptions) = navigate(INTERESTS_GRAPH_ROUTE_PATTERN, navOptions) +fun NavController.navigateToInterests(topicId: String? = null, navOptions: NavOptions? = null) { + val route = if (topicId != null) { + "${INTERESTS_ROUTE_BASE}?${TOPIC_ID_ARG}=$topicId" + } else { + INTERESTS_ROUTE_BASE + } + navigate(route, navOptions) +} -fun NavGraphBuilder.interestsGraph( +fun NavGraphBuilder.interestsScreen( onTopicClick: (String) -> Unit, - nestedGraphs: NavGraphBuilder.() -> Unit, ) { - navigation( - route = INTERESTS_GRAPH_ROUTE_PATTERN, - startDestination = INTERESTS_ROUTE, + composable( + route = INTERESTS_ROUTE, + arguments = listOf( + navArgument(TOPIC_ID_ARG) { + defaultValue = null + nullable = true + type = NavType.StringType + }, + ), ) { - composable(route = INTERESTS_ROUTE) { - InterestsRoute(onTopicClick) - } - nestedGraphs() + InterestsRoute(onTopicClick = onTopicClick) } } diff --git a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt index c46cb7780..63d3c49b7 100644 --- a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt +++ b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.interests +import androidx.lifecycle.SavedStateHandle import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic @@ -24,6 +25,7 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserData import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState import com.google.samples.apps.nowinandroid.feature.interests.InterestsViewModel +import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -53,6 +55,7 @@ class InterestsViewModelTest { @Before fun setup() { viewModel = InterestsViewModel( + savedStateHandle = SavedStateHandle(mapOf(TOPIC_ID_ARG to testInputTopics[0].topic.id)), userDataRepository = userDataRepository, getFollowableTopics = getFollowableTopicsUseCase, ) @@ -93,7 +96,10 @@ class InterestsViewModelTest { ) assertEquals( - InterestsUiState.Interests(topics = testOutputTopics), + InterestsUiState.Interests( + topics = testOutputTopics, + selectedTopicId = testInputTopics[0].topic.id, + ), viewModel.uiState.value, ) @@ -123,7 +129,10 @@ class InterestsViewModelTest { ) assertEquals( - InterestsUiState.Interests(topics = testInputTopics), + InterestsUiState.Interests( + topics = testInputTopics, + selectedTopicId = testInputTopics[0].topic.id, + ), viewModel.uiState.value, ) diff --git a/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index b64e397ea..2b87baf9e 100644 --- a/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -55,6 +55,7 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Loading, + showBackButton = true, onBackClick = {}, onFollowClick = {}, onTopicClick = {}, @@ -75,6 +76,7 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Success(testTopic), newsUiState = NewsUiState.Loading, + showBackButton = true, onBackClick = {}, onFollowClick = {}, onTopicClick = {}, @@ -100,6 +102,7 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Success(userNewsResourcesTestData), + showBackButton = true, onBackClick = {}, onFollowClick = {}, onTopicClick = {}, @@ -123,6 +126,7 @@ class TopicScreenTest { newsUiState = NewsUiState.Success( userNewsResourcesTestData, ), + showBackButton = true, onBackClick = {}, onFollowClick = {}, onTopicClick = {}, diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt index 3f3862c2a..4402cecd4 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsTopHeight @@ -44,6 +45,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -70,6 +72,7 @@ import com.google.samples.apps.nowinandroid.feature.topic.R.string @Composable internal fun TopicRoute( + showBackButton: Boolean, onBackClick: () -> Unit, onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, @@ -82,7 +85,8 @@ internal fun TopicRoute( TopicScreen( topicUiState = topicUiState, newsUiState = newsUiState, - modifier = modifier, + modifier = modifier.testTag("topic:${viewModel.topicId}"), + showBackButton = showBackButton, onBackClick = onBackClick, onFollowClick = viewModel::followTopicToggle, onBookmarkChanged = viewModel::bookmarkNews, @@ -96,6 +100,7 @@ internal fun TopicRoute( internal fun TopicScreen( topicUiState: TopicUiState, newsUiState: NewsUiState, + showBackButton: Boolean, onBackClick: () -> Unit, onFollowClick: (Boolean) -> Unit, onTopicClick: (String) -> Unit, @@ -127,6 +132,7 @@ internal fun TopicScreen( is TopicUiState.Success -> { item { TopicToolbar( + showBackButton = showBackButton, onBackClick = onBackClick, onFollowClick = onFollowClick, uiState = topicUiState.followableTopic, @@ -270,6 +276,7 @@ private fun TopicBodyPreview() { private fun TopicToolbar( uiState: FollowableTopic, modifier: Modifier = Modifier, + showBackButton: Boolean = true, onBackClick: () -> Unit = {}, onFollowClick: (Boolean) -> Unit = {}, ) { @@ -280,13 +287,18 @@ private fun TopicToolbar( .fillMaxWidth() .padding(bottom = 32.dp), ) { - IconButton(onClick = { onBackClick() }) { - Icon( - imageVector = NiaIcons.ArrowBack, - contentDescription = stringResource( - id = com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_back, - ), - ) + if (showBackButton) { + IconButton(onClick = { onBackClick() }) { + Icon( + imageVector = NiaIcons.ArrowBack, + contentDescription = stringResource( + id = com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_back, + ), + ) + } + } else { + // Keeps the NiaFilterChip aligned to the end of the Row. + Spacer(modifier = Modifier.width(1.dp)) } val selected = uiState.isFollowed NiaFilterChip( @@ -314,6 +326,7 @@ fun TopicScreenPopulated( TopicScreen( topicUiState = TopicUiState.Success(userNewsResources[0].followableTopics[0]), newsUiState = NewsUiState.Success(userNewsResources), + showBackButton = true, onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, @@ -332,6 +345,7 @@ fun TopicScreenLoading() { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Loading, + showBackButton = true, onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt index bba46c5ab..41804b634 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt @@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting import androidx.lifecycle.SavedStateHandle import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument @@ -32,20 +33,23 @@ private val URL_CHARACTER_ENCODING = UTF_8.name() @VisibleForTesting internal const val TOPIC_ID_ARG = "topicId" +const val TOPIC_ROUTE = "topic_route" internal class TopicArgs(val topicId: String) { constructor(savedStateHandle: SavedStateHandle) : this(URLDecoder.decode(checkNotNull(savedStateHandle[TOPIC_ID_ARG]), URL_CHARACTER_ENCODING)) } -fun NavController.navigateToTopic(topicId: String) { +fun NavController.navigateToTopic(topicId: String, navOptions: NavOptionsBuilder.() -> Unit = {}) { val encodedId = URLEncoder.encode(topicId, URL_CHARACTER_ENCODING) - navigate("topic_route/$encodedId") { - launchSingleTop = true + val newRoute = "$TOPIC_ROUTE/$encodedId" + navigate(newRoute) { + navOptions() } } fun NavGraphBuilder.topicScreen( + showBackButton: Boolean, onBackClick: () -> Unit, onTopicClick: (String) -> Unit, ) { @@ -55,6 +59,10 @@ fun NavGraphBuilder.topicScreen( navArgument(TOPIC_ID_ARG) { type = NavType.StringType }, ), ) { - TopicRoute(onBackClick = onBackClick, onTopicClick = onTopicClick) + TopicRoute( + showBackButton = showBackButton, + onBackClick = onBackClick, + onTopicClick = onTopicClick, + ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b1318c7a..481494a48 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,8 +7,9 @@ androidTools = "31.3.0" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" -androidxComposeBom = "2024.02.00" +androidxComposeBom = "2024.02.01" androidxComposeCompiler = "1.5.8" +androidxComposeMaterial3Adaptive = "1.0.0-alpha06" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" @@ -70,6 +71,7 @@ androidx-compose-foundation = { group = "androidx.compose.foundation", name = "f androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-compose-material3-adaptive = { group = "androidx.compose.material3", name = "material3-adaptive", version.ref = "androidxComposeMaterial3Adaptive" } 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" } From aa15e65a7764348ddf745e013308727c25eb78b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Fri, 8 Mar 2024 10:30:32 +0100 Subject: [PATCH 49/62] Fix search flickering Change-Id: I6d17f5703186dbebcba4f931ab9692d4ab1e9f79 --- .../feature/search/SearchViewModel.kt | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt index 6dd93ceb6..7c05f81c5 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt @@ -26,11 +26,11 @@ import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRep import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase -import com.google.samples.apps.nowinandroid.core.result.Result -import com.google.samples.apps.nowinandroid.core.result.asResult +import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -61,18 +61,15 @@ class SearchViewModel @Inject constructor( flowOf(SearchResultUiState.EmptyQuery) } else { getSearchContentsUseCase(query) - .asResult() - .map { result -> - when (result) { - is Result.Success -> SearchResultUiState.Success( - topics = result.data.topics, - newsResources = result.data.newsResources, - ) - - is Result.Loading -> SearchResultUiState.Loading - is Result.Error -> SearchResultUiState.LoadFailed - } + // Not using .asResult() here, because it emits Loading state every + // time the user types a letter in the search box, which flickers the screen. + .map { data -> + SearchResultUiState.Success( + topics = data.topics, + newsResources = data.newsResources, + ) } + .catch { emit(SearchResultUiState.LoadFailed) } } } } From 4dc4ba670a09fd23eb6d920686b8efb3c4f0922c Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 14:05:57 -0800 Subject: [PATCH 50/62] Implement network module as the multiplatform module --- core/network/build.gradle.kts | 4 ++- .../core/network/assets/SampleData.kt | 2 +- .../core/network/di/DiskCacheComponent.kt | 25 --------------- .../core/network/di/ImageLoaderComponent.kt | 5 --- .../core/network/di/NetworkModule.kt | 3 +- .../network/fake/FakeNiaNetworkDataSource.kt | 11 +++---- .../network/retrofit/RetrofitNiaNetwork.kt | 4 +-- .../fake/FakeNiaNetworkDataSourceTest.kt | 3 +- .../core/network/di/FlavoredNetworkModule.kt | 32 ------------------- gradle/libs.versions.toml | 2 +- 10 files changed, 14 insertions(+), 77 deletions(-) delete mode 100644 core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt rename core/network/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt (98%) delete mode 100644 core/network/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 876dfc20d..24aa870d7 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING + plugins { alias(libs.plugins.nowinandroid.kmp.library) alias(libs.plugins.nowinandroid.android.library.jacoco) @@ -38,7 +40,7 @@ secrets { defaultPropertiesFileName = "secrets.defaults.properties" } -buildKonfig { +buildkonfig { packageName = "com.google.samples.apps.nowinandroid.core.network" defaultConfigs { buildConfigField(STRING, "BACKEND_URL", "\"https://www.example.com\"") diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt index 3c47eade9..bd02586da 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/assets/SampleData.kt @@ -4761,4 +4761,4 @@ internal val NEWS_DATA = """ ] } ] -""".trimIndent() \ No newline at end of file +""".trimIndent() diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt deleted file mode 100644 index e954315fd..000000000 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/DiskCacheComponent.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.network.di - -import coil3.disk.DiskCache -import me.tatarka.inject.annotations.Provides - -expect class DiskCacheComponent { - @Provides - internal fun newDiskCache(): DiskCache? -} \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt index cfae7527f..b0e194413 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/ImageLoaderComponent.kt @@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.core.network.di import coil3.ImageLoader import coil3.PlatformContext -import coil3.disk.DiskCache import coil3.memory.MemoryCache import coil3.request.crossfade import coil3.util.DebugLogger @@ -37,7 +36,6 @@ abstract class ImageLoaderComponent { @Provides fun provideImageLoader( context: PlatformContext, - diskCache: DiskCache?, debug: Boolean, ): ImageLoader { return ImageLoader.Builder(context) @@ -47,9 +45,6 @@ abstract class ImageLoaderComponent { .maxSizePercent(context, percent = 0.25) .build() } - .diskCache { - diskCache - } // Show a short crossfade when loading images asynchronously. .crossfade(true) // Enable logging if this is a debug build. diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index 4408885c5..dc8be8b8c 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.core.network.di +import com.google.samples.apps.nowinandroid.core.network.BuildKonfig import de.jensklingenberg.ktorfit.Ktorfit import de.jensklingenberg.ktorfit.converter.builtin.CallConverterFactory import de.jensklingenberg.ktorfit.converter.builtin.FlowConverterFactory @@ -37,7 +38,7 @@ internal abstract class NetworkModule { @Provides fun provideKtorfit(json: Json): Ktorfit = ktorfit { - baseUrl(BuildConfig.BACKEND_URL) + baseUrl(BuildKonfig.BACKEND_URL) httpClient( HttpClient { install(ContentNegotiation) { diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt index 8b4b235ff..355584433 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt @@ -18,6 +18,8 @@ package com.google.samples.apps.nowinandroid.core.network.fake import com.google.samples.apps.nowinandroid.core.di.IODispatcher import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource +import com.google.samples.apps.nowinandroid.core.network.assets.NEWS_DATA +import com.google.samples.apps.nowinandroid.core.network.assets.TOPICS_DATA import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic @@ -37,13 +39,13 @@ class FakeNiaNetworkDataSource @Inject constructor( @OptIn(ExperimentalSerializationApi::class) override suspend fun getTopics(ids: List?): List = withContext(ioDispatcher) { - networkJson.decodeFromString(TOPICS_ASSET) + networkJson.decodeFromString(TOPICS_DATA) } @OptIn(ExperimentalSerializationApi::class) override suspend fun getNewsResources(ids: List?): List = withContext(ioDispatcher) { - networkJson.decodeFromString(NEWS_ASSET) + networkJson.decodeFromString(NEWS_DATA) } override suspend fun getTopicChangeList(after: Int?): List = @@ -51,11 +53,6 @@ class FakeNiaNetworkDataSource @Inject constructor( override suspend fun getNewsResourceChangeList(after: Int?): List = getNewsResources().mapToChangeList(NetworkNewsResource::id) - - companion object { - private const val NEWS_ASSET = "news.json" - private const val TOPICS_ASSET = "topics.json" - } } /** diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index cac997563..3ae6ac79f 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -29,7 +29,7 @@ import me.tatarka.inject.annotations.Inject /** * Retrofit API declaration for NIA Network API */ -private interface RetrofitNiaNetworkApi { +internal interface RetrofitNiaNetworkApi { @GET(value = "topics") suspend fun getTopics( @Query("id") ids: List?, @@ -55,7 +55,7 @@ private interface RetrofitNiaNetworkApi { * Wrapper for data provided from the [NIA_BASE_URL] */ @Serializable -private data class NetworkResponse( +internal data class NetworkResponse( val data: T, ) diff --git a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt b/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt similarity index 98% rename from core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt rename to core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt index b64c06e52..c88755a6d 100644 --- a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt +++ b/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt @@ -24,9 +24,8 @@ import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant import kotlinx.serialization.json.Json -import org.junit.Before -import org.junit.Test import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertEquals class FakeNiaNetworkDataSourceTest { diff --git a/core/network/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt b/core/network/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt deleted file mode 100644 index bff1ca5be..000000000 --- a/core/network/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt +++ /dev/null @@ -1,32 +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.network.di - -import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource -import com.google.samples.apps.nowinandroid.core.network.retrofit.RetrofitNiaNetwork -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal interface FlavoredNetworkModule { - - @Binds - fun binds(impl: RetrofitNiaNetwork): NiaNetworkDataSource -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a8b69017e..3b70ada5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -75,7 +75,7 @@ sqldelight = "2.0.1" kotlinInject = '0.6.3' multiplatform-settings = "1.1.1" kermit = "2.0.3" -ktor = "3.0.0-beta-1" +ktor = "2.3.9" ktrofit = "1.12.0" buildKonfig = "0.15.1" From 790fc4a42edd97309221917acdbb86b29aab322a Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 14:09:02 -0800 Subject: [PATCH 51/62] Remove unused annotation --- .../nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt index 355584433..b4220c8f6 100644 --- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt +++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt @@ -24,7 +24,6 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import kotlinx.coroutines.withContext -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import me.tatarka.inject.annotations.Inject @@ -36,13 +35,11 @@ class FakeNiaNetworkDataSource @Inject constructor( private val networkJson: Json, ) : NiaNetworkDataSource { - @OptIn(ExperimentalSerializationApi::class) override suspend fun getTopics(ids: List?): List = withContext(ioDispatcher) { networkJson.decodeFromString(TOPICS_DATA) } - @OptIn(ExperimentalSerializationApi::class) override suspend fun getNewsResources(ids: List?): List = withContext(ioDispatcher) { networkJson.decodeFromString(NEWS_DATA) From 1f2b7206367910b7ba061e89551765a23dee52d9 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 14:37:31 -0800 Subject: [PATCH 52/62] Migrate the analytics module to multiplatform --- core/analytics/build.gradle.kts | 18 ++++---- .../{main => commonMain}/AndroidManifest.xml | 0 .../core/analytics/AnalyticsEvent.kt | 0 .../core/analytics/AnalyticsHelper.kt | 0 .../core/analytics/NoOpAnalyticsHelper.kt | 0 .../core/analytics/StubAnalyticsHelper.kt | 11 ++--- .../nowinandroid/core/analytics/UiHelpers.kt | 0 .../core/analytics/di}/AnalyticsModule.kt | 19 ++++----- .../core/analytics/AnalyticsModule.kt | 42 ------------------- .../core/analytics/FirebaseAnalyticsHelper.kt | 41 ------------------ gradle/libs.versions.toml | 2 +- 11 files changed, 23 insertions(+), 110 deletions(-) rename core/analytics/src/{main => commonMain}/AndroidManifest.xml (100%) rename core/analytics/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsEvent.kt (100%) rename core/analytics/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsHelper.kt (100%) rename core/analytics/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/analytics/NoOpAnalyticsHelper.kt (100%) rename core/analytics/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt (76%) rename core/analytics/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/analytics/UiHelpers.kt (100%) rename core/analytics/src/{demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics => commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/di}/AnalyticsModule.kt (57%) delete mode 100644 core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt delete mode 100644 core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts index 023574e6f..5ae18a2b7 100644 --- a/core/analytics/build.gradle.kts +++ b/core/analytics/build.gradle.kts @@ -14,18 +14,20 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.compose) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.nowinandroid.kotlin.inject) + alias(libs.plugins.jetbrains.compose) } android { namespace = "com.google.samples.apps.nowinandroid.core.analytics" } -dependencies { - implementation(libs.androidx.compose.runtime) - - prodImplementation(platform(libs.firebase.bom)) - prodImplementation(libs.firebase.analytics) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.logging) + implementation(compose.runtime) + } + } } diff --git a/core/analytics/src/main/AndroidManifest.xml b/core/analytics/src/commonMain/AndroidManifest.xml similarity index 100% rename from core/analytics/src/main/AndroidManifest.xml rename to core/analytics/src/commonMain/AndroidManifest.xml diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsEvent.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsEvent.kt similarity index 100% rename from core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsEvent.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsEvent.kt diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsHelper.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsHelper.kt similarity index 100% rename from core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsHelper.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsHelper.kt diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/NoOpAnalyticsHelper.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/NoOpAnalyticsHelper.kt similarity index 100% rename from core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/NoOpAnalyticsHelper.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/NoOpAnalyticsHelper.kt diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt similarity index 76% rename from core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt index f570be4a9..ec42eaa64 100644 --- a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt +++ b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt @@ -16,19 +16,14 @@ package com.google.samples.apps.nowinandroid.core.analytics -import android.util.Log -import javax.inject.Inject -import javax.inject.Singleton - -private const val TAG = "StubAnalyticsHelper" +import co.touchlab.kermit.Logger /** * An implementation of AnalyticsHelper just writes the events to logcat. Used in builds where no * analytics events should be sent to a backend. */ -@Singleton -internal class StubAnalyticsHelper @Inject constructor() : AnalyticsHelper { +internal class StubAnalyticsHelper : AnalyticsHelper { override fun logEvent(event: AnalyticsEvent) { - Log.d(TAG, "Received analytics event: $event") + Logger.d { "Received analytics event: $event" } } } diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/UiHelpers.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/UiHelpers.kt similarity index 100% rename from core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/UiHelpers.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/UiHelpers.kt diff --git a/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/di/AnalyticsModule.kt similarity index 57% rename from core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt rename to core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/di/AnalyticsModule.kt index 4ad6b6dc2..e61cb98ec 100644 --- a/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt +++ b/core/analytics/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/analytics/di/AnalyticsModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -14,16 +14,15 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.analytics +package com.google.samples.apps.nowinandroid.core.analytics.di -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper +import com.google.samples.apps.nowinandroid.core.analytics.StubAnalyticsHelper +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) +@Component internal abstract class AnalyticsModule { - @Binds - abstract fun bindsAnalyticsHelper(analyticsHelperImpl: StubAnalyticsHelper): AnalyticsHelper + @Provides + fun providesAnalyticsHelper(): AnalyticsHelper = StubAnalyticsHelper() } diff --git a/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt b/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt deleted file mode 100644 index 111130a79..000000000 --- a/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 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.analytics - -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.ktx.Firebase -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal abstract class AnalyticsModule { - @Binds - abstract fun bindsAnalyticsHelper(analyticsHelperImpl: FirebaseAnalyticsHelper): AnalyticsHelper - - companion object { - @Provides - @Singleton - fun provideFirebaseAnalytics(): FirebaseAnalytics { - return Firebase.analytics - } - } -} diff --git a/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt b/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt deleted file mode 100644 index 5a4b7f362..000000000 --- a/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2023 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.analytics - -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.logEvent -import javax.inject.Inject - -/** - * Implementation of `AnalyticsHelper` which logs events to a Firebase backend. - */ -internal class FirebaseAnalyticsHelper @Inject constructor( - private val firebaseAnalytics: FirebaseAnalytics, -) : AnalyticsHelper { - - override fun logEvent(event: AnalyticsEvent) { - firebaseAnalytics.logEvent(event.type) { - for (extra in event.extras) { - // Truncate parameter keys and values according to firebase maximum length values. - param( - key = extra.key.take(40), - value = extra.value.take(100), - ) - } - } - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3b70ada5e..54d8403c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ androidx-espresso-core = "3.5.1" androidx-material = "1.11.0" androidx-test-junit = "1.1.5" compose = "1.6.0" -compose-plugin = "1.6.0-alpha01" +compose-plugin = "1.6.0" junit = "4.13.2" sqldelight = "2.0.1" kotlinInject = '0.6.3' From 61b2b5e43606666931a429d7dc11d59f6f57477d Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 15:04:20 -0800 Subject: [PATCH 53/62] Make notification module as the multiplatform library --- core/notifications/build.gradle.kts | 21 +++++++------ .../{main => androidMain}/AndroidManifest.xml | 0 .../core/notifications/SystemTrayNotifier.kt | 11 +++---- .../res/values/strings.xml | 0 .../core/notifications/NoOpNotifier.kt | 3 +- .../core/notifications/Notifier.kt | 0 .../notifications/di}/NotificationsModule.kt | 20 +++++------- .../core/notifications/NotificationsModule.kt | 31 ------------------- 8 files changed, 25 insertions(+), 61 deletions(-) rename core/notifications/src/{main => androidMain}/AndroidManifest.xml (100%) rename core/notifications/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt (95%) rename core/notifications/src/{main => androidMain}/res/values/strings.xml (100%) rename core/notifications/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt (91%) rename core/notifications/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/notifications/Notifier.kt (100%) rename core/notifications/src/{demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications => commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di}/NotificationsModule.kt (61%) delete mode 100644 core/notifications/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt diff --git a/core/notifications/build.gradle.kts b/core/notifications/build.gradle.kts index 92871b72b..40a616470 100644 --- a/core/notifications/build.gradle.kts +++ b/core/notifications/build.gradle.kts @@ -14,19 +14,22 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.nowinandroid.kotlin.inject) } android { namespace = "com.google.samples.apps.nowinandroid.core.notifications" } -dependencies { - api(projects.core.model) - - implementation(projects.core.common) - - compileOnly(platform(libs.androidx.compose.bom)) - compileOnly(libs.androidx.compose.runtime) +kotlin { + sourceSets { + commonMain.dependencies { + api(projects.core.model) + implementation(projects.core.common) + } + androidMain.dependencies { + implementation(libs.androidx.core.ktx) + } + } } diff --git a/core/notifications/src/main/AndroidManifest.xml b/core/notifications/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/notifications/src/main/AndroidManifest.xml rename to core/notifications/src/androidMain/AndroidManifest.xml diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt b/core/notifications/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt similarity index 95% rename from core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt rename to core/notifications/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt index 1c9e7ab63..735fcc122 100644 --- a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt +++ b/core/notifications/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -33,9 +33,6 @@ import androidx.core.app.NotificationCompat.InboxStyle import androidx.core.app.NotificationManagerCompat import androidx.core.net.toUri import com.google.samples.apps.nowinandroid.core.model.data.NewsResource -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject -import javax.inject.Singleton private const val MAX_NUM_NOTIFICATIONS = 5 private const val TARGET_ACTIVITY_NAME = "com.google.samples.apps.nowinandroid.MainActivity" @@ -49,9 +46,9 @@ private const val FOR_YOU_PATH = "foryou" /** * Implementation of [Notifier] that displays notifications in the system tray. */ -@Singleton -internal class SystemTrayNotifier @Inject constructor( - @ApplicationContext private val context: Context, + +internal class SystemTrayNotifier constructor( + private val context: Context, ) : Notifier { override fun postNewsNotifications( diff --git a/core/notifications/src/main/res/values/strings.xml b/core/notifications/src/androidMain/res/values/strings.xml similarity index 100% rename from core/notifications/src/main/res/values/strings.xml rename to core/notifications/src/androidMain/res/values/strings.xml diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt similarity index 91% rename from core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt rename to core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt index 863c1a662..93a0029df 100644 --- a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt +++ b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt @@ -17,11 +17,10 @@ package com.google.samples.apps.nowinandroid.core.notifications import com.google.samples.apps.nowinandroid.core.model.data.NewsResource -import javax.inject.Inject /** * Implementation of [Notifier] which does nothing. Useful for tests and previews. */ -internal class NoOpNotifier @Inject constructor() : Notifier { +internal class NoOpNotifier : Notifier { override fun postNewsNotifications(newsResources: List) = Unit } diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/Notifier.kt b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/Notifier.kt similarity index 100% rename from core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/Notifier.kt rename to core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/Notifier.kt diff --git a/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt similarity index 61% rename from core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt rename to core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt index 99ba10fa7..1cf85ab09 100644 --- a/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt +++ b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -14,18 +14,14 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.notifications +package com.google.samples.apps.nowinandroid.core.notifications.di -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import com.google.samples.apps.nowinandroid.core.notifications.NoOpNotifier +import com.google.samples.apps.nowinandroid.core.notifications.Notifier +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) internal abstract class NotificationsModule { - @Binds - abstract fun bindNotifier( - notifier: NoOpNotifier, - ): Notifier + + @Provides + internal fun provideNotifier(): Notifier = NoOpNotifier() } diff --git a/core/notifications/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt b/core/notifications/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt deleted file mode 100644 index c2e1f76ca..000000000 --- a/core/notifications/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2023 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.notifications - -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal abstract class NotificationsModule { - @Binds - abstract fun bindNotifier( - notifier: SystemTrayNotifier, - ): Notifier -} From 6916bfbce4d01b71e18c5305a2a7403e9e445d4b Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 15:04:52 -0800 Subject: [PATCH 54/62] Move AndroidManifest.xml to the correct folder --- core/network/src/androidMain/{kotlin => }/AndroidManifest.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/network/src/androidMain/{kotlin => }/AndroidManifest.xml (100%) diff --git a/core/network/src/androidMain/kotlin/AndroidManifest.xml b/core/network/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/network/src/androidMain/kotlin/AndroidManifest.xml rename to core/network/src/androidMain/AndroidManifest.xml From f616e11d0ac77d79f7b765855091c0784caf5481 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 8 Mar 2024 15:05:34 -0800 Subject: [PATCH 55/62] Add comments for temporary usage --- .../nowinandroid/core/notifications/di/NotificationsModule.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt index 1cf85ab09..a663f869b 100644 --- a/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt +++ b/core/notifications/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/notifications/di/NotificationsModule.kt @@ -22,6 +22,7 @@ import me.tatarka.inject.annotations.Provides internal abstract class NotificationsModule { + // TODO: Add a real implementation of Notifier foe each platform @Provides internal fun provideNotifier(): Notifier = NoOpNotifier() } From a7a7736426e3f056af6dba0f8c83b2cc4b223f53 Mon Sep 17 00:00:00 2001 From: mlykotom Date: Mon, 11 Mar 2024 14:52:51 +0000 Subject: [PATCH 56/62] =?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 --- .../dependencies/releaseRuntimeClasspath.txt | 43 ++++++++++--------- .../prodReleaseRuntimeClasspath.txt | 24 +++++------ 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index ca4bf99a3..bfb84ef6a 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 androidx.annotation:annotation-experimental:1.4.0 -androidx.annotation:annotation-jvm:1.7.0 -androidx.annotation:annotation:1.7.0 +androidx.annotation:annotation-jvm:1.7.1 +androidx.annotation:annotation:1.7.1 androidx.appcompat:appcompat-resources:1.6.1 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 @@ -53,19 +53,20 @@ androidx.core:core:1.12.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.0.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.6 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment:1.5.1 androidx.interpolator:interpolator:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.2 -androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata:2.6.2 -androidx.lifecycle:lifecycle-process:2.6.2 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -androidx.lifecycle:lifecycle-runtime:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2 -androidx.lifecycle:lifecycle-viewmodel:2.6.2 +androidx.lifecycle:lifecycle-common-java8:2.7.0 +androidx.lifecycle:lifecycle-common:2.7.0 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0 +androidx.lifecycle:lifecycle-livedata-core:2.7.0 +androidx.lifecycle:lifecycle-livedata:2.7.0 +androidx.lifecycle:lifecycle-process:2.7.0 +androidx.lifecycle:lifecycle-runtime-ktx:2.7.0 +androidx.lifecycle:lifecycle-runtime:2.7.0 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0 +androidx.lifecycle:lifecycle-viewmodel:2.7.0 androidx.loader:loader:1.0.0 androidx.metrics:metrics-performance:1.0.0-alpha04 androidx.profileinstaller:profileinstaller:1.3.1 @@ -86,16 +87,16 @@ 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 -com.squareup.okio:okio:3.6.0 -io.coil-kt:coil-base:2.5.0 -io.coil-kt:coil-compose-base:2.5.0 -io.coil-kt:coil-compose:2.5.0 -io.coil-kt:coil:2.5.0 +com.squareup.okio:okio-jvm:3.8.0 +com.squareup.okio:okio:3.8.0 +io.coil-kt:coil-base:2.6.0 +io.coil-kt:coil-compose-base:2.6.0 +io.coil-kt:coil-compose:2.6.0 +io.coil-kt:coil:2.6.0 javax.inject:javax.inject:1 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-jdk7:1.9.0 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 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 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index b9da05522..a25a227fa 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 androidx.annotation:annotation-experimental:1.4.0 -androidx.annotation:annotation-jvm:1.7.0 -androidx.annotation:annotation:1.7.0 +androidx.annotation:annotation-jvm:1.7.1 +androidx.annotation:annotation:1.7.1 androidx.appcompat:appcompat-resources:1.6.1 androidx.appcompat:appcompat:1.6.1 androidx.arch.core:core-common:2.2.0 @@ -68,7 +68,7 @@ androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.6 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment:1.5.1 androidx.hilt:hilt-common:1.1.0 androidx.hilt:hilt-navigation-compose:1.0.0 @@ -181,21 +181,21 @@ com.google.protobuf:protobuf-kotlin-lite:3.25.2 com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.6.0 -com.squareup.okio:okio:3.6.0 +com.squareup.okio:okio-jvm:3.8.0 +com.squareup.okio:okio:3.8.0 com.squareup.retrofit2:retrofit:2.9.0 -io.coil-kt:coil-base:2.5.0 -io.coil-kt:coil-compose-base:2.5.0 -io.coil-kt:coil-compose:2.5.0 -io.coil-kt:coil-svg:2.5.0 -io.coil-kt:coil:2.5.0 +io.coil-kt:coil-base:2.6.0 +io.coil-kt:coil-compose-base:2.6.0 +io.coil-kt:coil-compose:2.6.0 +io.coil-kt:coil-svg:2.6.0 +io.coil-kt:coil:2.6.0 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.22 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 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 From 3fc345230435e8970138028c59be9a08d3667704 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 11 Mar 2024 14:30:29 -0700 Subject: [PATCH 57/62] WIP: Make :core:data as the multiplatform module --- core/data/build.gradle.kts | 39 +++++++++----- .../{main => androidMain}/AndroidManifest.xml | 0 .../di/PlatformDependentDataModule.android.kt | 35 +++++++++++++ .../util/ConnectivityManagerNetworkMonitor.kt | 10 ++-- .../data/util/TimeZoneBroadcastMonitor.kt} | 28 +++------- .../nowinandroid/core/data/SyncUtilities.kt | 11 ++-- .../nowinandroid/core/data/di/DataModule.kt | 51 +++++++------------ .../data/di/PlatformDependentDataModule.kt | 29 +++++++++++ .../di/UserNewsResourceRepositoryModule.kt | 15 +++--- .../core/data/model/NewsResource.kt | 0 .../core/data/model/RecentSearchQuery.kt | 0 .../nowinandroid/core/data/model/Topic.kt | 0 .../data/repository/AnalyticsExtensions.kt | 0 .../CompositeUserNewsResourceRepository.kt | 5 +- .../DefaultRecentSearchRepository.kt | 5 +- .../DefaultSearchContentsRepository.kt | 11 ++-- .../core/data/repository/NewsRepository.kt | 0 .../repository/OfflineFirstNewsRepository.kt | 5 +- .../OfflineFirstTopicsRepository.kt | 5 +- .../OfflineFirstUserDataRepository.kt | 7 ++- .../data/repository/RecentSearchRepository.kt | 0 .../repository/SearchContentsRepository.kt | 0 .../core/data/repository/TopicsRepository.kt | 0 .../data/repository/UserDataRepository.kt | 0 .../repository/UserNewsResourceRepository.kt | 0 .../core/data/util/NetworkMonitor.kt | 0 .../core/data/util/SyncManager.kt | 0 .../core/data/util/TimeZoneMonitor.kt | 28 ++++++++++ ...CompositeUserNewsResourceRepositoryTest.kt | 2 +- .../core/data/UserNewsResourceTest.kt | 6 +-- .../core/data/model/NetworkEntityKtTest.kt | 2 +- .../OfflineFirstNewsRepositoryTest.kt | 0 .../OfflineFirstTopicsRepositoryTest.kt | 8 ++- .../OfflineFirstUserDataRepositoryTest.kt | 8 ++- .../core/data/repository/TestSynchronizer.kt | 0 .../data/testdoubles/TestNewsResourceDao.kt | 0 .../testdoubles/TestNiaNetworkDataSource.kt | 0 .../core/data/testdoubles/TestTopicDao.kt | 0 .../model/PopulatedNewsResourceKtTest.kt | 0 .../di/PlatformDependentDataModule.jvm.kt | 48 +++++++++++++++++ .../di/PlatformDependentDataModule.native.kt | 48 +++++++++++++++++ .../datastore/NiaPreferencesDataSource.kt | 45 ++++++++++++---- 42 files changed, 321 insertions(+), 130 deletions(-) rename core/data/src/{main => androidMain}/AndroidManifest.xml (100%) create mode 100644 core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt rename core/data/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt (92%) rename core/data/src/{main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt => androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt} (80%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt (96%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt (61%) create mode 100644 core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt (78%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt (96%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt (94%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt (91%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt (98%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt (96%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt (95%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/util/NetworkMonitor.kt (100%) rename core/data/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt (100%) create mode 100644 core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt (99%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt (97%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt (99%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt (98%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt (98%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt (100%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt (100%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt (100%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestTopicDao.kt (100%) rename core/data/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt (100%) create mode 100644 core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt create mode 100644 core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 142637ff9..0d21ea909 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -14,9 +14,9 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.nowinandroid.kotlin.inject) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) id("kotlinx-serialization") } @@ -30,17 +30,30 @@ android { } } -dependencies { - api(projects.core.common) - api(projects.core.database) - api(projects.core.datastore) - api(projects.core.network) +kotlin { + sourceSets { + commonMain.dependencies { + api(projects.core.common) + api(projects.core.database) + api(projects.core.datastore) + api(projects.core.network) - implementation(projects.core.analytics) - implementation(projects.core.notifications) + implementation(projects.core.analytics) + implementation(projects.core.notifications) + } + + commonTest.dependencies { + implementation(libs.kotlin.test) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.kotlinx.serialization.json) +// implementation(projects.core.datastoreTest) +// implementation(projects.core.testing) + } - testImplementation(libs.kotlinx.coroutines.test) - testImplementation(libs.kotlinx.serialization.json) - testImplementation(projects.core.datastoreTest) - testImplementation(projects.core.testing) + androidMain.dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.tracing.ktx) + } + } } + diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/data/src/main/AndroidManifest.xml rename to core/data/src/androidMain/AndroidManifest.xml diff --git a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt new file mode 100644 index 000000000..87f01db64 --- /dev/null +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.android.kt @@ -0,0 +1,35 @@ +/* + * 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.di + +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides + +@Component +internal actual abstract class PlatformDependentDataModule { + @Provides + internal actual fun bindsNetworkMonitor(): NetworkMonitor { + TODO() + } + + @Provides + internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { + TODO() + } +} diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt similarity index 92% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt rename to core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt index e9599c555..bb653dd40 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -26,15 +26,15 @@ import android.net.NetworkRequest.Builder import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.core.content.getSystemService -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -internal class ConnectivityManagerNetworkMonitor @Inject constructor( - @ApplicationContext private val context: Context, +@Inject +internal class ConnectivityManagerNetworkMonitor( + private val context: Context, ) : NetworkMonitor { override val isOnline: Flow = callbackFlow { val connectivityManager = context.getSystemService() diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt similarity index 80% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt rename to core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt index 031bc9388..27fb21eda 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneBroadcastMonitor.kt @@ -23,14 +23,9 @@ import android.content.IntentFilter import android.os.Build.VERSION import android.os.Build.VERSION_CODES 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 -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineDispatcher +import com.google.samples.apps.nowinandroid.core.di.IODispatcher 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 @@ -40,23 +35,14 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import kotlinx.datetime.TimeZone import kotlinx.datetime.toKotlinTimeZone +import me.tatarka.inject.annotations.Inject 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 currentTimeZone: Flow -} - -@Singleton -internal class TimeZoneBroadcastMonitor @Inject constructor( - @ApplicationContext private val context: Context, - @ApplicationScope appScope: CoroutineScope, - @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, +@Inject +internal class TimeZoneBroadcastMonitor( + private val context: Context, + appScope: CoroutineScope, + private val ioDispatcher: IODispatcher, ) : TimeZoneMonitor { override val currentTimeZone: SharedFlow = diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt similarity index 96% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt index 5d069dbaf..9ee73b11a 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.core.data -import android.util.Log +import co.touchlab.kermit.Logger import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import kotlinx.coroutines.flow.Flow @@ -59,11 +59,10 @@ private suspend fun suspendRunCatching(block: suspend () -> T): Result = } catch (cancellationException: CancellationException) { throw cancellationException } catch (exception: Exception) { - Log.i( - "suspendRunCatching", - "Failed to evaluate a suspendRunCatchingBlock. Returning failure Result", - exception, - ) + Logger.i { + "suspendRunCatching" + + "Failed to evaluate a suspendRunCatchingBlock. Returning failure Result" + } Result.failure(exception) } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt similarity index 61% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt index fa4bde8b8..496f08e4d 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt @@ -26,49 +26,34 @@ 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.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 -import dagger.hilt.components.SingletonComponent +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) +@Component abstract class DataModule { - @Binds - internal abstract fun bindsTopicRepository( + @Provides + internal fun bindsTopicRepository( topicsRepository: OfflineFirstTopicsRepository, - ): TopicsRepository + ): TopicsRepository = topicsRepository - @Binds - internal abstract fun bindsNewsResourceRepository( + @Provides + internal fun bindsNewsResourceRepository( newsRepository: OfflineFirstNewsRepository, - ): NewsRepository + ): NewsRepository = newsRepository - @Binds - internal abstract fun bindsUserDataRepository( + @Provides + internal fun bindsUserDataRepository( userDataRepository: OfflineFirstUserDataRepository, - ): UserDataRepository + ): UserDataRepository = userDataRepository - @Binds - internal abstract fun bindsRecentSearchRepository( + @Provides + internal fun bindsRecentSearchRepository( recentSearchRepository: DefaultRecentSearchRepository, - ): RecentSearchRepository + ): RecentSearchRepository = recentSearchRepository - @Binds - internal abstract fun bindsSearchContentsRepository( + @Provides + internal fun bindsSearchContentsRepository( searchContentsRepository: DefaultSearchContentsRepository, - ): SearchContentsRepository - - @Binds - internal abstract fun bindsNetworkMonitor( - networkMonitor: ConnectivityManagerNetworkMonitor, - ): NetworkMonitor - - @Binds - internal abstract fun binds(impl: TimeZoneBroadcastMonitor): TimeZoneMonitor + ): SearchContentsRepository = searchContentsRepository } diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt new file mode 100644 index 000000000..d4c6409b7 --- /dev/null +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.kt @@ -0,0 +1,29 @@ +/* + * 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.di + +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import me.tatarka.inject.annotations.Provides + +internal expect abstract class PlatformDependentDataModule { + @Provides + internal fun bindsNetworkMonitor(): NetworkMonitor + + @Provides + internal fun bindsTimeZoneMonitor(): TimeZoneMonitor +} diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt similarity index 78% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt index 7f4e27b41..59293fa9d 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt @@ -18,16 +18,13 @@ package com.google.samples.apps.nowinandroid.core.data.di import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) -internal interface UserNewsResourceRepositoryModule { - @Binds +@Component +internal abstract class UserNewsResourceRepositoryModule { + @Provides fun bindsUserNewsResourceRepository( userDataRepository: CompositeUserNewsResourceRepository, - ): UserNewsResourceRepository + ): UserNewsResourceRepository = userDataRepository } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt similarity index 96% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt index 64e02e7d9..a02967bf2 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt @@ -24,13 +24,14 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject /** * Implements a [UserNewsResourceRepository] by combining a [NewsRepository] with a * [UserDataRepository]. */ -class CompositeUserNewsResourceRepository @Inject constructor( +@Inject +class CompositeUserNewsResourceRepository( val newsRepository: NewsRepository, val userDataRepository: UserDataRepository, ) : UserNewsResourceRepository { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt similarity index 94% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt index 32239362d..62b8691da 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt @@ -23,9 +23,10 @@ import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQuer import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.datetime.Clock -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -internal class DefaultRecentSearchRepository @Inject constructor( +@Inject +internal class DefaultRecentSearchRepository( private val recentSearchQueryDao: RecentSearchQueryDao, ) : RecentSearchRepository { override suspend fun insertOrReplaceRecentSearch(searchQuery: String) { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt similarity index 91% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt index 3bacb8a14..7b6b48e8d 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt @@ -23,10 +23,8 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity +import com.google.samples.apps.nowinandroid.core.di.IODispatcher import com.google.samples.apps.nowinandroid.core.model.data.SearchResult -import com.google.samples.apps.nowinandroid.core.network.Dispatcher -import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -34,14 +32,15 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -internal class DefaultSearchContentsRepository @Inject constructor( +@Inject +internal class DefaultSearchContentsRepository( private val newsResourceDao: NewsResourceDao, private val newsResourceFtsDao: NewsResourceFtsDao, private val topicDao: TopicDao, private val topicFtsDao: TopicFtsDao, - @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, + private val ioDispatcher: IODispatcher, ) : SearchContentsRepository { override suspend fun populateFtsData() { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt similarity index 98% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt index d33c904e5..02c75c04a 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt @@ -35,7 +35,7 @@ import com.google.samples.apps.nowinandroid.core.notifications.Notifier import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject // Heuristic value to optimize for serialization and deserialization cost on client and server // for each news resource batch. @@ -45,7 +45,8 @@ private const val SYNC_BATCH_SIZE = 40 * Disk storage backed implementation of the [NewsRepository]. * Reads are exclusively from local storage to support offline access. */ -internal class OfflineFirstNewsRepository @Inject constructor( +@Inject +internal class OfflineFirstNewsRepository( private val niaPreferencesDataSource: NiaPreferencesDataSource, private val newsResourceDao: NewsResourceDao, private val topicDao: TopicDao, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt similarity index 96% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt index 5c8cecce8..864be34e6 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt @@ -28,13 +28,14 @@ import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject /** * Disk storage backed implementation of the [TopicsRepository]. * Reads are exclusively from local storage to support offline access. */ -internal class OfflineFirstTopicsRepository @Inject constructor( +@Inject +internal class OfflineFirstTopicsRepository( private val topicDao: TopicDao, private val network: NiaNetworkDataSource, ) : TopicsRepository { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt similarity index 95% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt index c0b1bcc33..f5c65440e 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt @@ -16,16 +16,16 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import androidx.annotation.VisibleForTesting import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData import kotlinx.coroutines.flow.Flow -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -internal class OfflineFirstUserDataRepository @Inject constructor( +@Inject +internal class OfflineFirstUserDataRepository( private val niaPreferencesDataSource: NiaPreferencesDataSource, private val analyticsHelper: AnalyticsHelper, ) : UserDataRepository { @@ -33,7 +33,6 @@ internal class OfflineFirstUserDataRepository @Inject constructor( override val userData: Flow = niaPreferencesDataSource.userData - @VisibleForTesting override suspend fun setFollowedTopicIds(followedTopicIds: Set) = niaPreferencesDataSource.setFollowedTopicIds(followedTopicIds) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/NetworkMonitor.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/NetworkMonitor.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/NetworkMonitor.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/NetworkMonitor.kt diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt similarity index 100% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt rename to core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt new file mode 100644 index 000000000..92611d6a5 --- /dev/null +++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -0,0 +1,28 @@ +/* + * 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 kotlinx.coroutines.flow.Flow +import kotlinx.datetime.TimeZone + +/** + * 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 currentTimeZone: Flow +} diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt similarity index 99% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt index 05811f4be..f4786485c 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt +++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt @@ -27,7 +27,7 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserDat import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals class CompositeUserNewsResourceRepositoryTest { diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt similarity index 97% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt index c7dfd99d0..0623a4632 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt +++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt @@ -24,9 +24,9 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Clock -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class UserNewsResourceTest { diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt similarity index 99% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt index 7dd251a99..da64b9ee8 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt +++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt @@ -20,7 +20,7 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResour import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import kotlinx.datetime.Instant -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals class NetworkEntityKtTest { diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt similarity index 98% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt index 3bd314eae..215caeb9b 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt +++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt @@ -32,10 +32,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertEquals class OfflineFirstTopicsRepositoryTest { @@ -55,7 +53,7 @@ class OfflineFirstTopicsRepositoryTest { @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() - @Before + @BeforeTest fun setup() { topicDao = TestTopicDao() network = TestNiaNetworkDataSource() diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt similarity index 98% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt index 27e86f2f4..be4f12e56 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt +++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt @@ -27,10 +27,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -48,7 +46,7 @@ class OfflineFirstUserDataRepositoryTest { @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() - @Before + @BeforeTest fun setup() { niaPreferencesDataSource = NiaPreferencesDataSource( tmpFolder.testUserPreferencesDataStore(testScope), diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestTopicDao.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestTopicDao.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestTopicDao.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestTopicDao.kt diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt similarity index 100% rename from core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt rename to core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt diff --git a/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt b/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt new file mode 100644 index 000000000..df3acd1a3 --- /dev/null +++ b/core/data/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.jvm.kt @@ -0,0 +1,48 @@ +/* + * 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.di + +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.datetime.TimeZone +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides + +/** + * JVM module that provides platform dependent data + * Leave empty for now + */ +@Component +internal actual abstract class PlatformDependentDataModule { + @Provides + internal actual fun bindsNetworkMonitor(): NetworkMonitor { + return object : NetworkMonitor { + override val isOnline: Flow + get() = flowOf(true) + } + } + + @Provides + internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { + return object : TimeZoneMonitor { + override val currentTimeZone: Flow + get() = flowOf(TimeZone.UTC) + } + } +} diff --git a/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt b/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt new file mode 100644 index 000000000..1600ad86b --- /dev/null +++ b/core/data/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/data/di/PlatformDependentDataModule.native.kt @@ -0,0 +1,48 @@ +/* + * 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.di + +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.datetime.TimeZone +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides + +/** + * Native module that provides platform dependent data + * Leave empty for now + */ +@Component +internal actual abstract class PlatformDependentDataModule { + @Provides + internal actual fun bindsNetworkMonitor(): NetworkMonitor { + return object : NetworkMonitor { + override val isOnline: Flow + get() = flowOf(true) + } + } + + @Provides + internal actual fun bindsTimeZoneMonitor(): TimeZoneMonitor { + return object : TimeZoneMonitor { + override val currentTimeZone: Flow + get() = flowOf(TimeZone.UTC) + } + } +} diff --git a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 078af8243..59ac0433d 100644 --- a/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -19,12 +19,15 @@ package com.google.samples.apps.nowinandroid.core.datastore import com.google.samples.apps.nowinandroid.core.di.IODispatcher import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.russhwolf.settings.ExperimentalSettingsApi import com.russhwolf.settings.Settings import com.russhwolf.settings.serialization.decodeValue import com.russhwolf.settings.serialization.decodeValueOrNull import com.russhwolf.settings.serialization.encodeValue +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi @@ -37,7 +40,7 @@ class NiaPreferencesDataSource( ) { // FlowSettings did not support JS, use a workaround instead // https://github.com/russhwolf/multiplatform-settings/issues/139 - val userData = MutableStateFlow( + private val _userData = MutableStateFlow( settings.decodeValue( key = USER_DATA_KEY, serializer = UserPreferences.serializer(), @@ -48,12 +51,24 @@ class NiaPreferencesDataSource( ), ) + val userData: Flow = _userData.map { + UserData( + bookmarkedNewsResources = it.bookmarkedNewsResourceIds, + viewedNewsResources = it.viewedNewsResourceIds, + followedTopics = it.followedTopicIds, + themeBrand = it.themeBrand.toThemeBrand(), + darkThemeConfig = it.darkThemeConfig.toDarkThemeConfig(), + useDynamicColor = it.useDynamicColor, + shouldHideOnboarding = it.shouldHideOnboarding, + ) + } + suspend fun setFollowedTopicIds(topicIds: Set) = withContext(dispatcher) { val preference = settings.getUserPreference() .copy(followedTopicIds = topicIds) .updateShouldHideOnboardingIfNecessary() settings.putUserPreference(preference) - userData.value = preference + _userData.value = preference } suspend fun setTopicIdFollowed(topicId: String, followed: Boolean) = withContext(dispatcher) { @@ -68,28 +83,28 @@ class NiaPreferencesDataSource( ) .updateShouldHideOnboardingIfNecessary() settings.putUserPreference(newPreference) - userData.value = newPreference + _userData.value = newPreference } suspend fun setThemeBrand(themeBrand: ThemeBrand) = withContext(dispatcher) { val newPreference = settings.getUserPreference() .copy(themeBrand = themeBrand.toThemeBrandProto()) settings.putUserPreference(newPreference) - userData.value = newPreference + _userData.value = newPreference } suspend fun setDynamicColorPreference(useDynamicColor: Boolean) = withContext(dispatcher) { val newPreference = settings.getUserPreference() .copy(useDynamicColor = useDynamicColor) settings.putUserPreference(newPreference) - userData.value = newPreference + _userData.value = newPreference } suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) = withContext(dispatcher) { val newPreference = settings.getUserPreference() .copy(darkThemeConfig = darkThemeConfig.toDarkThemeConfigProto()) settings.putUserPreference(newPreference) - userData.value = newPreference + _userData.value = newPreference } suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) = @@ -104,7 +119,7 @@ class NiaPreferencesDataSource( }, ) settings.putUserPreference(newPreferences) - userData.value = newPreferences + _userData.value = newPreferences } suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { @@ -123,7 +138,7 @@ class NiaPreferencesDataSource( }, ) settings.putUserPreference(newPreferences) - userData.value = newPreferences + _userData.value = newPreferences } suspend fun getChangeListVersions(): ChangeListVersions = withContext(dispatcher) { @@ -151,14 +166,14 @@ class NiaPreferencesDataSource( newsResourceChangeListVersion = updatedChangeListVersions.newsResourceVersion, ) settings.putUserPreference(updatedPreference) - userData.value = updatedPreference + _userData.value = updatedPreference } suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) = withContext(dispatcher) { val newPreference = settings.getUserPreference() .copy(shouldHideOnboarding = shouldHideOnboarding) settings.putUserPreference(newPreference) - userData.value = newPreference + _userData.value = newPreference } } @@ -212,3 +227,13 @@ private fun DarkThemeConfig.toDarkThemeConfigProto(): DarkThemeConfigProto { DarkThemeConfig.LIGHT -> DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT } } + +private fun DarkThemeConfigProto.toDarkThemeConfig(): DarkThemeConfig { + return when (this) { + DarkThemeConfigProto.DARK_THEME_CONFIG_UNSPECIFIED, + DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM, + -> DarkThemeConfig.FOLLOW_SYSTEM + DarkThemeConfigProto.DARK_THEME_CONFIG_DARK -> DarkThemeConfig.DARK + DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT -> DarkThemeConfig.LIGHT + } +} From 0dce05406c8c5a045ac3d56bee6e24f4958159ed Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 11 Mar 2024 16:31:38 -0700 Subject: [PATCH 58/62] WIP: Make :core:designsystem as the multiplatform module --- core/designsystem/build.gradle.kts | 45 ++++++++++++------ .../core/designsystem/ThemeTest.kt | 0 .../{main => androidMain}/AndroidManifest.xml | 0 ...re_designsystem_ic_placeholder_default.xml | 0 .../core/designsystem/component/Background.kt | 17 +++---- .../core/designsystem/component/Button.kt | 0 .../core/designsystem/component/Chip.kt | 2 +- .../component/DynamicAsyncImage.kt | 9 ++-- .../core/designsystem/component/IconButton.kt | 0 .../designsystem/component/LoadingWheel.kt | 0 .../core/designsystem/component/Navigation.kt | 0 .../core/designsystem/component/Tabs.kt | 0 .../core/designsystem/component/Tag.kt | 0 .../core/designsystem/component/TopAppBar.kt | 0 .../core/designsystem/component/ViewToggle.kt | 0 .../component/scrollbar/AppScrollbars.kt | 4 -- .../scrollbar/LazyScrollbarUtilities.kt | 0 .../component/scrollbar/Scrollbar.kt | 1 + .../component/scrollbar/ScrollbarExt.kt | 0 .../component/scrollbar/ThumbExt.kt | 0 .../core/designsystem/icon/NiaIcons.kt | 0 .../core/designsystem/theme/Background.kt | 0 .../core/designsystem/theme/Color.kt | 0 .../core/designsystem/theme/Gradient.kt | 0 .../core/designsystem/theme/Theme.kt | 3 -- .../core/designsystem/theme/Tint.kt | 0 .../core/designsystem/theme/Type.kt | 0 .../designsystem/BackgroundScreenshotTests.kt | 0 .../designsystem/ButtonScreenshotTests.kt | 0 .../designsystem/FilterChipScreenshotTests.kt | 0 .../designsystem/IconButtonScreenshotTests.kt | 0 .../LoadingWheelScreenshotTests.kt | 0 .../designsystem/NavigationScreenshotTests.kt | 0 .../core/designsystem/TabsScreenshotTests.kt | 0 .../core/designsystem/TagScreenshotTests.kt | 0 .../designsystem/TopAppBarScreenshotTests.kt | 0 ...ackground_dark_androidTheme_notDynamic.png | Bin .../Background_dark_defaultTheme_dynamic.png | Bin ...ackground_dark_defaultTheme_notDynamic.png | Bin ...ckground_light_androidTheme_notDynamic.png | Bin .../Background_light_defaultTheme_dynamic.png | Bin ...ckground_light_defaultTheme_notDynamic.png | Bin ...ackground_dark_androidTheme_notDynamic.png | Bin ...ntBackground_dark_defaultTheme_dynamic.png | Bin ...ackground_dark_defaultTheme_notDynamic.png | Bin ...ckground_light_androidTheme_notDynamic.png | Bin ...tBackground_light_defaultTheme_dynamic.png | Bin ...ckground_light_defaultTheme_notDynamic.png | Bin ...nLeadingIcon_dark_defaultTheme_dynamic.png | Bin ...adingIcon_dark_defaultTheme_notDynamic.png | Bin ...LeadingIcon_light_defaultTheme_dynamic.png | Bin ...dingIcon_light_defaultTheme_notDynamic.png | Bin .../Button_dark_androidTheme_notDynamic.png | Bin .../Button_dark_defaultTheme_dynamic.png | Bin .../Button_dark_defaultTheme_notDynamic.png | Bin .../Button_light_androidTheme_notDynamic.png | Bin .../Button_light_defaultTheme_dynamic.png | Bin .../Button_light_defaultTheme_notDynamic.png | Bin ...ineButton_dark_androidTheme_notDynamic.png | Bin ...utlineButton_dark_defaultTheme_dynamic.png | Bin ...ineButton_dark_defaultTheme_notDynamic.png | Bin ...neButton_light_androidTheme_notDynamic.png | Bin ...tlineButton_light_defaultTheme_dynamic.png | Bin ...neButton_light_defaultTheme_notDynamic.png | Bin ...pSelected_dark_androidTheme_notDynamic.png | Bin ...ChipSelected_dark_defaultTheme_dynamic.png | Bin ...pSelected_dark_defaultTheme_notDynamic.png | Bin ...Selected_light_androidTheme_notDynamic.png | Bin ...hipSelected_light_defaultTheme_dynamic.png | Bin ...Selected_light_defaultTheme_notDynamic.png | Bin ...ilterChip_dark_androidTheme_notDynamic.png | Bin .../FilterChip_dark_defaultTheme_dynamic.png | Bin ...ilterChip_dark_defaultTheme_notDynamic.png | Bin .../FilterChip/FilterChip_fontScale2.png | Bin ...lterChip_light_androidTheme_notDynamic.png | Bin .../FilterChip_light_defaultTheme_dynamic.png | Bin ...lterChip_light_defaultTheme_notDynamic.png | Bin ...Unchecked_dark_androidTheme_notDynamic.png | Bin ...tonUnchecked_dark_defaultTheme_dynamic.png | Bin ...Unchecked_dark_defaultTheme_notDynamic.png | Bin ...nchecked_light_androidTheme_notDynamic.png | Bin ...onUnchecked_light_defaultTheme_dynamic.png | Bin ...nchecked_light_defaultTheme_notDynamic.png | Bin ...conButton_dark_androidTheme_notDynamic.png | Bin .../IconButton_dark_defaultTheme_dynamic.png | Bin ...conButton_dark_defaultTheme_notDynamic.png | Bin ...onButton_light_androidTheme_notDynamic.png | Bin .../IconButton_light_defaultTheme_dynamic.png | Bin ...onButton_light_defaultTheme_notDynamic.png | Bin .../LoadingWheel_animation_1000.png | Bin .../LoadingWheel_animation_115.png | Bin .../LoadingWheel_animation_20.png | Bin .../LoadingWheel_animation_724.png | Bin ...dingWheel_dark_androidTheme_notDynamic.png | Bin ...LoadingWheel_dark_defaultTheme_dynamic.png | Bin ...dingWheel_dark_defaultTheme_notDynamic.png | Bin ...ingWheel_light_androidTheme_notDynamic.png | Bin ...oadingWheel_light_defaultTheme_dynamic.png | Bin ...ingWheel_light_defaultTheme_notDynamic.png | Bin ...dingWheel_dark_androidTheme_notDynamic.png | Bin ...LoadingWheel_dark_defaultTheme_dynamic.png | Bin ...dingWheel_dark_defaultTheme_notDynamic.png | Bin ...ingWheel_light_androidTheme_notDynamic.png | Bin ...oadingWheel_light_defaultTheme_dynamic.png | Bin ...ingWheel_light_defaultTheme_notDynamic.png | Bin ...avigation_dark_androidTheme_notDynamic.png | Bin .../Navigation_dark_defaultTheme_dynamic.png | Bin ...avigation_dark_defaultTheme_notDynamic.png | Bin .../Navigation/Navigation_fontScale2.png | Bin ...vigation_light_androidTheme_notDynamic.png | Bin .../Navigation_light_defaultTheme_dynamic.png | Bin ...vigation_light_defaultTheme_notDynamic.png | Bin .../Tabs_dark_androidTheme_notDynamic.png | Bin .../Tabs/Tabs_dark_defaultTheme_dynamic.png | Bin .../Tabs_dark_defaultTheme_notDynamic.png | Bin .../screenshots/Tabs/Tabs_fontScale2.png | Bin .../Tabs_light_androidTheme_notDynamic.png | Bin .../Tabs/Tabs_light_defaultTheme_dynamic.png | Bin .../Tabs_light_defaultTheme_notDynamic.png | Bin .../Tag/Tag_dark_androidTheme_notDynamic.png | Bin .../Tag/Tag_dark_defaultTheme_dynamic.png | Bin .../Tag/Tag_dark_defaultTheme_notDynamic.png | Bin .../screenshots/Tag/Tag_fontScale2.png | Bin .../Tag/Tag_light_androidTheme_notDynamic.png | Bin .../Tag/Tag_light_defaultTheme_dynamic.png | Bin .../Tag/Tag_light_defaultTheme_notDynamic.png | Bin ...TopAppBar_dark_androidTheme_notDynamic.png | Bin .../TopAppBar_dark_defaultTheme_dynamic.png | Bin ...TopAppBar_dark_defaultTheme_notDynamic.png | Bin .../TopAppBar/TopAppBar_fontScale2.png | Bin ...opAppBar_light_androidTheme_notDynamic.png | Bin .../TopAppBar_light_defaultTheme_dynamic.png | Bin ...opAppBar_light_defaultTheme_notDynamic.png | Bin core/testing/build.gradle.kts | 38 ++++++++------- .../{main => androidMain}/AndroidManifest.xml | 0 .../GrantPostNotificationsPermissionRule.kt | 0 .../core/testing/NiaTestRunner.kt | 0 .../testing/data/FollowableTopicTestData.kt | 0 .../testing/data/NewsResourcesTestData.kt | 0 .../core/testing/data/TopicsTestData.kt | 0 .../testing/data/UserNewsResourcesTestData.kt | 0 .../core/testing/di/TestDispatcherModule.kt | 0 .../core/testing/di/TestDispatchersModule.kt | 0 .../testing/notifications/TestNotifier.kt | 0 .../testing/repository/TestNewsRepository.kt | 0 .../repository/TestRecentSearchRepository.kt | 0 .../TestSearchContentsRepository.kt | 0 .../repository/TestTopicsRepository.kt | 0 .../repository/TestUserDataRepository.kt | 0 .../core/testing/util/MainDispatcherRule.kt | 0 .../core/testing/util/TestAnalyticsHelper.kt | 0 .../core/testing/util/TestNetworkMonitor.kt | 0 .../core/testing/util/TestSyncManager.kt | 0 .../core/testing/util/TestTimeZoneMonitor.kt | 0 gradle.properties | 5 +- 155 files changed, 72 insertions(+), 52 deletions(-) rename core/designsystem/src/{androidTest => androidInstrumentedTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt (100%) rename core/designsystem/src/{main => androidMain}/AndroidManifest.xml (100%) rename core/designsystem/src/{main => androidMain}/res/drawable/core_designsystem_ic_placeholder_default.xml (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt (92%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt (98%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt (93%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt (97%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt (99%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Background.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Gradient.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt (98%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Tint.kt (100%) rename core/designsystem/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/Background_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/Button_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_fontScale2.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_animation_1000.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_animation_115.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_animation_20.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_animation_724.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_fontScale2.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_fontScale2.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_fontScale2.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_fontScale2.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{test => commonTest}/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png (100%) rename core/testing/src/{main => androidMain}/AndroidManifest.xml (100%) rename core/testing/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/core/rules/GrantPostNotificationsPermissionRule.kt (100%) rename core/testing/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/NiaTestRunner.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/notifications/TestNotifier.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestAnalyticsHelper.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestNetworkMonitor.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestSyncManager.kt (100%) rename core/testing/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt (100%) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index f08bae2fc..59862be9a 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -14,8 +14,8 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.compose) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.jetbrains.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.roborazzi) } @@ -27,26 +27,41 @@ android { namespace = "com.google.samples.apps.nowinandroid.core.designsystem" } +kotlin { + sourceSets { + androidMain.dependencies { + implementation(libs.compose.ui.tooling.preview) + implementation(libs.androidx.activity.compose) + } + androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(projects.core.testing) + } + commonMain.dependencies { + implementation(libs.coil.compose) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.material3) + implementation(compose.materialIconsExtended) + implementation(compose.ui) + implementation(compose.uiUtil) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + + } + } +} dependencies { lintPublish(projects.lint) - api(libs.androidx.compose.foundation) - api(libs.androidx.compose.foundation.layout) - api(libs.androidx.compose.material.iconsExtended) - api(libs.androidx.compose.material3) - api(libs.androidx.compose.runtime) - api(libs.androidx.compose.ui.util) - - implementation(libs.coil.compose) - testImplementation(libs.androidx.compose.ui.test) testImplementation(libs.accompanist.testharness) - 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) - androidTestImplementation(projects.core.testing) } diff --git a/core/designsystem/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt b/core/designsystem/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt similarity index 100% rename from core/designsystem/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt rename to core/designsystem/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt diff --git a/core/designsystem/src/main/AndroidManifest.xml b/core/designsystem/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/designsystem/src/main/AndroidManifest.xml rename to core/designsystem/src/androidMain/AndroidManifest.xml diff --git a/core/designsystem/src/main/res/drawable/core_designsystem_ic_placeholder_default.xml b/core/designsystem/src/androidMain/res/drawable/core_designsystem_ic_placeholder_default.xml similarity index 100% rename from core/designsystem/src/main/res/drawable/core_designsystem_ic_placeholder_default.xml rename to core/designsystem/src/androidMain/res/drawable/core_designsystem_ic_placeholder_default.xml diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt similarity index 92% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt index 9ef52dd76..e25fd08f3 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Background.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component -import android.content.res.Configuration import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size @@ -31,13 +30,14 @@ import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalBackgroundTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import org.jetbrains.compose.ui.tooling.preview.Preview +import kotlin.math.PI import kotlin.math.tan /** @@ -95,14 +95,14 @@ fun NiaGradientBackground( .drawWithCache { // Compute the start and end coordinates such that the gradients are angled 11.06 // degrees off the vertical axis + // Math.toRadians is replaced by deg / 180.0 * PI val offset = size.height * tan( - Math - .toRadians(11.06) + (11.06) / 180.0 * PI .toFloat(), ) - val start = Offset(size.width / 2 + offset / 2, 0f) - val end = Offset(size.width / 2 - offset / 2, size.height) + val start = Offset((size.width / 2 + offset / 2).toFloat(), 0f) + val end = Offset((size.width / 2 - offset / 2).toFloat(), size.height) // Create the top gradient that fades out after the halfway point vertically val topGradient = Brush.linearGradient( @@ -143,8 +143,9 @@ fun NiaGradientBackground( * Multipreview annotation that represents light and dark themes. Add this annotation to a * composable to render the both themes. */ -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme") -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme") +//@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme") +//@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme") +@Preview annotation class ThemePreviews @ThemePreviews diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt similarity index 98% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt index 9497bd92d..26ac80633 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import org.jetbrains.compose.ui.tooling.preview.Preview /** * Now in Android filter chip with included leading checked icon as well as text content slot. @@ -43,7 +44,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme * @param label The text label content. */ @Composable -@OptIn(ExperimentalMaterial3Api::class) fun NiaFilterChip( selected: Boolean, onSelectedChange: (Boolean) -> Unit, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt similarity index 93% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt index 1557cac06..2c936cf13 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt @@ -35,12 +35,13 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import coil.compose.AsyncImagePainter.State.Error -import coil.compose.AsyncImagePainter.State.Loading -import coil.compose.rememberAsyncImagePainter +import coil3.compose.AsyncImage +import coil3.compose.AsyncImagePainter.State.Error +import coil3.compose.AsyncImagePainter.State.Loading +import coil3.compose.rememberAsyncImagePainter import com.google.samples.apps.nowinandroid.core.designsystem.R import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme +import org.jetbrains.compose.resources.painterResource /** * A wrapper around [AsyncImage] which determines the colorFilter based on the theme diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/LoadingWheel.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tabs.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt similarity index 97% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt index 1086e280b..9ce7b3a60 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar -import android.annotation.SuppressLint import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Spring import androidx.compose.animation.core.SpringSpec @@ -164,9 +163,6 @@ private fun ScrollableState.DecorativeScrollbarThumb( ) } -// TODO: This lint is removed in 1.6 as the recommendation has changed -// remove when project is upgraded -@SuppressLint("ComposableModifierFactory") @Composable private fun Modifier.scrollThumb( scrollableState: ScrollableState, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt similarity index 99% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt index 002f36b31..fe8a9c605 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.util.unpackFloat2 import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout +import kotlin.jvm.JvmInline import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/icon/NiaIcons.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Background.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Background.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Background.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Background.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Gradient.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Gradient.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Gradient.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Gradient.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt similarity index 98% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt index 582f88473..b2d2d7bca 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt @@ -17,8 +17,6 @@ package com.google.samples.apps.nowinandroid.core.designsystem.theme import android.os.Build -import androidx.annotation.ChecksSdkIntAtLeast -import androidx.annotation.VisibleForTesting import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme @@ -35,7 +33,6 @@ import androidx.compose.ui.unit.dp /** * Light default theme color scheme */ -@VisibleForTesting val LightDefaultColorScheme = lightColorScheme( primary = Purple40, onPrimary = Color.White, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Tint.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Tint.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Tint.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Tint.kt diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt similarity index 100% rename from core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt rename to core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Type.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt b/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt similarity index 100% rename from core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt rename to core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_fontScale2.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_fontScale2.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_fontScale2.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_fontScale2.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_fontScale2.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_fontScale2.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_fontScale2.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_fontScale2.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_fontScale2.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_fontScale2.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png b/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png rename to core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts index 02729ceff..2b7083286 100644 --- a/core/testing/build.gradle.kts +++ b/core/testing/build.gradle.kts @@ -14,7 +14,8 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.nowinandroid.kotlin.inject) alias(libs.plugins.nowinandroid.android.library.compose) alias(libs.plugins.nowinandroid.android.hilt) } @@ -23,20 +24,25 @@ android { namespace = "com.google.samples.apps.nowinandroid.core.testing" } -dependencies { - api(kotlin("test")) - api(libs.androidx.compose.ui.test) - api(projects.core.analytics) - api(projects.core.data) - api(projects.core.model) - api(projects.core.notifications) +kotlin { + sourceSets { + commonMain.dependencies { + api(kotlin("test")) +// api(libs.androidx.compose.ui.test) + api(projects.core.analytics) + api(projects.core.data) + api(projects.core.model) + api(projects.core.notifications) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.kotlinx.datetime) + implementation(projects.core.common) + implementation(projects.core.designsystem) + } - debugApi(libs.androidx.compose.ui.testManifest) - - implementation(libs.androidx.test.rules) - implementation(libs.hilt.android.testing) - implementation(libs.kotlinx.coroutines.test) - implementation(libs.kotlinx.datetime) - implementation(projects.core.common) - implementation(projects.core.designsystem) + androidMain.dependencies { + api(libs.androidx.compose.ui.testManifest) + implementation(libs.androidx.test.rules) + implementation(libs.hilt.android.testing) + } + } } diff --git a/core/testing/src/main/AndroidManifest.xml b/core/testing/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/testing/src/main/AndroidManifest.xml rename to core/testing/src/androidMain/AndroidManifest.xml diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/rules/GrantPostNotificationsPermissionRule.kt b/core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/rules/GrantPostNotificationsPermissionRule.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/rules/GrantPostNotificationsPermissionRule.kt rename to core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/rules/GrantPostNotificationsPermissionRule.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/NiaTestRunner.kt b/core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/NiaTestRunner.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/NiaTestRunner.kt rename to core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/NiaTestRunner.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/notifications/TestNotifier.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/notifications/TestNotifier.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/notifications/TestNotifier.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/notifications/TestNotifier.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestAnalyticsHelper.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestAnalyticsHelper.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestAnalyticsHelper.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestAnalyticsHelper.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestNetworkMonitor.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestNetworkMonitor.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestNetworkMonitor.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestNetworkMonitor.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestSyncManager.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestSyncManager.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestSyncManager.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestSyncManager.kt diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt similarity index 100% rename from core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt rename to core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/TestTimeZoneMonitor.kt diff --git a/gradle.properties b/gradle.properties index 82b326eab..87ec125b4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -41,4 +41,7 @@ android.defaults.buildfeatures.resvalues=false android.defaults.buildfeatures.shaders=false # Suppress: The following Kotlin/Native targets cannot be built on this machine and are disabled -kotlin.native.ignoreDisabledTargets=true \ No newline at end of file +kotlin.native.ignoreDisabledTargets=true + +# Suppress: ERROR: Compose targets '[macos]' are experimental and may have bugs! +org.jetbrains.compose.experimental.macos.enabled=true From 3d179d6757b48aca824864c186567cea0dd917e7 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 12 Mar 2024 13:09:14 -0700 Subject: [PATCH 59/62] Add test dependencies in the KmpLibraryConventionPlugin --- .../src/main/kotlin/KmpLibraryConventionPlugin.kt | 7 +++++++ core/common/build.gradle.kts | 5 ----- core/data/build.gradle.kts | 1 - core/database/build.gradle.kts | 4 ---- core/datastore/build.gradle.kts | 2 -- core/designsystem/build.gradle.kts | 4 ---- core/model/build.gradle.kts | 3 --- core/network/build.gradle.kts | 4 ---- 8 files changed, 7 insertions(+), 23 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt index 7710ff73a..bb8adccdd 100644 --- a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -19,9 +19,11 @@ import com.google.samples.apps.nowinandroid.configureFlavors import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform +import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies class KmpLibraryConventionPlugin: Plugin { override fun apply(target: Project) { @@ -38,6 +40,11 @@ class KmpLibraryConventionPlugin: Plugin { // so resources inside ":core:module1" must be prefixed with "core_module1_" resourcePrefix = path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_").lowercase() + "_" } + dependencies { + add("commonTestImplementation", libs.findLibrary("kotlin.test").get()) + add("commonTestImplementation", libs.findLibrary("turbine").get()) + add("commonTestImplementation", libs.findLibrary("kotlinx.coroutines.test").get()) + } } } } diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 56a6fd958..008fcc02c 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -29,10 +29,5 @@ kotlin { api(libs.logging) implementation(libs.kotlinx.coroutines.core) } - commonTest.dependencies { - implementation(libs.kotlin.test) - implementation(libs.turbine) - implementation(libs.kotlinx.coroutines.test) - } } } diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 0d21ea909..9ce32bc8a 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -43,7 +43,6 @@ kotlin { } commonTest.dependencies { - implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.serialization.json) // implementation(projects.core.datastoreTest) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index faf1fc3c3..f7eda81de 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -57,10 +57,6 @@ kotlin { // implementation(npm("sql.js", "1.8.0")) // implementation(devNpm("copy-webpack-plugin", "9.1.0")) // } - commonTest.dependencies { - implementation(libs.kotlin.test) - implementation(libs.kotlinx.coroutines.test) - } } } diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index 883b16025..ffa4331da 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -45,8 +45,6 @@ kotlin { implementation(projects.core.datastoreProto) } commonTest.dependencies { - implementation(libs.kotlin.test) - implementation(libs.kotlinx.coroutines.test) implementation(libs.multiplatform.settings.test) } } diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 59862be9a..9dff86db8 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -49,10 +49,6 @@ kotlin { implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) } - commonTest.dependencies { - implementation(libs.kotlin.test) - - } } } dependencies { diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 0cba90bc7..75a15d512 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -27,8 +27,5 @@ kotlin { commonMain.dependencies { api(libs.kotlinx.datetime) } - commonTest.dependencies { - implementation(libs.kotlin.test) - } } } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 24aa870d7..fd690441a 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -66,10 +66,6 @@ kotlin { implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktorfit.lib) } - commonTest.dependencies { - implementation(libs.kotlin.test) - implementation(libs.kotlinx.coroutines.test) - } androidMain.dependencies { implementation(libs.ktor.client.android) } From 20a8d5c9aff6f9659fafcdf077d994dbff154194 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 12 Mar 2024 13:23:59 -0700 Subject: [PATCH 60/62] Fix warnings in Theme.kt --- .../core/designsystem/theme/Theme.kt | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt index b2d2d7bca..205713879 100644 --- a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt @@ -16,18 +16,14 @@ package com.google.samples.apps.nowinandroid.core.designsystem.theme -import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp /** @@ -64,7 +60,6 @@ val LightDefaultColorScheme = lightColorScheme( /** * Dark default theme color scheme */ -@VisibleForTesting val DarkDefaultColorScheme = darkColorScheme( primary = Purple80, onPrimary = Purple20, @@ -96,7 +91,6 @@ val DarkDefaultColorScheme = darkColorScheme( /** * Light Android theme color scheme */ -@VisibleForTesting val LightAndroidColorScheme = lightColorScheme( primary = Green40, onPrimary = Color.White, @@ -128,7 +122,6 @@ val LightAndroidColorScheme = lightColorScheme( /** * Dark Android theme color scheme */ -@VisibleForTesting val DarkAndroidColorScheme = darkColorScheme( primary = Green80, onPrimary = Green20, @@ -196,10 +189,10 @@ fun NiaTheme( // Color scheme val colorScheme = when { androidTheme -> if (darkTheme) DarkAndroidColorScheme else LightAndroidColorScheme - !disableDynamicTheming && supportsDynamicTheming() -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } + // Workaround for dynamic theming support in Compose Multiplatform +// !disableDynamicTheming && supportsDynamicTheming() -> { +// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) +// } else -> if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme } @@ -243,5 +236,7 @@ fun NiaTheme( } } -@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) -fun supportsDynamicTheming() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S +//@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) +//fun supportsDynamicTheming() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S +// A workaround for the dynamic theming support in Compose Multiplatform +fun supportsDynamicTheming() = false From bc5d12aef2811708d120dea97848d288baed2cf2 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 12 Mar 2024 13:48:41 -0700 Subject: [PATCH 61/62] Fix errors in the designsystem module --- .../designsystem/component/DynamicAsyncImage.kt | 15 ++++++++------- .../core/designsystem/component/TopAppBar.kt | 12 +++++------- .../core/designsystem/component/ViewToggle.kt | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt index 2c936cf13..c0e88b81b 100644 --- a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt @@ -30,18 +30,15 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color.Companion.Unspecified import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import coil3.ImageLoader import coil3.compose.AsyncImage import coil3.compose.AsyncImagePainter.State.Error import coil3.compose.AsyncImagePainter.State.Loading import coil3.compose.rememberAsyncImagePainter -import com.google.samples.apps.nowinandroid.core.designsystem.R import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme -import org.jetbrains.compose.resources.painterResource /** * A wrapper around [AsyncImage] which determines the colorFilter based on the theme @@ -49,19 +46,22 @@ import org.jetbrains.compose.resources.painterResource @Composable fun DynamicAsyncImage( imageUrl: String, + imageLoader: ImageLoader, contentDescription: String?, modifier: Modifier = Modifier, - placeholder: Painter = painterResource(R.drawable.core_designsystem_ic_placeholder_default), + //TODO Use Compose resources to present a placeholder +// placeholder: Painter = painterResource(R.drawable.core_designsystem_ic_placeholder_default), ) { val iconTint = LocalTintTheme.current.iconTint var isLoading by remember { mutableStateOf(true) } var isError by remember { mutableStateOf(false) } - val imageLoader = rememberAsyncImagePainter( + val asyncImagePainter = rememberAsyncImagePainter( model = imageUrl, onState = { state -> isLoading = state is Loading isError = state is Error }, + imageLoader = imageLoader, ) val isLocalInspection = LocalInspectionMode.current Box( @@ -79,7 +79,8 @@ fun DynamicAsyncImage( } Image( contentScale = ContentScale.Crop, - painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder, + painter = asyncImagePainter, +// painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder, contentDescription = contentDescription, colorFilter = if (iconTint != Unspecified) ColorFilter.tint(iconTint) else null, ) diff --git a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt index f85c65677..074dc0025 100644 --- a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/TopAppBar.kt @@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component -import androidx.annotation.StringRes import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -31,15 +30,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import org.jetbrains.compose.ui.tooling.preview.Preview @OptIn(ExperimentalMaterial3Api::class) @Composable fun NiaTopAppBar( - @StringRes titleRes: Int, + title: String, navigationIcon: ImageVector, navigationIconContentDescription: String, actionIcon: ImageVector, @@ -50,7 +48,7 @@ fun NiaTopAppBar( onActionClick: () -> Unit = {}, ) { CenterAlignedTopAppBar( - title = { Text(text = stringResource(id = titleRes)) }, + title = { Text(text = title) }, navigationIcon = { IconButton(onClick = onNavigationClick) { Icon( @@ -75,12 +73,12 @@ fun NiaTopAppBar( } @OptIn(ExperimentalMaterial3Api::class) -@Preview("Top App Bar") +@Preview @Composable private fun NiaTopAppBarPreview() { NiaTheme { NiaTopAppBar( - titleRes = android.R.string.untitled, + title = "Untitled", navigationIcon = NiaIcons.Search, navigationIconContentDescription = "Navigation icon", actionIcon = NiaIcons.MoreVert, diff --git a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt index d368c46d7..3f9eb99cf 100644 --- a/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt +++ b/core/designsystem/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/ViewToggle.kt @@ -29,10 +29,10 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import org.jetbrains.compose.ui.tooling.preview.Preview /** * Now in Android view toggle button with included trailing icon as well as compact and expanded From 1aa6375d26aee736cb550690845e96597ab13ee2 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 12 Mar 2024 14:13:05 -0700 Subject: [PATCH 62/62] Fix compilation errors in tests in the designsystem module --- core/designsystem/build.gradle.kts | 17 ++++++----- .../designsystem/BackgroundScreenshotTests.kt | 2 +- .../designsystem/ButtonScreenshotTests.kt | 2 +- .../designsystem/FilterChipScreenshotTests.kt | 2 +- .../designsystem/IconButtonScreenshotTests.kt | 2 +- .../LoadingWheelScreenshotTests.kt | 2 +- .../designsystem/NavigationScreenshotTests.kt | 2 +- .../core/designsystem/TabsScreenshotTests.kt | 2 +- .../core/designsystem/TagScreenshotTests.kt | 2 +- .../designsystem/TopAppBarScreenshotTests.kt | 6 ++-- ...ackground_dark_androidTheme_notDynamic.png | Bin .../Background_dark_defaultTheme_dynamic.png | Bin ...ackground_dark_defaultTheme_notDynamic.png | Bin ...ckground_light_androidTheme_notDynamic.png | Bin .../Background_light_defaultTheme_dynamic.png | Bin ...ckground_light_defaultTheme_notDynamic.png | Bin ...ackground_dark_androidTheme_notDynamic.png | Bin ...ntBackground_dark_defaultTheme_dynamic.png | Bin ...ackground_dark_defaultTheme_notDynamic.png | Bin ...ckground_light_androidTheme_notDynamic.png | Bin ...tBackground_light_defaultTheme_dynamic.png | Bin ...ckground_light_defaultTheme_notDynamic.png | Bin ...nLeadingIcon_dark_defaultTheme_dynamic.png | Bin ...adingIcon_dark_defaultTheme_notDynamic.png | Bin ...LeadingIcon_light_defaultTheme_dynamic.png | Bin ...dingIcon_light_defaultTheme_notDynamic.png | Bin .../Button_dark_androidTheme_notDynamic.png | Bin .../Button_dark_defaultTheme_dynamic.png | Bin .../Button_dark_defaultTheme_notDynamic.png | Bin .../Button_light_androidTheme_notDynamic.png | Bin .../Button_light_defaultTheme_dynamic.png | Bin .../Button_light_defaultTheme_notDynamic.png | Bin ...ineButton_dark_androidTheme_notDynamic.png | Bin ...utlineButton_dark_defaultTheme_dynamic.png | Bin ...ineButton_dark_defaultTheme_notDynamic.png | Bin ...neButton_light_androidTheme_notDynamic.png | Bin ...tlineButton_light_defaultTheme_dynamic.png | Bin ...neButton_light_defaultTheme_notDynamic.png | Bin ...pSelected_dark_androidTheme_notDynamic.png | Bin ...ChipSelected_dark_defaultTheme_dynamic.png | Bin ...pSelected_dark_defaultTheme_notDynamic.png | Bin ...Selected_light_androidTheme_notDynamic.png | Bin ...hipSelected_light_defaultTheme_dynamic.png | Bin ...Selected_light_defaultTheme_notDynamic.png | Bin ...ilterChip_dark_androidTheme_notDynamic.png | Bin .../FilterChip_dark_defaultTheme_dynamic.png | Bin ...ilterChip_dark_defaultTheme_notDynamic.png | Bin .../FilterChip/FilterChip_fontScale2.png | Bin ...lterChip_light_androidTheme_notDynamic.png | Bin .../FilterChip_light_defaultTheme_dynamic.png | Bin ...lterChip_light_defaultTheme_notDynamic.png | Bin ...Unchecked_dark_androidTheme_notDynamic.png | Bin ...tonUnchecked_dark_defaultTheme_dynamic.png | Bin ...Unchecked_dark_defaultTheme_notDynamic.png | Bin ...nchecked_light_androidTheme_notDynamic.png | Bin ...onUnchecked_light_defaultTheme_dynamic.png | Bin ...nchecked_light_defaultTheme_notDynamic.png | Bin ...conButton_dark_androidTheme_notDynamic.png | Bin .../IconButton_dark_defaultTheme_dynamic.png | Bin ...conButton_dark_defaultTheme_notDynamic.png | Bin ...onButton_light_androidTheme_notDynamic.png | Bin .../IconButton_light_defaultTheme_dynamic.png | Bin ...onButton_light_defaultTheme_notDynamic.png | Bin .../LoadingWheel_animation_1000.png | Bin .../LoadingWheel_animation_115.png | Bin .../LoadingWheel_animation_20.png | Bin .../LoadingWheel_animation_724.png | Bin ...dingWheel_dark_androidTheme_notDynamic.png | Bin ...LoadingWheel_dark_defaultTheme_dynamic.png | Bin ...dingWheel_dark_defaultTheme_notDynamic.png | Bin ...ingWheel_light_androidTheme_notDynamic.png | Bin ...oadingWheel_light_defaultTheme_dynamic.png | Bin ...ingWheel_light_defaultTheme_notDynamic.png | Bin ...dingWheel_dark_androidTheme_notDynamic.png | Bin ...LoadingWheel_dark_defaultTheme_dynamic.png | Bin ...dingWheel_dark_defaultTheme_notDynamic.png | Bin ...ingWheel_light_androidTheme_notDynamic.png | Bin ...oadingWheel_light_defaultTheme_dynamic.png | Bin ...ingWheel_light_defaultTheme_notDynamic.png | Bin ...avigation_dark_androidTheme_notDynamic.png | Bin .../Navigation_dark_defaultTheme_dynamic.png | Bin ...avigation_dark_defaultTheme_notDynamic.png | Bin .../Navigation/Navigation_fontScale2.png | Bin ...vigation_light_androidTheme_notDynamic.png | Bin .../Navigation_light_defaultTheme_dynamic.png | Bin ...vigation_light_defaultTheme_notDynamic.png | Bin .../Tabs_dark_androidTheme_notDynamic.png | Bin .../Tabs/Tabs_dark_defaultTheme_dynamic.png | Bin .../Tabs_dark_defaultTheme_notDynamic.png | Bin .../screenshots/Tabs/Tabs_fontScale2.png | Bin .../Tabs_light_androidTheme_notDynamic.png | Bin .../Tabs/Tabs_light_defaultTheme_dynamic.png | Bin .../Tabs_light_defaultTheme_notDynamic.png | Bin .../Tag/Tag_dark_androidTheme_notDynamic.png | Bin .../Tag/Tag_dark_defaultTheme_dynamic.png | Bin .../Tag/Tag_dark_defaultTheme_notDynamic.png | Bin .../screenshots/Tag/Tag_fontScale2.png | Bin .../Tag/Tag_light_androidTheme_notDynamic.png | Bin .../Tag/Tag_light_defaultTheme_dynamic.png | Bin .../Tag/Tag_light_defaultTheme_notDynamic.png | Bin ...TopAppBar_dark_androidTheme_notDynamic.png | Bin .../TopAppBar_dark_defaultTheme_dynamic.png | Bin ...TopAppBar_dark_defaultTheme_notDynamic.png | Bin .../TopAppBar/TopAppBar_fontScale2.png | Bin ...opAppBar_light_androidTheme_notDynamic.png | Bin .../TopAppBar_light_defaultTheme_dynamic.png | Bin ...opAppBar_light_defaultTheme_notDynamic.png | Bin .../core/testing/util/MainDispatcherRule.kt | 0 .../core/testing/di/TestDispatcherModule.kt | 17 +++++------ .../core/testing/di/TestDispatchersModule.kt | 28 ++++-------------- .../TestSearchContentsRepository.kt | 5 +--- 111 files changed, 35 insertions(+), 54 deletions(-) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt (97%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt (98%) rename core/designsystem/src/{commonTest => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt (95%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/Background_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/Button_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_fontScale2.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_animation_1000.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_animation_115.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_animation_20.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_animation_724.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_fontScale2.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_fontScale2.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_fontScale2.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_fontScale2.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png (100%) rename core/designsystem/src/{commonTest => androidUnitTest}/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png (100%) rename core/testing/src/{commonMain => androidMain}/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt (100%) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 9dff86db8..c9a973ac7 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -33,8 +33,17 @@ kotlin { implementation(libs.compose.ui.tooling.preview) implementation(libs.androidx.activity.compose) } + androidInstrumentedTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(projects.core.testing) + } androidUnitTest.dependencies { implementation(libs.androidx.compose.ui.test) + implementation(libs.accompanist.testharness) + implementation(libs.robolectric) + implementation(libs.roborazzi) + implementation(libs.hilt.android.testing) + implementation(projects.core.screenshotTesting) implementation(projects.core.testing) } commonMain.dependencies { @@ -51,13 +60,7 @@ kotlin { } } } + dependencies { lintPublish(projects.lint) - - testImplementation(libs.androidx.compose.ui.test) - testImplementation(libs.accompanist.testharness) - testImplementation(libs.robolectric) - testImplementation(libs.roborazzi) - testImplementation(projects.core.screenshotTesting) - testImplementation(projects.core.testing) } diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt similarity index 97% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt index e8cfd9a96..d65a19dae 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt index 2f6ab5370..b4297f372 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt index 2c9bc91ec..d838362c4 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt index 0104cfd47..5f12e44fd 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt index 9f80d04e5..57916092b 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt index e2e92b570..31ade1f88 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt index 9190d5f35..f551770d3 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt similarity index 98% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt index d9edfd6c6..9c73aff18 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt similarity index 95% rename from core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt rename to core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt index 6fac01562..a016f911b 100644 --- a/core/designsystem/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt +++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -45,7 +45,7 @@ import org.robolectric.annotation.LooperMode @GraphicsMode(GraphicsMode.Mode.NATIVE) @Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) -class TopAppBarScreenshotTests() { +class TopAppBarScreenshotTests { @get:Rule val composeTestRule = createAndroidComposeRule() @@ -80,7 +80,7 @@ class TopAppBarScreenshotTests() { @Composable private fun NiaTopAppBarExample() { NiaTopAppBar( - titleRes = android.R.string.untitled, + title = "Untitled", navigationIcon = NiaIcons.Search, navigationIconContentDescription = "Navigation icon", actionIcon = NiaIcons.MoreVert, diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_fontScale2.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_fontScale2.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_fontScale2.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_fontScale2.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_fontScale2.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_fontScale2.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_fontScale2.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_fontScale2.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_fontScale2.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_fontScale2.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_fontScale2.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_fontScale2.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_fontScale2.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_fontScale2.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_fontScale2.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png diff --git a/core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png similarity index 100% rename from core/designsystem/src/commonTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png rename to core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png diff --git a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt b/core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt similarity index 100% rename from core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt rename to core/testing/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/MainDispatcherRule.kt diff --git a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt index 09c739243..40c1d45e7 100644 --- a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt +++ b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -16,18 +16,15 @@ package com.google.samples.apps.nowinandroid.core.testing.di -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher -import javax.inject.Singleton +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) -internal object TestDispatcherModule { +@Component +internal abstract class TestDispatcherModule { + @OptIn(ExperimentalCoroutinesApi::class) @Provides - @Singleton fun providesTestDispatcher(): TestDispatcher = UnconfinedTestDispatcher() } diff --git a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt index a664d5538..167e71237 100644 --- a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt +++ b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -17,29 +17,13 @@ package com.google.samples.apps.nowinandroid.core.testing.di import com.google.samples.apps.nowinandroid.core.di.DispatchersComponent -import com.google.samples.apps.nowinandroid.core.network.Dispatcher -import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default -import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO -import dagger.Module -import dagger.Provides -import dagger.hilt.components.SingletonComponent -import dagger.hilt.testing.TestInstallIn import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.TestDispatcher +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@TestInstallIn( - components = [SingletonComponent::class], - replaces = [DispatchersComponent::class], -) -internal object TestDispatchersModule { +@Component +internal abstract class TestDispatchersModule { @Provides - @Dispatcher(IO) - fun providesIODispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher - - @Provides - @Dispatcher(Default) - fun providesDefaultDispatcher( - testDispatcher: TestDispatcher, - ): CoroutineDispatcher = testDispatcher + fun providesDispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher } diff --git a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt index 5436cd10f..47d95556e 100644 --- a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt +++ b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.update -import org.jetbrains.annotations.TestOnly class TestSearchContentsRepository : SearchContentsRepository { @@ -47,10 +46,8 @@ class TestSearchContentsRepository : SearchContentsRepository { override fun getSearchContentsCount(): Flow = combine(cachedTopics, cachedNewsResources) { topics, news -> topics.size + news.size } - @TestOnly fun addTopics(topics: List) = cachedTopics.update { it + topics } - @TestOnly fun addNewsResources(newsResources: List) = cachedNewsResources.update { it + newsResources } }