From ebfe01affd6b420e815fe29d5c8185c8e80183f7 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Mon, 29 May 2023 13:03:27 +0200 Subject: [PATCH 01/70] Enforce `resourcePrefix` on Android library modules ``` :ui-test-hilt-manifest -> ui_test_hilt_manifest_ :core:analytics -> core_analytics_ :core:common -> core_common_ :core:data -> core_data_ :core:data-test -> core_data_test_ :core:database -> core_database_ :core:datastore -> core_datastore_ :core:datastore-test -> core_datastore_test_ :core:designsystem -> core_designsystem_ :core:domain -> core_domain_ :core:network -> core_network_ :core:notifications -> core_notifications_ :core:testing -> core_testing_ :core:ui -> core_ui_ :feature:bookmarks -> feature_bookmarks_ :feature:foryou -> feature_foryou_ :feature:interests -> feature_interests_ :feature:search -> feature_search_ :feature:settings -> feature_settings_ :feature:topic -> feature_topic_ :sync:sync-test -> sync_test_ :sync:work -> sync_work_ ``` --- .../apps/nowinandroid/ui/NavigationTest.kt | 16 +++---- .../navigation/TopLevelDestination.kt | 12 ++--- .../samples/apps/nowinandroid/ui/NiaApp.kt | 4 +- .../kotlin/AndroidLibraryConventionPlugin.kt | 3 ++ ...ml => core_common_ic_nia_notification.xml} | 0 ...ng => core_common_ic_nia_notification.png} | Bin ...ng => core_common_ic_nia_notification.png} | Bin ...ng => core_common_ic_nia_notification.png} | Bin ...ng => core_common_ic_nia_notification.png} | Bin .../component/DynamicAsyncImage.kt | 2 +- ...e_designsystem_ic_placeholder_default.xml} | 0 .../core/notifications/SystemTrayNotifier.kt | 10 ++-- .../src/main/res/values/strings.xml | 6 +-- .../core/ui/NewsResourceCardTest.kt | 6 +-- .../nowinandroid/core/ui/NewsResourceCard.kt | 16 +++---- core/ui/src/main/res/values/strings.xml | 16 +++---- .../feature/bookmarks/BookmarksScreenTest.kt | 8 ++-- .../feature/bookmarks/BookmarksScreen.kt | 12 ++--- ...feature_bookmarks_img_empty_bookmarks.xml} | 0 .../bookmarks/src/main/res/values/strings.xml | 12 ++--- .../feature/foryou/ForYouScreenTest.kt | 10 ++-- .../feature/foryou/ForYouScreen.kt | 10 ++-- ...=> feature_foryou_ic_icon_placeholder.xml} | 0 .../foryou/src/main/res/values/strings.xml | 12 ++--- .../interests/InterestsScreenTest.kt | 8 ++-- .../feature/interests/InterestsItem.kt | 4 +- .../feature/interests/InterestsScreen.kt | 4 +- .../interests/src/main/res/values/strings.xml | 10 ++-- .../feature/search/SearchScreenTest.kt | 18 +++---- .../feature/search/SearchScreen.kt | 24 +++++----- .../search/src/main/res/values/strings.xml | 22 ++++----- .../feature/settings/SettingsDialogTest.kt | 44 +++++++++--------- .../feature/settings/SettingsDialog.kt | 34 +++++++------- .../settings/src/main/res/values/strings.xml | 38 +++++++-------- .../feature/topic/TopicScreenTest.kt | 2 +- .../nowinandroid/feature/topic/TopicScreen.kt | 4 +- feature/topic/src/main/res/values/strings.xml | 2 +- .../sync/initializers/SyncWorkHelpers.kt | 8 ++-- sync/work/src/main/res/values/strings.xml | 6 +-- 39 files changed, 193 insertions(+), 190 deletions(-) rename core/common/src/main/res/drawable-anydpi-v24/{ic_nia_notification.xml => core_common_ic_nia_notification.xml} (100%) rename core/common/src/main/res/drawable-hdpi/{ic_nia_notification.png => core_common_ic_nia_notification.png} (100%) rename core/common/src/main/res/drawable-mdpi/{ic_nia_notification.png => core_common_ic_nia_notification.png} (100%) rename core/common/src/main/res/drawable-xhdpi/{ic_nia_notification.png => core_common_ic_nia_notification.png} (100%) rename core/common/src/main/res/drawable-xxhdpi/{ic_nia_notification.png => core_common_ic_nia_notification.png} (100%) rename core/designsystem/src/main/res/drawable/{ic_placeholder_default.xml => core_designsystem_ic_placeholder_default.xml} (100%) rename feature/bookmarks/src/main/res/drawable/{img_empty_bookmarks.xml => feature_bookmarks_img_empty_bookmarks.xml} (100%) rename feature/foryou/src/main/res/drawable/{ic_icon_placeholder.xml => feature_foryou_ic_icon_placeholder.xml} (100%) diff --git a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index e1eab4796..6515c14de 100644 --- a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -51,7 +51,7 @@ import javax.inject.Inject import kotlin.properties.ReadOnlyProperty import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR -import com.google.samples.apps.nowinandroid.feature.interests.R as FeatureInterestsR +import com.google.samples.apps.nowinandroid.feature.search.R as FeatureSearchR import com.google.samples.apps.nowinandroid.feature.settings.R as SettingsR /** @@ -93,15 +93,15 @@ class NavigationTest { ReadOnlyProperty { _, _ -> activity.getString(resId) } // The strings used for matching in these tests - private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.navigate_up) - private val forYou by composeTestRule.stringResource(FeatureForyouR.string.for_you) - private val interests by composeTestRule.stringResource(FeatureInterestsR.string.interests) + private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_navigate_up) + private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) + private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests) private val sampleTopic = "Headlines" private val appName by composeTestRule.stringResource(R.string.app_name) - private val saved by composeTestRule.stringResource(BookmarksR.string.saved) - private val settings by composeTestRule.stringResource(SettingsR.string.top_app_bar_action_icon_description) - private val brand by composeTestRule.stringResource(SettingsR.string.brand_android) - private val ok by composeTestRule.stringResource(SettingsR.string.dismiss_dialog_button_text) + private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title) + private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description) + private val brand by composeTestRule.stringResource(SettingsR.string.feature_settings_brand_android) + private val ok by composeTestRule.stringResource(SettingsR.string.feature_settings_dismiss_dialog_button_text) @Before fun setup() = hiltRule.inject() diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt index 8dbd0fcb6..aca7d54ab 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt @@ -21,7 +21,7 @@ import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.feature.bookmarks.R as bookmarksR import com.google.samples.apps.nowinandroid.feature.foryou.R as forYouR -import com.google.samples.apps.nowinandroid.feature.interests.R as interestsR +import com.google.samples.apps.nowinandroid.feature.search.R as searchR /** * Type for the top level destinations in the application. Each of these destinations @@ -37,19 +37,19 @@ enum class TopLevelDestination( FOR_YOU( selectedIcon = NiaIcons.Upcoming, unselectedIcon = NiaIcons.UpcomingBorder, - iconTextId = forYouR.string.for_you, + iconTextId = forYouR.string.feature_foryou_title, titleTextId = R.string.app_name, ), BOOKMARKS( selectedIcon = NiaIcons.Bookmarks, unselectedIcon = NiaIcons.BookmarksBorder, - iconTextId = bookmarksR.string.saved, - titleTextId = bookmarksR.string.saved, + iconTextId = bookmarksR.string.feature_bookmarks_title, + titleTextId = bookmarksR.string.feature_bookmarks_title, ), INTERESTS( selectedIcon = NiaIcons.Grid3x3, unselectedIcon = NiaIcons.Grid3x3, - iconTextId = interestsR.string.interests, - titleTextId = interestsR.string.interests, + iconTextId = searchR.string.feature_search_interests, + titleTextId = searchR.string.feature_search_interests, ), } diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt index aa85afebd..1660581a4 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt @@ -183,11 +183,11 @@ fun NiaApp( titleRes = destination.titleTextId, navigationIcon = NiaIcons.Search, navigationIconContentDescription = stringResource( - id = settingsR.string.top_app_bar_navigation_icon_description, + id = settingsR.string.feature_settings_top_app_bar_navigation_icon_description, ), actionIcon = NiaIcons.Settings, actionIconContentDescription = stringResource( - id = settingsR.string.top_app_bar_action_icon_description, + id = settingsR.string.feature_settings_top_app_bar_action_icon_description, ), colors = TopAppBarDefaults.centerAlignedTopAppBarColors( containerColor = Color.Transparent, diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index ef84cfbb4..995b922a2 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -41,6 +41,9 @@ class AndroidLibraryConventionPlugin : Plugin { defaultConfig.targetSdk = 34 configureFlavors(this) configureGradleManagedDevices(this) + // The resource prefix is derived from the module name, + // so resources inside ":core:module1" must be prefixed with "core_module1_" + resourcePrefix = path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_").lowercase() + "_" } extensions.configure { configurePrintApksTask(this) diff --git a/core/common/src/main/res/drawable-anydpi-v24/ic_nia_notification.xml b/core/common/src/main/res/drawable-anydpi-v24/core_common_ic_nia_notification.xml similarity index 100% rename from core/common/src/main/res/drawable-anydpi-v24/ic_nia_notification.xml rename to core/common/src/main/res/drawable-anydpi-v24/core_common_ic_nia_notification.xml diff --git a/core/common/src/main/res/drawable-hdpi/ic_nia_notification.png b/core/common/src/main/res/drawable-hdpi/core_common_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_nia_notification.png rename to core/common/src/main/res/drawable-hdpi/core_common_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-mdpi/ic_nia_notification.png b/core/common/src/main/res/drawable-mdpi/core_common_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_nia_notification.png rename to core/common/src/main/res/drawable-mdpi/core_common_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-xhdpi/ic_nia_notification.png b/core/common/src/main/res/drawable-xhdpi/core_common_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_nia_notification.png rename to core/common/src/main/res/drawable-xhdpi/core_common_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_nia_notification.png b/core/common/src/main/res/drawable-xxhdpi/core_common_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_nia_notification.png rename to core/common/src/main/res/drawable-xxhdpi/core_common_ic_nia_notification.png diff --git a/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt b/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt index cc352107b..bd22fa168 100644 --- a/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt +++ b/core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/DynamicAsyncImage.kt @@ -49,7 +49,7 @@ fun DynamicAsyncImage( imageUrl: String, contentDescription: String?, modifier: Modifier = Modifier, - placeholder: Painter = painterResource(R.drawable.ic_placeholder_default), + placeholder: Painter = painterResource(R.drawable.core_designsystem_ic_placeholder_default), ) { val iconTint = LocalTintTheme.current.iconTint var isLoading by remember { mutableStateOf(true) } diff --git a/core/designsystem/src/main/res/drawable/ic_placeholder_default.xml b/core/designsystem/src/main/res/drawable/core_designsystem_ic_placeholder_default.xml similarity index 100% rename from core/designsystem/src/main/res/drawable/ic_placeholder_default.xml rename to core/designsystem/src/main/res/drawable/core_designsystem_ic_placeholder_default.xml diff --git a/core/notifications/src/main/java/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt b/core/notifications/src/main/java/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt index b7fcc9b26..12691b007 100644 --- a/core/notifications/src/main/java/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt +++ b/core/notifications/src/main/java/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt @@ -72,7 +72,7 @@ class SystemTrayNotifier @Inject constructor( .map { newsResource -> createNewsNotification { setSmallIcon( - com.google.samples.apps.nowinandroid.core.common.R.drawable.ic_nia_notification, + com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, ) .setContentTitle(newsResource.title) .setContentText(newsResource.content) @@ -83,13 +83,13 @@ class SystemTrayNotifier @Inject constructor( } val summaryNotification = createNewsNotification { val title = getString( - R.string.news_notification_group_summary, + R.string.core_notifications_news_notification_group_summary, truncatedNewsResources.size, ) setContentTitle(title) .setContentText(title) .setSmallIcon( - com.google.samples.apps.nowinandroid.core.common.R.drawable.ic_nia_notification, + com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, ) // Build summary info into InboxStyle template. .setStyle(newsNotificationStyle(truncatedNewsResources, title)) @@ -148,10 +148,10 @@ private fun Context.ensureNotificationChannelExists() { val channel = NotificationChannel( NEWS_NOTIFICATION_CHANNEL_ID, - getString(R.string.news_notification_channel_name), + getString(R.string.core_notifications_news_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT, ).apply { - description = getString(R.string.news_notification_channel_description) + description = getString(R.string.core_notifications_news_notification_channel_description) } // Register the channel with the system NotificationManagerCompat.from(this).createNotificationChannel(channel) diff --git a/core/notifications/src/main/res/values/strings.xml b/core/notifications/src/main/res/values/strings.xml index 5bb37b23a..88e492e77 100644 --- a/core/notifications/src/main/res/values/strings.xml +++ b/core/notifications/src/main/res/values/strings.xml @@ -15,7 +15,7 @@ limitations under the License. --> - News updates - The latest updates on what\'s new in Android - %1$d news updates + News updates + The latest updates on what\'s new in Android + %1$d news updates diff --git a/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt b/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt index a2fdbaee1..d0a124316 100644 --- a/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt +++ b/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt @@ -52,7 +52,7 @@ class NewsResourceCardTest { composeTestRule .onNodeWithText( composeTestRule.activity.getString( - R.string.card_meta_data_text, + R.string.core_ui_card_meta_data_text, dateFormatted, newsWithKnownResourceType.type, ), @@ -123,7 +123,7 @@ class NewsResourceCardTest { composeTestRule .onNodeWithContentDescription( composeTestRule.activity.getString( - R.string.unread_resource_dot_content_description, + R.string.core_ui_unread_resource_dot_content_description, ), ) .assertIsDisplayed() @@ -147,7 +147,7 @@ class NewsResourceCardTest { composeTestRule .onNodeWithContentDescription( composeTestRule.activity.getString( - R.string.unread_resource_dot_content_description, + R.string.core_ui_unread_resource_dot_content_description, ), ) .assertDoesNotExist() diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index de4aec9d7..687604ccc 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -91,7 +91,7 @@ fun NewsResourceCardExpanded( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, ) { - val clickActionLabel = stringResource(R.string.card_tap_action) + val clickActionLabel = stringResource(R.string.core_ui_card_tap_action) Card( onClick = onClick, shape = RoundedCornerShape(16.dp), @@ -183,7 +183,7 @@ fun NewsResourceHeaderImage( painter = if (isError.not() && !isLocalInspection) { imageLoader } else { - painterResource(drawable.ic_placeholder_default) + painterResource(drawable.core_designsystem_ic_placeholder_default) }, // TODO b/226661685: Investigate using alt text of image to populate content description contentDescription = null, // decorative image, @@ -212,13 +212,13 @@ fun BookmarkButton( icon = { Icon( imageVector = NiaIcons.BookmarkBorder, - contentDescription = stringResource(R.string.bookmark), + contentDescription = stringResource(R.string.core_ui_bookmark), ) }, checkedIcon = { Icon( imageVector = NiaIcons.Bookmark, - contentDescription = stringResource(R.string.unbookmark), + contentDescription = stringResource(R.string.core_ui_unbookmark), ) }, ) @@ -229,7 +229,7 @@ fun NotificationDot( color: Color, modifier: Modifier = Modifier, ) { - val description = stringResource(R.string.unread_resource_dot_content_description) + val description = stringResource(R.string.core_ui_unread_resource_dot_content_description) Canvas( modifier = modifier .semantics { contentDescription = description }, @@ -273,7 +273,7 @@ fun NewsResourceMetaData( val formattedDate = dateFormatted(publishDate) Text( if (resourceType.isNotBlank()) { - stringResource(R.string.card_meta_data_text, formattedDate, resourceType) + stringResource(R.string.core_ui_card_meta_data_text, formattedDate, resourceType) } else { formattedDate }, @@ -305,12 +305,12 @@ fun NewsResourceTopics( text = { val contentDescription = if (followableTopic.isFollowed) { stringResource( - R.string.topic_chip_content_description_when_followed, + R.string.core_ui_topic_chip_content_description_when_followed, followableTopic.topic.name, ) } else { stringResource( - R.string.topic_chip_content_description_when_not_followed, + R.string.core_ui_topic_chip_content_description_when_not_followed, followableTopic.topic.name, ) } diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index d21a5ea36..65a855fc9 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -15,15 +15,15 @@ limitations under the License. --> - Bookmark - Unbookmark - Back + Bookmark + Unbookmark + Back - Unread + Unread - Open Resource Link - %1$s • %2$s + Open Resource Link + %1$s • %2$s - %1$s is followed - %1$s is not followed + %1$s is followed + %1$s is not followed diff --git a/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index 6e432f2ab..3d684f9d1 100644 --- a/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -59,7 +59,7 @@ class BookmarksScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.saved_loading), + composeTestRule.activity.resources.getString(R.string.feature_bookmarks_loading), ) .assertExists() } @@ -125,7 +125,7 @@ class BookmarksScreenTest { composeTestRule .onAllNodesWithContentDescription( composeTestRule.activity.getString( - com.google.samples.apps.nowinandroid.core.ui.R.string.unbookmark, + com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_unbookmark, ), ).filter( hasAnyAncestor( @@ -156,13 +156,13 @@ class BookmarksScreenTest { composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.bookmarks_empty_error), + composeTestRule.activity.getString(R.string.feature_bookmarks_empty_error), ) .assertExists() composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.bookmarks_empty_description), + composeTestRule.activity.getString(R.string.feature_bookmarks_empty_description), ) .assertExists() } diff --git a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt index e46ada015..c84263405 100644 --- a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt +++ b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt @@ -113,8 +113,8 @@ internal fun BookmarksScreen( undoBookmarkRemoval: () -> Unit = {}, clearUndoState: () -> Unit = {}, ) { - val bookmarkRemovedMessage = stringResource(id = R.string.bookmark_removed) - val undoText = stringResource(id = R.string.undo) + val bookmarkRemovedMessage = stringResource(id = R.string.feature_bookmarks_removed) + val undoText = stringResource(id = R.string.feature_bookmarks_undo) LaunchedEffect(shouldDisplayUndoBookmark) { if (shouldDisplayUndoBookmark) { @@ -163,7 +163,7 @@ private fun LoadingState(modifier: Modifier = Modifier) { .fillMaxWidth() .wrapContentSize() .testTag("forYou:loading"), - contentDesc = stringResource(id = R.string.saved_loading), + contentDesc = stringResource(id = R.string.feature_bookmarks_loading), ) } @@ -236,7 +236,7 @@ private fun EmptyState(modifier: Modifier = Modifier) { val iconTint = LocalTintTheme.current.iconTint Image( modifier = Modifier.fillMaxWidth(), - painter = painterResource(id = R.drawable.img_empty_bookmarks), + painter = painterResource(id = R.drawable.feature_bookmarks_img_empty_bookmarks), colorFilter = if (iconTint != null) ColorFilter.tint(iconTint) else null, contentDescription = null, ) @@ -244,7 +244,7 @@ private fun EmptyState(modifier: Modifier = Modifier) { Spacer(modifier = Modifier.height(48.dp)) Text( - text = stringResource(id = R.string.bookmarks_empty_error), + text = stringResource(id = R.string.feature_bookmarks_empty_error), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center, style = MaterialTheme.typography.titleMedium, @@ -254,7 +254,7 @@ private fun EmptyState(modifier: Modifier = Modifier) { Spacer(modifier = Modifier.height(8.dp)) Text( - text = stringResource(id = R.string.bookmarks_empty_description), + text = stringResource(id = R.string.feature_bookmarks_empty_description), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodyMedium, diff --git a/feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml b/feature/bookmarks/src/main/res/drawable/feature_bookmarks_img_empty_bookmarks.xml similarity index 100% rename from feature/bookmarks/src/main/res/drawable/img_empty_bookmarks.xml rename to feature/bookmarks/src/main/res/drawable/feature_bookmarks_img_empty_bookmarks.xml diff --git a/feature/bookmarks/src/main/res/values/strings.xml b/feature/bookmarks/src/main/res/values/strings.xml index 875a90a0b..6e2b23043 100644 --- a/feature/bookmarks/src/main/res/values/strings.xml +++ b/feature/bookmarks/src/main/res/values/strings.xml @@ -15,10 +15,10 @@ limitations under the License. --> - Saved - Loading saved… - No saved updates - Updates you save will be stored here\nto read later - Bookmark removed - UNDO + Saved + Loading saved… + No saved updates + Updates you save will be stored here\nto read later + Bookmark removed + UNDO diff --git a/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt b/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt index 7431555ba..5477493ef 100644 --- a/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt +++ b/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt @@ -45,7 +45,7 @@ class ForYouScreenTest { private val doneButtonMatcher by lazy { hasText( - composeTestRule.activity.resources.getString(R.string.done), + composeTestRule.activity.resources.getString(R.string.feature_foryou_done), ) } @@ -70,7 +70,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.for_you_loading), + composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), ) .assertExists() } @@ -96,7 +96,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.for_you_loading), + composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), ) .assertExists() } @@ -215,7 +215,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.for_you_loading), + composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), ) .assertExists() } @@ -241,7 +241,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.for_you_loading), + composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), ) .assertExists() } diff --git a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index a24a91f1a..fb2752e08 100644 --- a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -216,7 +216,7 @@ internal fun ForYouScreen( targetOffsetY = { fullHeight -> -fullHeight }, ) + fadeOut(), ) { - val loadingContentDescription = stringResource(id = R.string.for_you_loading) + val loadingContentDescription = stringResource(id = R.string.feature_foryou_loading) Box( modifier = Modifier .fillMaxWidth() @@ -271,7 +271,7 @@ private fun LazyGridScope.onboarding( item(span = { GridItemSpan(maxLineSpan) }, contentType = "onboarding") { Column(modifier = interestsItemModifier) { Text( - text = stringResource(R.string.onboarding_guidance_title), + text = stringResource(R.string.feature_foryou_onboarding_guidance_title), textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -279,7 +279,7 @@ private fun LazyGridScope.onboarding( style = MaterialTheme.typography.titleMedium, ) Text( - text = stringResource(R.string.onboarding_guidance_subtitle), + text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle), modifier = Modifier .fillMaxWidth() .padding(top = 8.dp, start = 24.dp, end = 24.dp), @@ -305,7 +305,7 @@ private fun LazyGridScope.onboarding( .fillMaxWidth(), ) { Text( - text = stringResource(R.string.done), + text = stringResource(R.string.feature_foryou_done), ) } } @@ -434,7 +434,7 @@ fun TopicIcon( modifier: Modifier = Modifier, ) { DynamicAsyncImage( - placeholder = painterResource(R.drawable.ic_icon_placeholder), + placeholder = painterResource(R.drawable.feature_foryou_ic_icon_placeholder), imageUrl = imageUrl, contentDescription = null, // decorative modifier = modifier diff --git a/feature/foryou/src/main/res/drawable/ic_icon_placeholder.xml b/feature/foryou/src/main/res/drawable/feature_foryou_ic_icon_placeholder.xml similarity index 100% rename from feature/foryou/src/main/res/drawable/ic_icon_placeholder.xml rename to feature/foryou/src/main/res/drawable/feature_foryou_ic_icon_placeholder.xml diff --git a/feature/foryou/src/main/res/values/strings.xml b/feature/foryou/src/main/res/values/strings.xml index 5a33bc9c8..166749664 100644 --- a/feature/foryou/src/main/res/values/strings.xml +++ b/feature/foryou/src/main/res/values/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - For you - Done - Loading for you… - Navigate up - What are you interested in? - Updates from topics you follow will appear here. Follow some things to get started. + For you + Done + Loading for you… + Navigate up + What are you interested in? + Updates from topics you follow will appear here. Follow some things to get started. diff --git a/feature/interests/src/androidTest/java/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidTest/java/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index 492e91fa3..4f9cbcc04 100644 --- a/feature/interests/src/androidTest/java/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidTest/java/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -50,12 +50,12 @@ class InterestsScreenTest { @Before fun setup() { composeTestRule.activity.apply { - interestsLoading = getString(R.string.loading) - interestsEmptyHeader = getString(R.string.empty_header) + interestsLoading = getString(R.string.feature_interests_loading) + interestsEmptyHeader = getString(R.string.feature_interests_empty_header) interestsTopicCardFollowButton = - getString(R.string.card_follow_button_content_desc) + getString(R.string.feature_interests_card_follow_button_content_desc) interestsTopicCardUnfollowButton = - getString(R.string.card_unfollow_button_content_desc) + getString(R.string.feature_interests_card_unfollow_button_content_desc) } } diff --git a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt index 7456ba92b..0406f4abe 100644 --- a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt +++ b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt @@ -68,7 +68,7 @@ fun InterestsItem( Icon( imageVector = NiaIcons.Add, contentDescription = stringResource( - id = string.card_follow_button_content_desc, + id = string.feature_interests_card_follow_button_content_desc, ), ) }, @@ -76,7 +76,7 @@ fun InterestsItem( Icon( imageVector = NiaIcons.Check, contentDescription = stringResource( - id = string.card_unfollow_button_content_desc, + id = string.feature_interests_card_unfollow_button_content_desc, ), ) }, diff --git a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt index e618c1c9f..5944b8631 100644 --- a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt +++ b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt @@ -65,7 +65,7 @@ internal fun InterestsScreen( InterestsUiState.Loading -> NiaLoadingWheel( modifier = modifier, - contentDesc = stringResource(id = R.string.loading), + contentDesc = stringResource(id = R.string.feature_interests_loading), ) is InterestsUiState.Interests -> TopicsTabContent( @@ -82,7 +82,7 @@ internal fun InterestsScreen( @Composable private fun InterestsEmptyScreen() { - Text(text = stringResource(id = R.string.empty_header)) + Text(text = stringResource(id = R.string.feature_interests_empty_header)) } @DevicePreviews diff --git a/feature/interests/src/main/res/values/strings.xml b/feature/interests/src/main/res/values/strings.xml index 384cb1deb..2dd1c18a9 100644 --- a/feature/interests/src/main/res/values/strings.xml +++ b/feature/interests/src/main/res/values/strings.xml @@ -15,9 +15,9 @@ limitations under the License. --> - Interests - Loading data - "No available data" - Follow interest - Unfollow interest + Interests + Loading data + "No available data" + Follow interest + Unfollow interest diff --git a/feature/search/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt b/feature/search/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt index d6c07221e..8a0532e1b 100644 --- a/feature/search/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt +++ b/feature/search/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt @@ -70,17 +70,17 @@ class SearchScreenTest { @Before fun setup() { composeTestRule.activity.apply { - clearSearchContentDesc = getString(R.string.clear_search_text_content_desc) - clearRecentSearchesContentDesc = getString(R.string.clear_recent_searches_content_desc) + clearSearchContentDesc = getString(R.string.feature_search_clear_search_text_content_desc) + clearRecentSearchesContentDesc = getString(R.string.feature_search_clear_recent_searches_content_desc) followButtonContentDesc = - getString(interestsR.string.card_follow_button_content_desc) + getString(interestsR.string.feature_interests_card_follow_button_content_desc) unfollowButtonContentDesc = - getString(interestsR.string.card_unfollow_button_content_desc) - topicsString = getString(R.string.topics) - updatesString = getString(R.string.updates) - tryAnotherSearchString = getString(R.string.try_another_search) + - " " + getString(R.string.interests) + " " + getString(R.string.to_browse_topics) - searchNotReadyString = getString(R.string.search_not_ready) + getString(interestsR.string.feature_interests_card_unfollow_button_content_desc) + topicsString = getString(R.string.feature_search_topics) + updatesString = getString(R.string.feature_search_updates) + tryAnotherSearchString = getString(R.string.feature_search_try_another_search) + + " " + getString(R.string.feature_search_interests) + " " + getString(R.string.feature_search_to_browse_topics) + searchNotReadyString = getString(R.string.feature_search_not_ready) } } diff --git a/feature/search/src/main/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/main/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt index 944d17630..7a57a1b3f 100644 --- a/feature/search/src/main/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt +++ b/feature/search/src/main/java/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt @@ -217,7 +217,7 @@ fun EmptySearchResultBody( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(horizontal = 48.dp), ) { - val message = stringResource(id = searchR.string.search_result_not_found, searchQuery) + val message = stringResource(id = searchR.string.feature_search_result_not_found, searchQuery) val start = message.indexOf(searchQuery) Text( text = AnnotatedString( @@ -234,9 +234,9 @@ fun EmptySearchResultBody( textAlign = TextAlign.Center, modifier = Modifier.padding(vertical = 24.dp), ) - val interests = stringResource(id = searchR.string.interests) + val interests = stringResource(id = searchR.string.feature_search_interests) val tryAnotherSearchString = buildAnnotatedString { - append(stringResource(id = searchR.string.try_another_search)) + append(stringResource(id = searchR.string.feature_search_try_another_search)) append(" ") withStyle( style = SpanStyle( @@ -248,7 +248,7 @@ fun EmptySearchResultBody( append(interests) } append(" ") - append(stringResource(id = searchR.string.to_browse_topics)) + append(stringResource(id = searchR.string.feature_search_to_browse_topics)) } ClickableText( text = tryAnotherSearchString, @@ -278,7 +278,7 @@ private fun SearchNotReadyBody() { modifier = Modifier.padding(horizontal = 48.dp), ) { Text( - text = stringResource(id = searchR.string.search_not_ready), + text = stringResource(id = searchR.string.feature_search_not_ready), style = MaterialTheme.typography.bodyLarge, textAlign = TextAlign.Center, modifier = Modifier.padding(vertical = 24.dp), @@ -321,7 +321,7 @@ private fun SearchResultBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.topics)) + append(stringResource(id = searchR.string.feature_search_topics)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -360,7 +360,7 @@ private fun SearchResultBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.updates)) + append(stringResource(id = searchR.string.feature_search_updates)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -412,7 +412,7 @@ private fun RecentSearchesBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.recent_searches)) + append(stringResource(id = searchR.string.feature_search_recent_searches)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -427,7 +427,7 @@ private fun RecentSearchesBody( Icon( imageVector = NiaIcons.Close, contentDescription = stringResource( - id = searchR.string.clear_recent_searches_content_desc, + id = searchR.string.feature_search_clear_recent_searches_content_desc, ), tint = MaterialTheme.colorScheme.onSurface, ) @@ -465,7 +465,7 @@ private fun SearchToolbar( Icon( imageVector = NiaIcons.ArrowBack, contentDescription = stringResource( - id = string.back, + id = string.core_ui_back, ), ) } @@ -502,7 +502,7 @@ private fun SearchTextField( Icon( imageVector = NiaIcons.Search, contentDescription = stringResource( - id = searchR.string.search, + id = searchR.string.feature_search_title, ), tint = MaterialTheme.colorScheme.onSurface, ) @@ -517,7 +517,7 @@ private fun SearchTextField( Icon( imageVector = NiaIcons.Close, contentDescription = stringResource( - id = searchR.string.clear_search_text_content_desc, + id = searchR.string.feature_search_clear_search_text_content_desc, ), tint = MaterialTheme.colorScheme.onSurface, ) diff --git a/feature/search/src/main/res/values/strings.xml b/feature/search/src/main/res/values/strings.xml index 2a824653e..e11576747 100644 --- a/feature/search/src/main/res/values/strings.xml +++ b/feature/search/src/main/res/values/strings.xml @@ -15,15 +15,15 @@ limitations under the License. --> - Search - Clear search text - Sorry, there is no content found for your search \"%1$s\" - Sorry, we are still processing the search index. Please come back later. - Try another search or explorer - Interests - to browse topics - Topics - Updates - Recent searches - Clear searches + Search + Clear search text + Sorry, there is no content found for your search \"%1$s\" + Sorry, we are still processing the search index. Please come back later + Try another search or explorer + Interests + to browse topics + Topics + Updates + Recent searches + Clear searches diff --git a/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt b/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt index febc606b0..790b5964d 100644 --- a/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt +++ b/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt @@ -48,7 +48,7 @@ class SettingsDialogTest { } composeTestRule - .onNodeWithText(getString(R.string.loading)) + .onNodeWithText(getString(R.string.feature_settings_loading)) .assertExists() } @@ -71,17 +71,17 @@ class SettingsDialogTest { } // Check that all the possible settings are displayed. - composeTestRule.onNodeWithText(getString(R.string.brand_default)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.brand_android)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_default)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_android)).assertExists() composeTestRule.onNodeWithText( - getString(R.string.dark_mode_config_system_default), + getString(R.string.feature_settings_dark_mode_config_system_default), ).assertExists() - composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_light)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_dark)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_light)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_dark)).assertExists() // Check that the correct settings are selected. - composeTestRule.onNodeWithText(getString(R.string.brand_android)).assertIsSelected() - composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_dark)).assertIsSelected() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_android)).assertIsSelected() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_dark)).assertIsSelected() } @Test @@ -103,12 +103,12 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_preference)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_yes)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_no)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertExists() // Check that the correct default dynamic color setting is selected. - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_no)).assertIsSelected() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertIsSelected() } @Test @@ -129,10 +129,10 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_preference)) + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)) .assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_yes)).assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_no)).assertDoesNotExist() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertDoesNotExist() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertDoesNotExist() } @Test @@ -153,10 +153,10 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_preference)) + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)) .assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_yes)).assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.dynamic_color_no)).assertDoesNotExist() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertDoesNotExist() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertDoesNotExist() } @Test @@ -177,9 +177,9 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.privacy_policy)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.licenses)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.brand_guidelines)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feedback)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_privacy_policy)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_licenses)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_guidelines)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.feature_settings_feedback)).assertExists() } } diff --git a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index 01ec30e74..c71572246 100644 --- a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -108,7 +108,7 @@ fun SettingsDialog( onDismissRequest = { onDismiss() }, title = { Text( - text = stringResource(string.settings_title), + text = stringResource(string.feature_settings_title), style = MaterialTheme.typography.titleLarge, ) }, @@ -118,7 +118,7 @@ fun SettingsDialog( when (settingsUiState) { Loading -> { Text( - text = stringResource(string.loading), + text = stringResource(string.feature_settings_loading), modifier = Modifier.padding(vertical = 16.dp), ) } @@ -140,7 +140,7 @@ fun SettingsDialog( }, confirmButton = { Text( - text = stringResource(string.dismiss_dialog_button_text), + text = stringResource(string.feature_settings_dismiss_dialog_button_text), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, modifier = Modifier @@ -160,50 +160,50 @@ private fun ColumnScope.SettingsPanel( onChangeDynamicColorPreference: (useDynamicColor: Boolean) -> Unit, onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit, ) { - SettingsDialogSectionTitle(text = stringResource(string.theme)) + SettingsDialogSectionTitle(text = stringResource(string.feature_settings_theme)) Column(Modifier.selectableGroup()) { SettingsDialogThemeChooserRow( - text = stringResource(string.brand_default), + text = stringResource(string.feature_settings_brand_default), selected = settings.brand == DEFAULT, onClick = { onChangeThemeBrand(DEFAULT) }, ) SettingsDialogThemeChooserRow( - text = stringResource(string.brand_android), + text = stringResource(string.feature_settings_brand_android), selected = settings.brand == ANDROID, onClick = { onChangeThemeBrand(ANDROID) }, ) } AnimatedVisibility(visible = settings.brand == DEFAULT && supportDynamicColor) { Column { - SettingsDialogSectionTitle(text = stringResource(string.dynamic_color_preference)) + SettingsDialogSectionTitle(text = stringResource(string.feature_settings_dynamic_color_preference)) Column(Modifier.selectableGroup()) { SettingsDialogThemeChooserRow( - text = stringResource(string.dynamic_color_yes), + text = stringResource(string.feature_settings_dynamic_color_yes), selected = settings.useDynamicColor, onClick = { onChangeDynamicColorPreference(true) }, ) SettingsDialogThemeChooserRow( - text = stringResource(string.dynamic_color_no), + text = stringResource(string.feature_settings_dynamic_color_no), selected = !settings.useDynamicColor, onClick = { onChangeDynamicColorPreference(false) }, ) } } } - SettingsDialogSectionTitle(text = stringResource(string.dark_mode_preference)) + SettingsDialogSectionTitle(text = stringResource(string.feature_settings_dark_mode_preference)) Column(Modifier.selectableGroup()) { SettingsDialogThemeChooserRow( - text = stringResource(string.dark_mode_config_system_default), + text = stringResource(string.feature_settings_dark_mode_config_system_default), selected = settings.darkThemeConfig == FOLLOW_SYSTEM, onClick = { onChangeDarkThemeConfig(FOLLOW_SYSTEM) }, ) SettingsDialogThemeChooserRow( - text = stringResource(string.dark_mode_config_light), + text = stringResource(string.feature_settings_dark_mode_config_light), selected = settings.darkThemeConfig == LIGHT, onClick = { onChangeDarkThemeConfig(LIGHT) }, ) SettingsDialogThemeChooserRow( - text = stringResource(string.dark_mode_config_dark), + text = stringResource(string.feature_settings_dark_mode_config_dark), selected = settings.darkThemeConfig == DARK, onClick = { onChangeDarkThemeConfig(DARK) }, ) @@ -259,7 +259,7 @@ private fun LinksPanel() { NiaTextButton( onClick = { uriHandler.openUri(PRIVACY_POLICY_URL) }, ) { - Text(text = stringResource(string.privacy_policy)) + Text(text = stringResource(string.feature_settings_privacy_policy)) } val context = LocalContext.current NiaTextButton( @@ -267,17 +267,17 @@ private fun LinksPanel() { context.startActivity(Intent(context, OssLicensesMenuActivity::class.java)) }, ) { - Text(text = stringResource(string.licenses)) + Text(text = stringResource(string.feature_settings_licenses)) } NiaTextButton( onClick = { uriHandler.openUri(BRAND_GUIDELINES_URL) }, ) { - Text(text = stringResource(string.brand_guidelines)) + Text(text = stringResource(string.feature_settings_brand_guidelines)) } NiaTextButton( onClick = { uriHandler.openUri(FEEDBACK_URL) }, ) { - Text(text = stringResource(string.feedback)) + Text(text = stringResource(string.feature_settings_feedback)) } } } diff --git a/feature/settings/src/main/res/values/strings.xml b/feature/settings/src/main/res/values/strings.xml index ad56f6b08..887539bd5 100644 --- a/feature/settings/src/main/res/values/strings.xml +++ b/feature/settings/src/main/res/values/strings.xml @@ -15,23 +15,23 @@ limitations under the License. --> - Settings - Search - Settings - Loading… - Privacy policy - Licenses - Brand Guidelines - Feedback - Theme - Default - Android - Dark mode preference - System default - Light - Dark - Use Dynamic Color - Yes - No - OK + Settings + Search + Settings + Loading… + Privacy policy + Licenses + Brand Guidelines + Feedback + Theme + Default + Android + Dark mode preference + System default + Light + Dark + Use Dynamic Color + Yes + No + OK diff --git a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index 94f86a8e4..b64e397ea 100644 --- a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -45,7 +45,7 @@ class TopicScreenTest { @Before fun setup() { composeTestRule.activity.apply { - topicLoading = getString(R.string.topic_loading) + topicLoading = getString(R.string.feature_topic_loading) } } diff --git a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt index 3dbbe7da8..3f3862c2a 100644 --- a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt +++ b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt @@ -119,7 +119,7 @@ internal fun TopicScreen( TopicUiState.Loading -> item { NiaLoadingWheel( modifier = modifier, - contentDesc = stringResource(id = string.topic_loading), + contentDesc = stringResource(id = string.feature_topic_loading), ) } @@ -284,7 +284,7 @@ private fun TopicToolbar( Icon( imageVector = NiaIcons.ArrowBack, contentDescription = stringResource( - id = com.google.samples.apps.nowinandroid.core.ui.R.string.back, + id = com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_back, ), ) } diff --git a/feature/topic/src/main/res/values/strings.xml b/feature/topic/src/main/res/values/strings.xml index 284f2f7b2..5fefc3f42 100644 --- a/feature/topic/src/main/res/values/strings.xml +++ b/feature/topic/src/main/res/values/strings.xml @@ -15,5 +15,5 @@ limitations under the License. --> - Loading topic + Loading topic diff --git a/sync/work/src/main/java/com/google/samples/apps/nowinandroid/sync/initializers/SyncWorkHelpers.kt b/sync/work/src/main/java/com/google/samples/apps/nowinandroid/sync/initializers/SyncWorkHelpers.kt index 5abf2eee5..843510aaf 100644 --- a/sync/work/src/main/java/com/google/samples/apps/nowinandroid/sync/initializers/SyncWorkHelpers.kt +++ b/sync/work/src/main/java/com/google/samples/apps/nowinandroid/sync/initializers/SyncWorkHelpers.kt @@ -54,10 +54,10 @@ private fun Context.syncWorkNotification(): Notification { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( SYNC_NOTIFICATION_CHANNEL_ID, - getString(R.string.sync_notification_channel_name), + getString(R.string.sync_work_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT, ).apply { - description = getString(R.string.sync_notification_channel_description) + description = getString(R.string.sync_work_notification_channel_description) } // Register the channel with the system val notificationManager: NotificationManager? = @@ -71,9 +71,9 @@ private fun Context.syncWorkNotification(): Notification { SYNC_NOTIFICATION_CHANNEL_ID, ) .setSmallIcon( - com.google.samples.apps.nowinandroid.core.common.R.drawable.ic_nia_notification, + com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, ) - .setContentTitle(getString(R.string.sync_notification_title)) + .setContentTitle(getString(R.string.sync_work_notification_title)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() } diff --git a/sync/work/src/main/res/values/strings.xml b/sync/work/src/main/res/values/strings.xml index e3fd73ff8..4d77f6a7b 100644 --- a/sync/work/src/main/res/values/strings.xml +++ b/sync/work/src/main/res/values/strings.xml @@ -15,8 +15,8 @@ limitations under the License. --> - Now in Android - Sync - Background tasks for Now in Android + Now in Android + Sync + Background tasks for Now in Android From bc56ba79bc229201a6fa24c39305cb835310bfb7 Mon Sep 17 00:00:00 2001 From: kimdowoo Date: Wed, 20 Sep 2023 22:28:37 +0900 Subject: [PATCH 02/70] Add Test for Saved Top Level Destination --- .../google/samples/apps/nowinandroid/ui/NavigationTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 e1eab4796..4b8baf06b 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 @@ -166,7 +166,10 @@ class NavigationTest { composeTestRule.apply { // GIVEN the user is on any of the top level destinations, THEN the Up arrow is not shown. onNodeWithContentDescription(navigateUp).assertDoesNotExist() - // TODO: Add top level destinations here, see b/226357686. + + onNodeWithText(saved).performClick() + onNodeWithContentDescription(navigateUp).assertDoesNotExist() + onNodeWithText(interests).performClick() onNodeWithContentDescription(navigateUp).assertDoesNotExist() } From 1c78acf73b797602bc8a5fedb647310f17d49ca9 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 12 Aug 2023 00:44:56 +0200 Subject: [PATCH 03/70] Enable dependabot updates on a daily basis using the `version update` label for gradle updates. --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..6a9528470 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + registries: "*" + labels: [ "version update" ] +registries: + maven-google: + type: "maven-repository" + url: "https://maven.google.com" + replaces-base: true From 8826c9cb424b4051e6448bcf99312d7cdb8d00e6 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Thu, 24 Aug 2023 18:19:22 +0200 Subject: [PATCH 04/70] Schedule weekly instead of daily as suggested --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6a9528470..5096f3bfb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,11 +4,11 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "gradle" directory: "/" schedule: - interval: "daily" + interval: "weekly" registries: "*" labels: [ "version update" ] registries: From 8d5984e8432680733a2e02a05e74ef6417b0cebc Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Thu, 24 Aug 2023 18:25:16 +0200 Subject: [PATCH 05/70] Add "Kotlin - KSP - Compose" update group --- .github/dependabot.yml | 7 +++++++ gradle/libs.versions.toml | 1 + 2 files changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5096f3bfb..00ada2a1c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,6 +11,13 @@ updates: interval: "weekly" registries: "*" labels: [ "version update" ] + groups: + kotlin-ksp-compose: + patterns: + - "org.jetbrains.kotlin:*" + - "org.jetbrains.kotlin.jvm" + - "com.google.devtools.ksp" + - "androidx.compose.compiler:compiler" registries: maven-google: type: "maven-repository" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 08391f2e6..f8d908c02 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,6 +64,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 7133de7faaa7b09aa7e832f61ecd30a1660b84b7 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Thu, 24 Aug 2023 18:27:27 +0200 Subject: [PATCH 06/70] Remove `.github/renovate.json` --- .github/renovate.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/renovate.json diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index f19341761..000000000 --- a/.github/renovate.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", "group:all", ":dependencyDashboard", "schedule:daily" - ], - "packageRules": [ - { - "matchPackageNames": ["org.objenesis:objenesis"], - "allowedVersions": "<=2.6" - }, - { - "matchPackageNames": ["com.google.protobuf"], - "allowedVersions": "<=0.8.19" - } - ] -} From 988002e692ec63d932594470207e7f32c98931b9 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 18 Nov 2023 12:13:21 +0100 Subject: [PATCH 07/70] Update Robolectric 4.11.1 - https://github.com/robolectric/robolectric/releases/tag/robolectric-4.11 - https://github.com/robolectric/robolectric/releases/tag/robolectric-4.11.1 > Robolectric 4.11 adds support for Android SDK 34 (U). It also contains many bug fixes, performance improvements, and Shadow API enhancements. Robolectric 4.11 removes support for Android SDKs 18 and below. Removing explicit API 33 Robolectric configuration. --- .../apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/BackgroundScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/ButtonScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/FilterChipScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/IconButtonScreenshotTests.kt | 2 +- .../core/designsystem/LoadingWheelScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/NavigationScreenshotTests.kt | 2 +- .../apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt | 2 +- .../apps/nowinandroid/core/designsystem/TagScreenshotTests.kt | 2 +- .../nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt | 2 +- .../nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt | 2 +- gradle/libs.versions.toml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) 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 bac088482..dcbc1e5c0 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 @@ -65,7 +65,7 @@ import javax.inject.Inject @GraphicsMode(GraphicsMode.Mode.NATIVE) // Configure Robolectric to use a very large screen size that can fit all of the test sizes. // This allows enough room to render the content under test without clipping or scaling. -@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi", sdk = [33]) +@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi") @LooperMode(LooperMode.Mode.PAUSED) @HiltAndroidTest class NiaAppScreenSizesScreenshotTests { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt index d3349de80..e8cfd9a96 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/BackgroundScreenshotTests.kt @@ -36,7 +36,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class BackgroundScreenshotTests { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt index d3aa4224f..2f6ab5370 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/ButtonScreenshotTests.kt @@ -36,7 +36,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class ButtonScreenshotTests { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt index 510a0dcfc..2c9bc91ec 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt @@ -43,7 +43,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class FilterChipScreenshotTests() { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt index f6d71fe77..0104cfd47 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/IconButtonScreenshotTests.kt @@ -35,7 +35,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class IconButtonScreenshotTests { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt index 412f42370..75a4aec3a 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt @@ -37,7 +37,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class LoadingWheelScreenshotTests() { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt index 05976ba47..e2e92b570 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt @@ -44,7 +44,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class NavigationScreenshotTests() { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt index 76117db2e..9190d5f35 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt @@ -42,7 +42,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class TabsScreenshotTests() { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt index 9db4d02a3..d9edfd6c6 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt @@ -39,7 +39,7 @@ import org.robolectric.annotation.LooperMode @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class TagScreenshotTests() { diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt index 29404da79..6fac01562 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TopAppBarScreenshotTests.kt @@ -43,7 +43,7 @@ import org.robolectric.annotation.LooperMode @OptIn(ExperimentalMaterial3Api::class) @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi") +@Config(application = HiltTestApplication::class, qualifiers = "480dpi") @LooperMode(LooperMode.Mode.PAUSED) class TopAppBarScreenshotTests() { diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt index 1c521d419..14b67c64e 100644 --- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt +++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt @@ -46,7 +46,7 @@ import java.util.TimeZone */ @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [33]) +@Config(application = HiltTestApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ForYouScreenScreenshotTests { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1eb9116e8..9ee708385 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,7 +52,7 @@ protobuf = "3.24.4" protobufPlugin = "0.9.4" retrofit = "2.9.0" retrofitKotlinxSerializationJson = "1.0.0" -robolectric = "4.10.3" +robolectric = "4.11.1" roborazzi = "1.6.0" room = "2.6.0" secrets = "2.0.1" From d5d5e3561f6c4f069be77c97d12a1a1b5edbf562 Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Fri, 10 Nov 2023 09:52:34 -0800 Subject: [PATCH 08/70] Add dependency guard plugin --- .../dependencies/releaseRuntimeClasspath.txt | 0 app/build.gradle.kts | 12 + .../demoDebugRuntimeClasspath.txt | 218 ++++++++++++++++++ .../demoReleaseRuntimeClasspath.txt | 211 +++++++++++++++++ .../prodDebugRuntimeClasspath.txt | 218 ++++++++++++++++++ .../prodReleaseRuntimeClasspath.txt | 211 +++++++++++++++++ gradle/libs.versions.toml | 2 + 7 files changed, 872 insertions(+) create mode 100644 app-nia-catalog/dependencies/releaseRuntimeClasspath.txt create mode 100644 app/dependencies/demoDebugRuntimeClasspath.txt create mode 100644 app/dependencies/demoReleaseRuntimeClasspath.txt create mode 100644 app/dependencies/prodDebugRuntimeClasspath.txt create mode 100644 app/dependencies/prodReleaseRuntimeClasspath.txt diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt new file mode 100644 index 000000000..e69de29bb diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7a3ada333..319b2b4b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,6 +25,7 @@ plugins { alias(libs.plugins.nowinandroid.android.application.firebase) id("com.google.android.gms.oss-licenses-plugin") alias(libs.plugins.baselineprofile) + alias(libs.plugins.dependencyGuard) } android { @@ -144,3 +145,14 @@ baselineProfile { // Instead enable generation directly for the release build variant. automaticGenerationDuringBuild = false } + +dependencyGuard { + + configuration("demoDebugRuntimeClasspath") + + configuration("demoReleaseRuntimeClasspath") + + configuration("prodDebugRuntimeClasspath") + + configuration("prodReleaseRuntimeClasspath") +} diff --git a/app/dependencies/demoDebugRuntimeClasspath.txt b/app/dependencies/demoDebugRuntimeClasspath.txt new file mode 100644 index 000000000..511aeaa02 --- /dev/null +++ b/app/dependencies/demoDebugRuntimeClasspath.txt @@ -0,0 +1,218 @@ +androidx.activity:activity-compose:1.8.0 +androidx.activity:activity-ktx:1.8.0 +androidx.activity:activity:1.8.0 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation-jvm:1.6.0 +androidx.annotation:annotation:1.6.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.appcompat:appcompat:1.6.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.browser:browser:1.6.0 +androidx.collection:collection-ktx:1.1.0 +androidx.collection:collection:1.2.0 +androidx.compose.animation:animation-android:1.5.4 +androidx.compose.animation:animation-core-android:1.5.4 +androidx.compose.animation:animation-core:1.5.4 +androidx.compose.animation:animation:1.5.4 +androidx.compose.foundation:foundation-android:1.5.4 +androidx.compose.foundation:foundation-layout-android:1.5.4 +androidx.compose.foundation:foundation-layout:1.5.4 +androidx.compose.foundation:foundation:1.5.4 +androidx.compose.material3:material3-window-size-class:1.1.2 +androidx.compose.material3:material3:1.1.2 +androidx.compose.material:material-android:1.5.4 +androidx.compose.material:material-icons-core-android:1.5.4 +androidx.compose.material:material-icons-core:1.5.4 +androidx.compose.material:material-icons-extended-android:1.5.4 +androidx.compose.material:material-icons-extended:1.5.4 +androidx.compose.material:material-ripple-android:1.5.4 +androidx.compose.material:material-ripple:1.5.4 +androidx.compose.material:material:1.5.4 +androidx.compose.runtime:runtime-android:1.5.4 +androidx.compose.runtime:runtime-livedata: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-alpha03 +androidx.compose.runtime:runtime:1.5.4 +androidx.compose.ui:ui-android:1.5.4 +androidx.compose.ui:ui-geometry-android:1.5.4 +androidx.compose.ui:ui-geometry:1.5.4 +androidx.compose.ui:ui-graphics-android:1.5.4 +androidx.compose.ui:ui-graphics:1.5.4 +androidx.compose.ui:ui-test-manifest:1.5.4 +androidx.compose.ui:ui-text-android:1.5.4 +androidx.compose.ui:ui-text:1.5.4 +androidx.compose.ui:ui-tooling-android:1.5.4 +androidx.compose.ui:ui-tooling-data-android:1.5.4 +androidx.compose.ui:ui-tooling-data:1.5.4 +androidx.compose.ui:ui-tooling-preview-android:1.5.4 +androidx.compose.ui:ui-tooling-preview:1.5.4 +androidx.compose.ui:ui-tooling:1.5.4 +androidx.compose.ui:ui-unit-android:1.5.4 +androidx.compose.ui:ui-unit:1.5.4 +androidx.compose.ui:ui-util-android:1.5.4 +androidx.compose.ui:ui-util:1.5.4 +androidx.compose.ui:ui:1.5.4 +androidx.compose:compose-bom:2023.10.01 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core-splashscreen:1.0.1 +androidx.core:core:1.12.0 +androidx.cursoradapter:cursoradapter:1.0.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.customview:customview:1.0.0 +androidx.datastore:datastore-core:1.0.0 +androidx.datastore:datastore-preferences-core:1.0.0 +androidx.datastore:datastore-preferences:1.0.0 +androidx.datastore:datastore:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.drawerlayout:drawerlayout:1.0.0 +androidx.emoji2:emoji2-views-helper:1.4.0 +androidx.emoji2:emoji2:1.4.0 +androidx.exifinterface:exifinterface:1.3.6 +androidx.fragment:fragment:1.5.1 +androidx.hilt:hilt-common:1.1.0 +androidx.hilt:hilt-navigation-compose:1.0.0 +androidx.hilt:hilt-navigation:1.0.0 +androidx.hilt:hilt-work:1.1.0 +androidx.interpolator:interpolator:1.0.0 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.6.2 +androidx.lifecycle:lifecycle-common:2.6.2 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata-core:2.6.2 +androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata:2.6.2 +androidx.lifecycle:lifecycle-process:2.6.2 +androidx.lifecycle:lifecycle-runtime-compose:2.6.2 +androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 +androidx.lifecycle:lifecycle-runtime:2.6.2 +androidx.lifecycle:lifecycle-service:2.6.2 +androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.metrics:metrics-performance:1.0.0-alpha04 +androidx.navigation:navigation-common-ktx:2.7.4 +androidx.navigation:navigation-common:2.7.4 +androidx.navigation:navigation-compose:2.7.4 +androidx.navigation:navigation-runtime-ktx:2.7.4 +androidx.navigation:navigation-runtime:2.7.4 +androidx.print:print:1.0.0 +androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 +androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.resourceinspection:resourceinspection-annotation:1.0.1 +androidx.room:room-common:2.6.0 +androidx.room:room-ktx:2.6.0 +androidx.room:room-runtime:2.6.0 +androidx.savedstate:savedstate-ktx:1.2.1 +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.2.0-alpha02 +androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 +androidx.tracing:tracing-perfetto:1.0.0-alpha11 +androidx.tracing:tracing:1.2.0-alpha02 +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.extensions.core:core:1.0.0 +androidx.window:window:1.1.0 +androidx.work:work-runtime-ktx:2.9.0-rc01 +androidx.work:work-runtime:2.9.0-rc01 +com.caverock:androidsvg-aar:1.4 +com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-permissions:0.32.0 +com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-backend-cct:3.1.9 +com.google.android.datatransport:transport-runtime:3.1.9 +com.google.android.gms:play-services-ads-identifier:18.0.0 +com.google.android.gms:play-services-base:18.0.1 +com.google.android.gms:play-services-basement:18.1.0 +com.google.android.gms:play-services-cloud-messaging:17.0.1 +com.google.android.gms:play-services-measurement-api:21.4.0 +com.google.android.gms:play-services-measurement-base:21.4.0 +com.google.android.gms:play-services-measurement-impl:21.4.0 +com.google.android.gms:play-services-measurement-sdk-api:21.4.0 +com.google.android.gms:play-services-measurement-sdk:21.4.0 +com.google.android.gms:play-services-measurement:21.4.0 +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.48.1 +com.google.dagger:dagger:2.48.1 +com.google.dagger:hilt-android:2.48.1 +com.google.dagger:hilt-core:2.48.1 +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 +com.google.firebase:firebase-analytics:21.4.0 +com.google.firebase:firebase-annotations:16.2.0 +com.google.firebase:firebase-bom:32.4.0 +com.google.firebase:firebase-common-ktx:20.4.2 +com.google.firebase:firebase-common:20.4.2 +com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-config:21.5.0 +com.google.firebase:firebase-crashlytics-ktx:18.5.0 +com.google.firebase:firebase-crashlytics:18.5.0 +com.google.firebase:firebase-datatransport:18.1.8 +com.google.firebase:firebase-encoders-json:18.0.1 +com.google.firebase:firebase-encoders-proto:16.0.0 +com.google.firebase:firebase-encoders:17.0.0 +com.google.firebase:firebase-iid-interop:17.1.0 +com.google.firebase:firebase-installations-interop:17.1.1 +com.google.firebase:firebase-installations:17.2.0 +com.google.firebase:firebase-measurement-connector:19.0.0 +com.google.firebase:firebase-messaging-ktx:23.3.0 +com.google.firebase:firebase-messaging:23.3.0 +com.google.firebase:firebase-perf-ktx:20.5.0 +com.google.firebase:firebase-perf:20.5.0 +com.google.firebase:firebase-sessions:1.1.0 +com.google.firebase:protolite-well-known-types:18.0.0 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:31.1-android +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.google.protobuf:protobuf-javalite:3.24.4 +com.google.protobuf:protobuf-kotlin-lite:3.24.4 +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.retrofit2:retrofit:2.9.0 +io.coil-kt:coil-base:2.4.0 +io.coil-kt:coil-compose-base:2.4.0 +io.coil-kt:coil-compose:2.4.0 +io.coil-kt:coil-svg:2.4.0 +io.coil-kt:coil:2.4.0 +io.github.aakira:napier-android-debug: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.10 +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.10 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/demoReleaseRuntimeClasspath.txt b/app/dependencies/demoReleaseRuntimeClasspath.txt new file mode 100644 index 000000000..8123217b9 --- /dev/null +++ b/app/dependencies/demoReleaseRuntimeClasspath.txt @@ -0,0 +1,211 @@ +androidx.activity:activity-compose:1.8.0 +androidx.activity:activity-ktx:1.8.0 +androidx.activity:activity:1.8.0 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation-jvm:1.6.0 +androidx.annotation:annotation:1.6.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.appcompat:appcompat:1.6.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.browser:browser:1.6.0 +androidx.collection:collection-ktx:1.1.0 +androidx.collection:collection:1.2.0 +androidx.compose.animation:animation-android:1.5.4 +androidx.compose.animation:animation-core-android:1.5.4 +androidx.compose.animation:animation-core:1.5.4 +androidx.compose.animation:animation:1.5.4 +androidx.compose.foundation:foundation-android:1.5.4 +androidx.compose.foundation:foundation-layout-android:1.5.4 +androidx.compose.foundation:foundation-layout:1.5.4 +androidx.compose.foundation:foundation:1.5.4 +androidx.compose.material3:material3-window-size-class:1.1.2 +androidx.compose.material3:material3:1.1.2 +androidx.compose.material:material-icons-core-android:1.5.4 +androidx.compose.material:material-icons-core:1.5.4 +androidx.compose.material:material-icons-extended-android:1.5.4 +androidx.compose.material:material-icons-extended:1.5.4 +androidx.compose.material:material-ripple-android:1.5.4 +androidx.compose.material:material-ripple:1.5.4 +androidx.compose.runtime:runtime-android:1.5.4 +androidx.compose.runtime:runtime-livedata: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-alpha03 +androidx.compose.runtime:runtime:1.5.4 +androidx.compose.ui:ui-android:1.5.4 +androidx.compose.ui:ui-geometry-android:1.5.4 +androidx.compose.ui:ui-geometry:1.5.4 +androidx.compose.ui:ui-graphics-android:1.5.4 +androidx.compose.ui:ui-graphics:1.5.4 +androidx.compose.ui:ui-text-android:1.5.4 +androidx.compose.ui:ui-text:1.5.4 +androidx.compose.ui:ui-tooling-preview-android:1.5.4 +androidx.compose.ui:ui-tooling-preview:1.5.4 +androidx.compose.ui:ui-unit-android:1.5.4 +androidx.compose.ui:ui-unit:1.5.4 +androidx.compose.ui:ui-util-android:1.5.4 +androidx.compose.ui:ui-util:1.5.4 +androidx.compose.ui:ui:1.5.4 +androidx.compose:compose-bom:2023.10.01 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core-splashscreen:1.0.1 +androidx.core:core:1.12.0 +androidx.cursoradapter:cursoradapter:1.0.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.customview:customview:1.0.0 +androidx.datastore:datastore-core:1.0.0 +androidx.datastore:datastore-preferences-core:1.0.0 +androidx.datastore:datastore-preferences:1.0.0 +androidx.datastore:datastore:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.drawerlayout:drawerlayout:1.0.0 +androidx.emoji2:emoji2-views-helper:1.4.0 +androidx.emoji2:emoji2:1.4.0 +androidx.exifinterface:exifinterface:1.3.6 +androidx.fragment:fragment:1.5.1 +androidx.hilt:hilt-common:1.1.0 +androidx.hilt:hilt-navigation-compose:1.0.0 +androidx.hilt:hilt-navigation:1.0.0 +androidx.hilt:hilt-work:1.1.0 +androidx.interpolator:interpolator:1.0.0 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.6.2 +androidx.lifecycle:lifecycle-common:2.6.2 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata-core:2.6.2 +androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata:2.6.2 +androidx.lifecycle:lifecycle-process:2.6.2 +androidx.lifecycle:lifecycle-runtime-compose:2.6.2 +androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 +androidx.lifecycle:lifecycle-runtime:2.6.2 +androidx.lifecycle:lifecycle-service:2.6.2 +androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.metrics:metrics-performance:1.0.0-alpha04 +androidx.navigation:navigation-common-ktx:2.7.4 +androidx.navigation:navigation-common:2.7.4 +androidx.navigation:navigation-compose:2.7.4 +androidx.navigation:navigation-runtime-ktx:2.7.4 +androidx.navigation:navigation-runtime:2.7.4 +androidx.print:print:1.0.0 +androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 +androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.resourceinspection:resourceinspection-annotation:1.0.1 +androidx.room:room-common:2.6.0 +androidx.room:room-ktx:2.6.0 +androidx.room:room-runtime:2.6.0 +androidx.savedstate:savedstate-ktx:1.2.1 +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.2.0-alpha02 +androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 +androidx.tracing:tracing-perfetto:1.0.0-alpha11 +androidx.tracing:tracing:1.2.0-alpha02 +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.extensions.core:core:1.0.0 +androidx.window:window:1.1.0 +androidx.work:work-runtime-ktx:2.9.0-rc01 +androidx.work:work-runtime:2.9.0-rc01 +com.caverock:androidsvg-aar:1.4 +com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-permissions:0.32.0 +com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-backend-cct:3.1.9 +com.google.android.datatransport:transport-runtime:3.1.9 +com.google.android.gms:play-services-ads-identifier:18.0.0 +com.google.android.gms:play-services-base:18.0.1 +com.google.android.gms:play-services-basement:18.1.0 +com.google.android.gms:play-services-cloud-messaging:17.0.1 +com.google.android.gms:play-services-measurement-api:21.4.0 +com.google.android.gms:play-services-measurement-base:21.4.0 +com.google.android.gms:play-services-measurement-impl:21.4.0 +com.google.android.gms:play-services-measurement-sdk-api:21.4.0 +com.google.android.gms:play-services-measurement-sdk:21.4.0 +com.google.android.gms:play-services-measurement:21.4.0 +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.48.1 +com.google.dagger:dagger:2.48.1 +com.google.dagger:hilt-android:2.48.1 +com.google.dagger:hilt-core:2.48.1 +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 +com.google.firebase:firebase-analytics:21.4.0 +com.google.firebase:firebase-annotations:16.2.0 +com.google.firebase:firebase-bom:32.4.0 +com.google.firebase:firebase-common-ktx:20.4.2 +com.google.firebase:firebase-common:20.4.2 +com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-config:21.5.0 +com.google.firebase:firebase-crashlytics-ktx:18.5.0 +com.google.firebase:firebase-crashlytics:18.5.0 +com.google.firebase:firebase-datatransport:18.1.8 +com.google.firebase:firebase-encoders-json:18.0.1 +com.google.firebase:firebase-encoders-proto:16.0.0 +com.google.firebase:firebase-encoders:17.0.0 +com.google.firebase:firebase-iid-interop:17.1.0 +com.google.firebase:firebase-installations-interop:17.1.1 +com.google.firebase:firebase-installations:17.2.0 +com.google.firebase:firebase-measurement-connector:19.0.0 +com.google.firebase:firebase-messaging-ktx:23.3.0 +com.google.firebase:firebase-messaging:23.3.0 +com.google.firebase:firebase-perf-ktx:20.5.0 +com.google.firebase:firebase-perf:20.5.0 +com.google.firebase:firebase-sessions:1.1.0 +com.google.firebase:protolite-well-known-types:18.0.0 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:31.1-android +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.google.protobuf:protobuf-javalite:3.24.4 +com.google.protobuf:protobuf-kotlin-lite:3.24.4 +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.retrofit2:retrofit:2.9.0 +io.coil-kt:coil-base:2.4.0 +io.coil-kt:coil-compose-base:2.4.0 +io.coil-kt:coil-compose:2.4.0 +io.coil-kt:coil-svg:2.4.0 +io.coil-kt:coil:2.4.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.10 +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.10 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/prodDebugRuntimeClasspath.txt b/app/dependencies/prodDebugRuntimeClasspath.txt new file mode 100644 index 000000000..511aeaa02 --- /dev/null +++ b/app/dependencies/prodDebugRuntimeClasspath.txt @@ -0,0 +1,218 @@ +androidx.activity:activity-compose:1.8.0 +androidx.activity:activity-ktx:1.8.0 +androidx.activity:activity:1.8.0 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation-jvm:1.6.0 +androidx.annotation:annotation:1.6.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.appcompat:appcompat:1.6.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.browser:browser:1.6.0 +androidx.collection:collection-ktx:1.1.0 +androidx.collection:collection:1.2.0 +androidx.compose.animation:animation-android:1.5.4 +androidx.compose.animation:animation-core-android:1.5.4 +androidx.compose.animation:animation-core:1.5.4 +androidx.compose.animation:animation:1.5.4 +androidx.compose.foundation:foundation-android:1.5.4 +androidx.compose.foundation:foundation-layout-android:1.5.4 +androidx.compose.foundation:foundation-layout:1.5.4 +androidx.compose.foundation:foundation:1.5.4 +androidx.compose.material3:material3-window-size-class:1.1.2 +androidx.compose.material3:material3:1.1.2 +androidx.compose.material:material-android:1.5.4 +androidx.compose.material:material-icons-core-android:1.5.4 +androidx.compose.material:material-icons-core:1.5.4 +androidx.compose.material:material-icons-extended-android:1.5.4 +androidx.compose.material:material-icons-extended:1.5.4 +androidx.compose.material:material-ripple-android:1.5.4 +androidx.compose.material:material-ripple:1.5.4 +androidx.compose.material:material:1.5.4 +androidx.compose.runtime:runtime-android:1.5.4 +androidx.compose.runtime:runtime-livedata: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-alpha03 +androidx.compose.runtime:runtime:1.5.4 +androidx.compose.ui:ui-android:1.5.4 +androidx.compose.ui:ui-geometry-android:1.5.4 +androidx.compose.ui:ui-geometry:1.5.4 +androidx.compose.ui:ui-graphics-android:1.5.4 +androidx.compose.ui:ui-graphics:1.5.4 +androidx.compose.ui:ui-test-manifest:1.5.4 +androidx.compose.ui:ui-text-android:1.5.4 +androidx.compose.ui:ui-text:1.5.4 +androidx.compose.ui:ui-tooling-android:1.5.4 +androidx.compose.ui:ui-tooling-data-android:1.5.4 +androidx.compose.ui:ui-tooling-data:1.5.4 +androidx.compose.ui:ui-tooling-preview-android:1.5.4 +androidx.compose.ui:ui-tooling-preview:1.5.4 +androidx.compose.ui:ui-tooling:1.5.4 +androidx.compose.ui:ui-unit-android:1.5.4 +androidx.compose.ui:ui-unit:1.5.4 +androidx.compose.ui:ui-util-android:1.5.4 +androidx.compose.ui:ui-util:1.5.4 +androidx.compose.ui:ui:1.5.4 +androidx.compose:compose-bom:2023.10.01 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core-splashscreen:1.0.1 +androidx.core:core:1.12.0 +androidx.cursoradapter:cursoradapter:1.0.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.customview:customview:1.0.0 +androidx.datastore:datastore-core:1.0.0 +androidx.datastore:datastore-preferences-core:1.0.0 +androidx.datastore:datastore-preferences:1.0.0 +androidx.datastore:datastore:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.drawerlayout:drawerlayout:1.0.0 +androidx.emoji2:emoji2-views-helper:1.4.0 +androidx.emoji2:emoji2:1.4.0 +androidx.exifinterface:exifinterface:1.3.6 +androidx.fragment:fragment:1.5.1 +androidx.hilt:hilt-common:1.1.0 +androidx.hilt:hilt-navigation-compose:1.0.0 +androidx.hilt:hilt-navigation:1.0.0 +androidx.hilt:hilt-work:1.1.0 +androidx.interpolator:interpolator:1.0.0 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.6.2 +androidx.lifecycle:lifecycle-common:2.6.2 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata-core:2.6.2 +androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata:2.6.2 +androidx.lifecycle:lifecycle-process:2.6.2 +androidx.lifecycle:lifecycle-runtime-compose:2.6.2 +androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 +androidx.lifecycle:lifecycle-runtime:2.6.2 +androidx.lifecycle:lifecycle-service:2.6.2 +androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.metrics:metrics-performance:1.0.0-alpha04 +androidx.navigation:navigation-common-ktx:2.7.4 +androidx.navigation:navigation-common:2.7.4 +androidx.navigation:navigation-compose:2.7.4 +androidx.navigation:navigation-runtime-ktx:2.7.4 +androidx.navigation:navigation-runtime:2.7.4 +androidx.print:print:1.0.0 +androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 +androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.resourceinspection:resourceinspection-annotation:1.0.1 +androidx.room:room-common:2.6.0 +androidx.room:room-ktx:2.6.0 +androidx.room:room-runtime:2.6.0 +androidx.savedstate:savedstate-ktx:1.2.1 +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.2.0-alpha02 +androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 +androidx.tracing:tracing-perfetto:1.0.0-alpha11 +androidx.tracing:tracing:1.2.0-alpha02 +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.extensions.core:core:1.0.0 +androidx.window:window:1.1.0 +androidx.work:work-runtime-ktx:2.9.0-rc01 +androidx.work:work-runtime:2.9.0-rc01 +com.caverock:androidsvg-aar:1.4 +com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-permissions:0.32.0 +com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-backend-cct:3.1.9 +com.google.android.datatransport:transport-runtime:3.1.9 +com.google.android.gms:play-services-ads-identifier:18.0.0 +com.google.android.gms:play-services-base:18.0.1 +com.google.android.gms:play-services-basement:18.1.0 +com.google.android.gms:play-services-cloud-messaging:17.0.1 +com.google.android.gms:play-services-measurement-api:21.4.0 +com.google.android.gms:play-services-measurement-base:21.4.0 +com.google.android.gms:play-services-measurement-impl:21.4.0 +com.google.android.gms:play-services-measurement-sdk-api:21.4.0 +com.google.android.gms:play-services-measurement-sdk:21.4.0 +com.google.android.gms:play-services-measurement:21.4.0 +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.48.1 +com.google.dagger:dagger:2.48.1 +com.google.dagger:hilt-android:2.48.1 +com.google.dagger:hilt-core:2.48.1 +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 +com.google.firebase:firebase-analytics:21.4.0 +com.google.firebase:firebase-annotations:16.2.0 +com.google.firebase:firebase-bom:32.4.0 +com.google.firebase:firebase-common-ktx:20.4.2 +com.google.firebase:firebase-common:20.4.2 +com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-config:21.5.0 +com.google.firebase:firebase-crashlytics-ktx:18.5.0 +com.google.firebase:firebase-crashlytics:18.5.0 +com.google.firebase:firebase-datatransport:18.1.8 +com.google.firebase:firebase-encoders-json:18.0.1 +com.google.firebase:firebase-encoders-proto:16.0.0 +com.google.firebase:firebase-encoders:17.0.0 +com.google.firebase:firebase-iid-interop:17.1.0 +com.google.firebase:firebase-installations-interop:17.1.1 +com.google.firebase:firebase-installations:17.2.0 +com.google.firebase:firebase-measurement-connector:19.0.0 +com.google.firebase:firebase-messaging-ktx:23.3.0 +com.google.firebase:firebase-messaging:23.3.0 +com.google.firebase:firebase-perf-ktx:20.5.0 +com.google.firebase:firebase-perf:20.5.0 +com.google.firebase:firebase-sessions:1.1.0 +com.google.firebase:protolite-well-known-types:18.0.0 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:31.1-android +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.google.protobuf:protobuf-javalite:3.24.4 +com.google.protobuf:protobuf-kotlin-lite:3.24.4 +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.retrofit2:retrofit:2.9.0 +io.coil-kt:coil-base:2.4.0 +io.coil-kt:coil-compose-base:2.4.0 +io.coil-kt:coil-compose:2.4.0 +io.coil-kt:coil-svg:2.4.0 +io.coil-kt:coil:2.4.0 +io.github.aakira:napier-android-debug: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.10 +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.10 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt new file mode 100644 index 000000000..8123217b9 --- /dev/null +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -0,0 +1,211 @@ +androidx.activity:activity-compose:1.8.0 +androidx.activity:activity-ktx:1.8.0 +androidx.activity:activity:1.8.0 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation-jvm:1.6.0 +androidx.annotation:annotation:1.6.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.appcompat:appcompat:1.6.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.browser:browser:1.6.0 +androidx.collection:collection-ktx:1.1.0 +androidx.collection:collection:1.2.0 +androidx.compose.animation:animation-android:1.5.4 +androidx.compose.animation:animation-core-android:1.5.4 +androidx.compose.animation:animation-core:1.5.4 +androidx.compose.animation:animation:1.5.4 +androidx.compose.foundation:foundation-android:1.5.4 +androidx.compose.foundation:foundation-layout-android:1.5.4 +androidx.compose.foundation:foundation-layout:1.5.4 +androidx.compose.foundation:foundation:1.5.4 +androidx.compose.material3:material3-window-size-class:1.1.2 +androidx.compose.material3:material3:1.1.2 +androidx.compose.material:material-icons-core-android:1.5.4 +androidx.compose.material:material-icons-core:1.5.4 +androidx.compose.material:material-icons-extended-android:1.5.4 +androidx.compose.material:material-icons-extended:1.5.4 +androidx.compose.material:material-ripple-android:1.5.4 +androidx.compose.material:material-ripple:1.5.4 +androidx.compose.runtime:runtime-android:1.5.4 +androidx.compose.runtime:runtime-livedata: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-alpha03 +androidx.compose.runtime:runtime:1.5.4 +androidx.compose.ui:ui-android:1.5.4 +androidx.compose.ui:ui-geometry-android:1.5.4 +androidx.compose.ui:ui-geometry:1.5.4 +androidx.compose.ui:ui-graphics-android:1.5.4 +androidx.compose.ui:ui-graphics:1.5.4 +androidx.compose.ui:ui-text-android:1.5.4 +androidx.compose.ui:ui-text:1.5.4 +androidx.compose.ui:ui-tooling-preview-android:1.5.4 +androidx.compose.ui:ui-tooling-preview:1.5.4 +androidx.compose.ui:ui-unit-android:1.5.4 +androidx.compose.ui:ui-unit:1.5.4 +androidx.compose.ui:ui-util-android:1.5.4 +androidx.compose.ui:ui-util:1.5.4 +androidx.compose.ui:ui:1.5.4 +androidx.compose:compose-bom:2023.10.01 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core-splashscreen:1.0.1 +androidx.core:core:1.12.0 +androidx.cursoradapter:cursoradapter:1.0.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.customview:customview:1.0.0 +androidx.datastore:datastore-core:1.0.0 +androidx.datastore:datastore-preferences-core:1.0.0 +androidx.datastore:datastore-preferences:1.0.0 +androidx.datastore:datastore:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.drawerlayout:drawerlayout:1.0.0 +androidx.emoji2:emoji2-views-helper:1.4.0 +androidx.emoji2:emoji2:1.4.0 +androidx.exifinterface:exifinterface:1.3.6 +androidx.fragment:fragment:1.5.1 +androidx.hilt:hilt-common:1.1.0 +androidx.hilt:hilt-navigation-compose:1.0.0 +androidx.hilt:hilt-navigation:1.0.0 +androidx.hilt:hilt-work:1.1.0 +androidx.interpolator:interpolator:1.0.0 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.6.2 +androidx.lifecycle:lifecycle-common:2.6.2 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata-core:2.6.2 +androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 +androidx.lifecycle:lifecycle-livedata:2.6.2 +androidx.lifecycle:lifecycle-process:2.6.2 +androidx.lifecycle:lifecycle-runtime-compose:2.6.2 +androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 +androidx.lifecycle:lifecycle-runtime:2.6.2 +androidx.lifecycle:lifecycle-service:2.6.2 +androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.metrics:metrics-performance:1.0.0-alpha04 +androidx.navigation:navigation-common-ktx:2.7.4 +androidx.navigation:navigation-common:2.7.4 +androidx.navigation:navigation-compose:2.7.4 +androidx.navigation:navigation-runtime-ktx:2.7.4 +androidx.navigation:navigation-runtime:2.7.4 +androidx.print:print:1.0.0 +androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 +androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.resourceinspection:resourceinspection-annotation:1.0.1 +androidx.room:room-common:2.6.0 +androidx.room:room-ktx:2.6.0 +androidx.room:room-runtime:2.6.0 +androidx.savedstate:savedstate-ktx:1.2.1 +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.2.0-alpha02 +androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 +androidx.tracing:tracing-perfetto:1.0.0-alpha11 +androidx.tracing:tracing:1.2.0-alpha02 +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.extensions.core:core:1.0.0 +androidx.window:window:1.1.0 +androidx.work:work-runtime-ktx:2.9.0-rc01 +androidx.work:work-runtime:2.9.0-rc01 +com.caverock:androidsvg-aar:1.4 +com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-permissions:0.32.0 +com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-backend-cct:3.1.9 +com.google.android.datatransport:transport-runtime:3.1.9 +com.google.android.gms:play-services-ads-identifier:18.0.0 +com.google.android.gms:play-services-base:18.0.1 +com.google.android.gms:play-services-basement:18.1.0 +com.google.android.gms:play-services-cloud-messaging:17.0.1 +com.google.android.gms:play-services-measurement-api:21.4.0 +com.google.android.gms:play-services-measurement-base:21.4.0 +com.google.android.gms:play-services-measurement-impl:21.4.0 +com.google.android.gms:play-services-measurement-sdk-api:21.4.0 +com.google.android.gms:play-services-measurement-sdk:21.4.0 +com.google.android.gms:play-services-measurement:21.4.0 +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.48.1 +com.google.dagger:dagger:2.48.1 +com.google.dagger:hilt-android:2.48.1 +com.google.dagger:hilt-core:2.48.1 +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 +com.google.firebase:firebase-analytics:21.4.0 +com.google.firebase:firebase-annotations:16.2.0 +com.google.firebase:firebase-bom:32.4.0 +com.google.firebase:firebase-common-ktx:20.4.2 +com.google.firebase:firebase-common:20.4.2 +com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-config:21.5.0 +com.google.firebase:firebase-crashlytics-ktx:18.5.0 +com.google.firebase:firebase-crashlytics:18.5.0 +com.google.firebase:firebase-datatransport:18.1.8 +com.google.firebase:firebase-encoders-json:18.0.1 +com.google.firebase:firebase-encoders-proto:16.0.0 +com.google.firebase:firebase-encoders:17.0.0 +com.google.firebase:firebase-iid-interop:17.1.0 +com.google.firebase:firebase-installations-interop:17.1.1 +com.google.firebase:firebase-installations:17.2.0 +com.google.firebase:firebase-measurement-connector:19.0.0 +com.google.firebase:firebase-messaging-ktx:23.3.0 +com.google.firebase:firebase-messaging:23.3.0 +com.google.firebase:firebase-perf-ktx:20.5.0 +com.google.firebase:firebase-perf:20.5.0 +com.google.firebase:firebase-sessions:1.1.0 +com.google.firebase:protolite-well-known-types:18.0.0 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:31.1-android +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.google.protobuf:protobuf-javalite:3.24.4 +com.google.protobuf:protobuf-kotlin-lite:3.24.4 +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.retrofit2:retrofit:2.9.0 +io.coil-kt:coil-base:2.4.0 +io.coil-kt:coil-compose-base:2.4.0 +io.coil-kt:coil-compose:2.4.0 +io.coil-kt:coil-svg:2.4.0 +io.coil-kt:coil:2.4.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.10 +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.10 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains:annotations:23.0.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9ee708385..5ae00c3d3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,6 +31,7 @@ androidxUiAutomator = "2.2.0" androidxWindowManager = "1.1.0" androidxWork = "2.9.0-rc01" coil = "2.4.0" +dependencyGuard = "0.4.3" firebaseBom = "32.4.0" firebaseCrashlyticsPlugin = "2.9.9" firebasePerfPlugin = "1.4.2" @@ -165,6 +166,7 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } +dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From 08a5796caaa641176468aad28f2e1bd84d5aa4dd Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Tue, 14 Nov 2023 09:56:10 -0800 Subject: [PATCH 09/70] Fix alphabetic ordering Change-Id: Ie0779252d969b80b8c8bcea6d58c13132e20c13c --- 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 5ae00c3d3..529029bb2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -156,6 +156,7 @@ android-application = { id = "com.android.application", version.ref = "androidGr android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidxMacroBenchmark"} +dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" } firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerfPlugin" } gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" } @@ -166,7 +167,6 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } -dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From e00f7b14380f746353af7e2f8aa6bae786f0ccab Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Thu, 16 Nov 2023 10:58:30 -0800 Subject: [PATCH 10/70] Remove empty file Change-Id: Iaed9b4a68c45c1f52a2311d79b34569d20aaea62 --- app-nia-catalog/dependencies/releaseRuntimeClasspath.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app-nia-catalog/dependencies/releaseRuntimeClasspath.txt diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt deleted file mode 100644 index e69de29bb..000000000 From d15a203c5929c7a8d850f2e9e9c279a3b10aae22 Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Mon, 20 Nov 2023 10:28:35 -0800 Subject: [PATCH 11/70] Only record prodReleaseRuntimeClasspath configuration Change-Id: I129fc4839ac2b4d5624c91cd25b47efba8847f44 --- app/build.gradle.kts | 7 - .../demoDebugRuntimeClasspath.txt | 218 ------------------ .../demoReleaseRuntimeClasspath.txt | 211 ----------------- .../prodDebugRuntimeClasspath.txt | 218 ------------------ 4 files changed, 654 deletions(-) delete mode 100644 app/dependencies/demoDebugRuntimeClasspath.txt delete mode 100644 app/dependencies/demoReleaseRuntimeClasspath.txt delete mode 100644 app/dependencies/prodDebugRuntimeClasspath.txt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 319b2b4b6..755b4c7fe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -147,12 +147,5 @@ baselineProfile { } dependencyGuard { - - configuration("demoDebugRuntimeClasspath") - - configuration("demoReleaseRuntimeClasspath") - - configuration("prodDebugRuntimeClasspath") - configuration("prodReleaseRuntimeClasspath") } diff --git a/app/dependencies/demoDebugRuntimeClasspath.txt b/app/dependencies/demoDebugRuntimeClasspath.txt deleted file mode 100644 index 511aeaa02..000000000 --- a/app/dependencies/demoDebugRuntimeClasspath.txt +++ /dev/null @@ -1,218 +0,0 @@ -androidx.activity:activity-compose:1.8.0 -androidx.activity:activity-ktx:1.8.0 -androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 -androidx.arch.core:core-common:2.2.0 -androidx.arch.core:core-runtime:2.2.0 -androidx.autofill:autofill:1.0.0 -androidx.browser:browser:1.6.0 -androidx.collection:collection-ktx:1.1.0 -androidx.collection:collection:1.2.0 -androidx.compose.animation:animation-android:1.5.4 -androidx.compose.animation:animation-core-android:1.5.4 -androidx.compose.animation:animation-core:1.5.4 -androidx.compose.animation:animation:1.5.4 -androidx.compose.foundation:foundation-android:1.5.4 -androidx.compose.foundation:foundation-layout-android:1.5.4 -androidx.compose.foundation:foundation-layout:1.5.4 -androidx.compose.foundation:foundation:1.5.4 -androidx.compose.material3:material3-window-size-class:1.1.2 -androidx.compose.material3:material3:1.1.2 -androidx.compose.material:material-android:1.5.4 -androidx.compose.material:material-icons-core-android:1.5.4 -androidx.compose.material:material-icons-core:1.5.4 -androidx.compose.material:material-icons-extended-android:1.5.4 -androidx.compose.material:material-icons-extended:1.5.4 -androidx.compose.material:material-ripple-android:1.5.4 -androidx.compose.material:material-ripple:1.5.4 -androidx.compose.material:material:1.5.4 -androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-livedata: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-alpha03 -androidx.compose.runtime:runtime:1.5.4 -androidx.compose.ui:ui-android:1.5.4 -androidx.compose.ui:ui-geometry-android:1.5.4 -androidx.compose.ui:ui-geometry:1.5.4 -androidx.compose.ui:ui-graphics-android:1.5.4 -androidx.compose.ui:ui-graphics:1.5.4 -androidx.compose.ui:ui-test-manifest:1.5.4 -androidx.compose.ui:ui-text-android:1.5.4 -androidx.compose.ui:ui-text:1.5.4 -androidx.compose.ui:ui-tooling-android:1.5.4 -androidx.compose.ui:ui-tooling-data-android:1.5.4 -androidx.compose.ui:ui-tooling-data:1.5.4 -androidx.compose.ui:ui-tooling-preview-android:1.5.4 -androidx.compose.ui:ui-tooling-preview:1.5.4 -androidx.compose.ui:ui-tooling:1.5.4 -androidx.compose.ui:ui-unit-android:1.5.4 -androidx.compose.ui:ui-unit:1.5.4 -androidx.compose.ui:ui-util-android:1.5.4 -androidx.compose.ui:ui-util:1.5.4 -androidx.compose.ui:ui:1.5.4 -androidx.compose:compose-bom:2023.10.01 -androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.12.0 -androidx.core:core-splashscreen:1.0.1 -androidx.core:core:1.12.0 -androidx.cursoradapter:cursoradapter:1.0.0 -androidx.customview:customview-poolingcontainer:1.0.0 -androidx.customview:customview:1.0.0 -androidx.datastore:datastore-core:1.0.0 -androidx.datastore:datastore-preferences-core:1.0.0 -androidx.datastore:datastore-preferences:1.0.0 -androidx.datastore:datastore:1.0.0 -androidx.documentfile:documentfile:1.0.0 -androidx.drawerlayout:drawerlayout:1.0.0 -androidx.emoji2:emoji2-views-helper:1.4.0 -androidx.emoji2:emoji2:1.4.0 -androidx.exifinterface:exifinterface:1.3.6 -androidx.fragment:fragment:1.5.1 -androidx.hilt:hilt-common:1.1.0 -androidx.hilt:hilt-navigation-compose:1.0.0 -androidx.hilt:hilt-navigation:1.0.0 -androidx.hilt:hilt-work:1.1.0 -androidx.interpolator:interpolator:1.0.0 -androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.2 -androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata:2.6.2 -androidx.lifecycle:lifecycle-process:2.6.2 -androidx.lifecycle:lifecycle-runtime-compose:2.6.2 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -androidx.lifecycle:lifecycle-runtime:2.6.2 -androidx.lifecycle:lifecycle-service:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 -androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 -androidx.metrics:metrics-performance:1.0.0-alpha04 -androidx.navigation:navigation-common-ktx:2.7.4 -androidx.navigation:navigation-common:2.7.4 -androidx.navigation:navigation-compose:2.7.4 -androidx.navigation:navigation-runtime-ktx:2.7.4 -androidx.navigation:navigation-runtime:2.7.4 -androidx.print:print:1.0.0 -androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 -androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 -androidx.profileinstaller:profileinstaller:1.3.1 -androidx.resourceinspection:resourceinspection-annotation:1.0.1 -androidx.room:room-common:2.6.0 -androidx.room:room-ktx:2.6.0 -androidx.room:room-runtime:2.6.0 -androidx.savedstate:savedstate-ktx:1.2.1 -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.2.0-alpha02 -androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 -androidx.tracing:tracing-perfetto:1.0.0-alpha11 -androidx.tracing:tracing:1.2.0-alpha02 -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.extensions.core:core:1.0.0 -androidx.window:window:1.1.0 -androidx.work:work-runtime-ktx:2.9.0-rc01 -androidx.work:work-runtime:2.9.0-rc01 -com.caverock:androidsvg-aar:1.4 -com.google.accompanist:accompanist-drawablepainter:0.30.1 -com.google.accompanist:accompanist-permissions:0.32.0 -com.google.android.datatransport:transport-api:3.0.0 -com.google.android.datatransport:transport-backend-cct:3.1.9 -com.google.android.datatransport:transport-runtime:3.1.9 -com.google.android.gms:play-services-ads-identifier:18.0.0 -com.google.android.gms:play-services-base:18.0.1 -com.google.android.gms:play-services-basement:18.1.0 -com.google.android.gms:play-services-cloud-messaging:17.0.1 -com.google.android.gms:play-services-measurement-api:21.4.0 -com.google.android.gms:play-services-measurement-base:21.4.0 -com.google.android.gms:play-services-measurement-impl:21.4.0 -com.google.android.gms:play-services-measurement-sdk-api:21.4.0 -com.google.android.gms:play-services-measurement-sdk:21.4.0 -com.google.android.gms:play-services-measurement:21.4.0 -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.48.1 -com.google.dagger:dagger:2.48.1 -com.google.dagger:hilt-android:2.48.1 -com.google.dagger:hilt-core:2.48.1 -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 -com.google.firebase:firebase-analytics:21.4.0 -com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:32.4.0 -com.google.firebase:firebase-common-ktx:20.4.2 -com.google.firebase:firebase-common:20.4.2 -com.google.firebase:firebase-components:17.1.5 -com.google.firebase:firebase-config:21.5.0 -com.google.firebase:firebase-crashlytics-ktx:18.5.0 -com.google.firebase:firebase-crashlytics:18.5.0 -com.google.firebase:firebase-datatransport:18.1.8 -com.google.firebase:firebase-encoders-json:18.0.1 -com.google.firebase:firebase-encoders-proto:16.0.0 -com.google.firebase:firebase-encoders:17.0.0 -com.google.firebase:firebase-iid-interop:17.1.0 -com.google.firebase:firebase-installations-interop:17.1.1 -com.google.firebase:firebase-installations:17.2.0 -com.google.firebase:firebase-measurement-connector:19.0.0 -com.google.firebase:firebase-messaging-ktx:23.3.0 -com.google.firebase:firebase-messaging:23.3.0 -com.google.firebase:firebase-perf-ktx:20.5.0 -com.google.firebase:firebase-perf:20.5.0 -com.google.firebase:firebase-sessions:1.1.0 -com.google.firebase:protolite-well-known-types:18.0.0 -com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:31.1-android -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava -com.google.j2objc:j2objc-annotations:1.3 -com.google.protobuf:protobuf-javalite:3.24.4 -com.google.protobuf:protobuf-kotlin-lite:3.24.4 -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.retrofit2:retrofit:2.9.0 -io.coil-kt:coil-base:2.4.0 -io.coil-kt:coil-compose-base:2.4.0 -io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil-svg:2.4.0 -io.coil-kt:coil:2.4.0 -io.github.aakira:napier-android-debug: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.10 -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.10 -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 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 -org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 -org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 -org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/demoReleaseRuntimeClasspath.txt b/app/dependencies/demoReleaseRuntimeClasspath.txt deleted file mode 100644 index 8123217b9..000000000 --- a/app/dependencies/demoReleaseRuntimeClasspath.txt +++ /dev/null @@ -1,211 +0,0 @@ -androidx.activity:activity-compose:1.8.0 -androidx.activity:activity-ktx:1.8.0 -androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 -androidx.arch.core:core-common:2.2.0 -androidx.arch.core:core-runtime:2.2.0 -androidx.autofill:autofill:1.0.0 -androidx.browser:browser:1.6.0 -androidx.collection:collection-ktx:1.1.0 -androidx.collection:collection:1.2.0 -androidx.compose.animation:animation-android:1.5.4 -androidx.compose.animation:animation-core-android:1.5.4 -androidx.compose.animation:animation-core:1.5.4 -androidx.compose.animation:animation:1.5.4 -androidx.compose.foundation:foundation-android:1.5.4 -androidx.compose.foundation:foundation-layout-android:1.5.4 -androidx.compose.foundation:foundation-layout:1.5.4 -androidx.compose.foundation:foundation:1.5.4 -androidx.compose.material3:material3-window-size-class:1.1.2 -androidx.compose.material3:material3:1.1.2 -androidx.compose.material:material-icons-core-android:1.5.4 -androidx.compose.material:material-icons-core:1.5.4 -androidx.compose.material:material-icons-extended-android:1.5.4 -androidx.compose.material:material-icons-extended:1.5.4 -androidx.compose.material:material-ripple-android:1.5.4 -androidx.compose.material:material-ripple:1.5.4 -androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-livedata: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-alpha03 -androidx.compose.runtime:runtime:1.5.4 -androidx.compose.ui:ui-android:1.5.4 -androidx.compose.ui:ui-geometry-android:1.5.4 -androidx.compose.ui:ui-geometry:1.5.4 -androidx.compose.ui:ui-graphics-android:1.5.4 -androidx.compose.ui:ui-graphics:1.5.4 -androidx.compose.ui:ui-text-android:1.5.4 -androidx.compose.ui:ui-text:1.5.4 -androidx.compose.ui:ui-tooling-preview-android:1.5.4 -androidx.compose.ui:ui-tooling-preview:1.5.4 -androidx.compose.ui:ui-unit-android:1.5.4 -androidx.compose.ui:ui-unit:1.5.4 -androidx.compose.ui:ui-util-android:1.5.4 -androidx.compose.ui:ui-util:1.5.4 -androidx.compose.ui:ui:1.5.4 -androidx.compose:compose-bom:2023.10.01 -androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.12.0 -androidx.core:core-splashscreen:1.0.1 -androidx.core:core:1.12.0 -androidx.cursoradapter:cursoradapter:1.0.0 -androidx.customview:customview-poolingcontainer:1.0.0 -androidx.customview:customview:1.0.0 -androidx.datastore:datastore-core:1.0.0 -androidx.datastore:datastore-preferences-core:1.0.0 -androidx.datastore:datastore-preferences:1.0.0 -androidx.datastore:datastore:1.0.0 -androidx.documentfile:documentfile:1.0.0 -androidx.drawerlayout:drawerlayout:1.0.0 -androidx.emoji2:emoji2-views-helper:1.4.0 -androidx.emoji2:emoji2:1.4.0 -androidx.exifinterface:exifinterface:1.3.6 -androidx.fragment:fragment:1.5.1 -androidx.hilt:hilt-common:1.1.0 -androidx.hilt:hilt-navigation-compose:1.0.0 -androidx.hilt:hilt-navigation:1.0.0 -androidx.hilt:hilt-work:1.1.0 -androidx.interpolator:interpolator:1.0.0 -androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.2 -androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata:2.6.2 -androidx.lifecycle:lifecycle-process:2.6.2 -androidx.lifecycle:lifecycle-runtime-compose:2.6.2 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -androidx.lifecycle:lifecycle-runtime:2.6.2 -androidx.lifecycle:lifecycle-service:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 -androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 -androidx.metrics:metrics-performance:1.0.0-alpha04 -androidx.navigation:navigation-common-ktx:2.7.4 -androidx.navigation:navigation-common:2.7.4 -androidx.navigation:navigation-compose:2.7.4 -androidx.navigation:navigation-runtime-ktx:2.7.4 -androidx.navigation:navigation-runtime:2.7.4 -androidx.print:print:1.0.0 -androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 -androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 -androidx.profileinstaller:profileinstaller:1.3.1 -androidx.resourceinspection:resourceinspection-annotation:1.0.1 -androidx.room:room-common:2.6.0 -androidx.room:room-ktx:2.6.0 -androidx.room:room-runtime:2.6.0 -androidx.savedstate:savedstate-ktx:1.2.1 -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.2.0-alpha02 -androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 -androidx.tracing:tracing-perfetto:1.0.0-alpha11 -androidx.tracing:tracing:1.2.0-alpha02 -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.extensions.core:core:1.0.0 -androidx.window:window:1.1.0 -androidx.work:work-runtime-ktx:2.9.0-rc01 -androidx.work:work-runtime:2.9.0-rc01 -com.caverock:androidsvg-aar:1.4 -com.google.accompanist:accompanist-drawablepainter:0.30.1 -com.google.accompanist:accompanist-permissions:0.32.0 -com.google.android.datatransport:transport-api:3.0.0 -com.google.android.datatransport:transport-backend-cct:3.1.9 -com.google.android.datatransport:transport-runtime:3.1.9 -com.google.android.gms:play-services-ads-identifier:18.0.0 -com.google.android.gms:play-services-base:18.0.1 -com.google.android.gms:play-services-basement:18.1.0 -com.google.android.gms:play-services-cloud-messaging:17.0.1 -com.google.android.gms:play-services-measurement-api:21.4.0 -com.google.android.gms:play-services-measurement-base:21.4.0 -com.google.android.gms:play-services-measurement-impl:21.4.0 -com.google.android.gms:play-services-measurement-sdk-api:21.4.0 -com.google.android.gms:play-services-measurement-sdk:21.4.0 -com.google.android.gms:play-services-measurement:21.4.0 -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.48.1 -com.google.dagger:dagger:2.48.1 -com.google.dagger:hilt-android:2.48.1 -com.google.dagger:hilt-core:2.48.1 -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 -com.google.firebase:firebase-analytics:21.4.0 -com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:32.4.0 -com.google.firebase:firebase-common-ktx:20.4.2 -com.google.firebase:firebase-common:20.4.2 -com.google.firebase:firebase-components:17.1.5 -com.google.firebase:firebase-config:21.5.0 -com.google.firebase:firebase-crashlytics-ktx:18.5.0 -com.google.firebase:firebase-crashlytics:18.5.0 -com.google.firebase:firebase-datatransport:18.1.8 -com.google.firebase:firebase-encoders-json:18.0.1 -com.google.firebase:firebase-encoders-proto:16.0.0 -com.google.firebase:firebase-encoders:17.0.0 -com.google.firebase:firebase-iid-interop:17.1.0 -com.google.firebase:firebase-installations-interop:17.1.1 -com.google.firebase:firebase-installations:17.2.0 -com.google.firebase:firebase-measurement-connector:19.0.0 -com.google.firebase:firebase-messaging-ktx:23.3.0 -com.google.firebase:firebase-messaging:23.3.0 -com.google.firebase:firebase-perf-ktx:20.5.0 -com.google.firebase:firebase-perf:20.5.0 -com.google.firebase:firebase-sessions:1.1.0 -com.google.firebase:protolite-well-known-types:18.0.0 -com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:31.1-android -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava -com.google.j2objc:j2objc-annotations:1.3 -com.google.protobuf:protobuf-javalite:3.24.4 -com.google.protobuf:protobuf-kotlin-lite:3.24.4 -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.retrofit2:retrofit:2.9.0 -io.coil-kt:coil-base:2.4.0 -io.coil-kt:coil-compose-base:2.4.0 -io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil-svg:2.4.0 -io.coil-kt:coil:2.4.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.10 -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.10 -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 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 -org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 -org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 -org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/prodDebugRuntimeClasspath.txt b/app/dependencies/prodDebugRuntimeClasspath.txt deleted file mode 100644 index 511aeaa02..000000000 --- a/app/dependencies/prodDebugRuntimeClasspath.txt +++ /dev/null @@ -1,218 +0,0 @@ -androidx.activity:activity-compose:1.8.0 -androidx.activity:activity-ktx:1.8.0 -androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 -androidx.arch.core:core-common:2.2.0 -androidx.arch.core:core-runtime:2.2.0 -androidx.autofill:autofill:1.0.0 -androidx.browser:browser:1.6.0 -androidx.collection:collection-ktx:1.1.0 -androidx.collection:collection:1.2.0 -androidx.compose.animation:animation-android:1.5.4 -androidx.compose.animation:animation-core-android:1.5.4 -androidx.compose.animation:animation-core:1.5.4 -androidx.compose.animation:animation:1.5.4 -androidx.compose.foundation:foundation-android:1.5.4 -androidx.compose.foundation:foundation-layout-android:1.5.4 -androidx.compose.foundation:foundation-layout:1.5.4 -androidx.compose.foundation:foundation:1.5.4 -androidx.compose.material3:material3-window-size-class:1.1.2 -androidx.compose.material3:material3:1.1.2 -androidx.compose.material:material-android:1.5.4 -androidx.compose.material:material-icons-core-android:1.5.4 -androidx.compose.material:material-icons-core:1.5.4 -androidx.compose.material:material-icons-extended-android:1.5.4 -androidx.compose.material:material-icons-extended:1.5.4 -androidx.compose.material:material-ripple-android:1.5.4 -androidx.compose.material:material-ripple:1.5.4 -androidx.compose.material:material:1.5.4 -androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-livedata: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-alpha03 -androidx.compose.runtime:runtime:1.5.4 -androidx.compose.ui:ui-android:1.5.4 -androidx.compose.ui:ui-geometry-android:1.5.4 -androidx.compose.ui:ui-geometry:1.5.4 -androidx.compose.ui:ui-graphics-android:1.5.4 -androidx.compose.ui:ui-graphics:1.5.4 -androidx.compose.ui:ui-test-manifest:1.5.4 -androidx.compose.ui:ui-text-android:1.5.4 -androidx.compose.ui:ui-text:1.5.4 -androidx.compose.ui:ui-tooling-android:1.5.4 -androidx.compose.ui:ui-tooling-data-android:1.5.4 -androidx.compose.ui:ui-tooling-data:1.5.4 -androidx.compose.ui:ui-tooling-preview-android:1.5.4 -androidx.compose.ui:ui-tooling-preview:1.5.4 -androidx.compose.ui:ui-tooling:1.5.4 -androidx.compose.ui:ui-unit-android:1.5.4 -androidx.compose.ui:ui-unit:1.5.4 -androidx.compose.ui:ui-util-android:1.5.4 -androidx.compose.ui:ui-util:1.5.4 -androidx.compose.ui:ui:1.5.4 -androidx.compose:compose-bom:2023.10.01 -androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.12.0 -androidx.core:core-splashscreen:1.0.1 -androidx.core:core:1.12.0 -androidx.cursoradapter:cursoradapter:1.0.0 -androidx.customview:customview-poolingcontainer:1.0.0 -androidx.customview:customview:1.0.0 -androidx.datastore:datastore-core:1.0.0 -androidx.datastore:datastore-preferences-core:1.0.0 -androidx.datastore:datastore-preferences:1.0.0 -androidx.datastore:datastore:1.0.0 -androidx.documentfile:documentfile:1.0.0 -androidx.drawerlayout:drawerlayout:1.0.0 -androidx.emoji2:emoji2-views-helper:1.4.0 -androidx.emoji2:emoji2:1.4.0 -androidx.exifinterface:exifinterface:1.3.6 -androidx.fragment:fragment:1.5.1 -androidx.hilt:hilt-common:1.1.0 -androidx.hilt:hilt-navigation-compose:1.0.0 -androidx.hilt:hilt-navigation:1.0.0 -androidx.hilt:hilt-work:1.1.0 -androidx.interpolator:interpolator:1.0.0 -androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.2 -androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 -androidx.lifecycle:lifecycle-livedata:2.6.2 -androidx.lifecycle:lifecycle-process:2.6.2 -androidx.lifecycle:lifecycle-runtime-compose:2.6.2 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -androidx.lifecycle:lifecycle-runtime:2.6.2 -androidx.lifecycle:lifecycle-service:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 -androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 -androidx.metrics:metrics-performance:1.0.0-alpha04 -androidx.navigation:navigation-common-ktx:2.7.4 -androidx.navigation:navigation-common:2.7.4 -androidx.navigation:navigation-compose:2.7.4 -androidx.navigation:navigation-runtime-ktx:2.7.4 -androidx.navigation:navigation-runtime:2.7.4 -androidx.print:print:1.0.0 -androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 -androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 -androidx.profileinstaller:profileinstaller:1.3.1 -androidx.resourceinspection:resourceinspection-annotation:1.0.1 -androidx.room:room-common:2.6.0 -androidx.room:room-ktx:2.6.0 -androidx.room:room-runtime:2.6.0 -androidx.savedstate:savedstate-ktx:1.2.1 -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.2.0-alpha02 -androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 -androidx.tracing:tracing-perfetto:1.0.0-alpha11 -androidx.tracing:tracing:1.2.0-alpha02 -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.extensions.core:core:1.0.0 -androidx.window:window:1.1.0 -androidx.work:work-runtime-ktx:2.9.0-rc01 -androidx.work:work-runtime:2.9.0-rc01 -com.caverock:androidsvg-aar:1.4 -com.google.accompanist:accompanist-drawablepainter:0.30.1 -com.google.accompanist:accompanist-permissions:0.32.0 -com.google.android.datatransport:transport-api:3.0.0 -com.google.android.datatransport:transport-backend-cct:3.1.9 -com.google.android.datatransport:transport-runtime:3.1.9 -com.google.android.gms:play-services-ads-identifier:18.0.0 -com.google.android.gms:play-services-base:18.0.1 -com.google.android.gms:play-services-basement:18.1.0 -com.google.android.gms:play-services-cloud-messaging:17.0.1 -com.google.android.gms:play-services-measurement-api:21.4.0 -com.google.android.gms:play-services-measurement-base:21.4.0 -com.google.android.gms:play-services-measurement-impl:21.4.0 -com.google.android.gms:play-services-measurement-sdk-api:21.4.0 -com.google.android.gms:play-services-measurement-sdk:21.4.0 -com.google.android.gms:play-services-measurement:21.4.0 -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.48.1 -com.google.dagger:dagger:2.48.1 -com.google.dagger:hilt-android:2.48.1 -com.google.dagger:hilt-core:2.48.1 -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 -com.google.firebase:firebase-analytics:21.4.0 -com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:32.4.0 -com.google.firebase:firebase-common-ktx:20.4.2 -com.google.firebase:firebase-common:20.4.2 -com.google.firebase:firebase-components:17.1.5 -com.google.firebase:firebase-config:21.5.0 -com.google.firebase:firebase-crashlytics-ktx:18.5.0 -com.google.firebase:firebase-crashlytics:18.5.0 -com.google.firebase:firebase-datatransport:18.1.8 -com.google.firebase:firebase-encoders-json:18.0.1 -com.google.firebase:firebase-encoders-proto:16.0.0 -com.google.firebase:firebase-encoders:17.0.0 -com.google.firebase:firebase-iid-interop:17.1.0 -com.google.firebase:firebase-installations-interop:17.1.1 -com.google.firebase:firebase-installations:17.2.0 -com.google.firebase:firebase-measurement-connector:19.0.0 -com.google.firebase:firebase-messaging-ktx:23.3.0 -com.google.firebase:firebase-messaging:23.3.0 -com.google.firebase:firebase-perf-ktx:20.5.0 -com.google.firebase:firebase-perf:20.5.0 -com.google.firebase:firebase-sessions:1.1.0 -com.google.firebase:protolite-well-known-types:18.0.0 -com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:31.1-android -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava -com.google.j2objc:j2objc-annotations:1.3 -com.google.protobuf:protobuf-javalite:3.24.4 -com.google.protobuf:protobuf-kotlin-lite:3.24.4 -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.retrofit2:retrofit:2.9.0 -io.coil-kt:coil-base:2.4.0 -io.coil-kt:coil-compose-base:2.4.0 -io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil-svg:2.4.0 -io.coil-kt:coil:2.4.0 -io.github.aakira:napier-android-debug: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.10 -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.10 -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 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 -org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 -org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 -org.jetbrains:annotations:23.0.0 From 392eaa3e61ce969f4a367ea8b76b84819a917ee1 Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Mon, 20 Nov 2023 10:29:14 -0800 Subject: [PATCH 12/70] Add dependencyGuard to AndroidApplicationConventionPlugin Change-Id: Ib695ca18512d0ccde4c67e101f09ebbff8897912 --- app/build.gradle.kts | 1 - .../src/main/kotlin/AndroidApplicationConventionPlugin.kt | 5 +++-- build.gradle.kts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 755b4c7fe..b003c4a54 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,7 +25,6 @@ plugins { alias(libs.plugins.nowinandroid.android.application.firebase) id("com.google.android.gms.oss-licenses-plugin") alias(libs.plugins.baselineprofile) - alias(libs.plugins.dependencyGuard) } android { diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt index f73ed1478..cd8bcfeb0 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -15,10 +15,10 @@ */ import com.android.build.api.dsl.ApplicationExtension -import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.gradle.BaseExtension import com.google.samples.apps.nowinandroid.configureBadgingTasks +import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask import org.gradle.api.Plugin @@ -33,6 +33,7 @@ class AndroidApplicationConventionPlugin : Plugin { apply("com.android.application") apply("org.jetbrains.kotlin.android") apply("nowinandroid.android.lint") + apply("com.dropbox.dependency-guard") } extensions.configure { @@ -47,4 +48,4 @@ class AndroidApplicationConventionPlugin : Plugin { } } -} \ No newline at end of file +} diff --git a/build.gradle.kts b/build.gradle.kts index 7b54f9058..a2c39f493 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,6 +36,7 @@ plugins { alias(libs.plugins.baselineprofile) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.dependencyGuard) apply false alias(libs.plugins.firebase.crashlytics) apply false alias(libs.plugins.firebase.perf) apply false alias(libs.plugins.gms) apply false From 6297b741e91296bdc4425d84de55a6aea3954545 Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Mon, 20 Nov 2023 10:55:55 -0800 Subject: [PATCH 13/70] Record dependencies for catalog releaseRuntimeClasspath Change-Id: Id74cc7f372498c4cea3fe221230d6042bfc10478 --- app-nia-catalog/build.gradle.kts | 4 + .../dependencies/releaseRuntimeClasspath.txt | 167 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 app-nia-catalog/dependencies/releaseRuntimeClasspath.txt diff --git a/app-nia-catalog/build.gradle.kts b/app-nia-catalog/build.gradle.kts index a90da1604..e02f6bc0b 100644 --- a/app-nia-catalog/build.gradle.kts +++ b/app-nia-catalog/build.gradle.kts @@ -69,3 +69,7 @@ dependencies { implementation(projects.core.ui) implementation(libs.androidx.activity.compose) } + +dependencyGuard { + configuration("releaseRuntimeClasspath") +} diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt new file mode 100644 index 000000000..786b15d57 --- /dev/null +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -0,0 +1,167 @@ +androidx.activity:activity-compose:1.8.0 +androidx.activity:activity-ktx:1.8.0 +androidx.activity:activity:1.8.0 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation-jvm:1.6.0 +androidx.annotation:annotation:1.6.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.browser:browser:1.6.0 +androidx.collection:collection:1.2.0 +androidx.compose.animation:animation-android:1.5.4 +androidx.compose.animation:animation-core-android:1.5.4 +androidx.compose.animation:animation-core:1.5.4 +androidx.compose.animation:animation:1.5.4 +androidx.compose.foundation:foundation-android:1.5.4 +androidx.compose.foundation:foundation-layout-android:1.5.4 +androidx.compose.foundation:foundation-layout:1.5.4 +androidx.compose.foundation:foundation:1.5.4 +androidx.compose.material3:material3:1.1.2 +androidx.compose.material:material-icons-core-android:1.5.4 +androidx.compose.material:material-icons-core:1.5.4 +androidx.compose.material:material-icons-extended-android:1.5.4 +androidx.compose.material:material-icons-extended:1.5.4 +androidx.compose.material:material-ripple-android:1.5.4 +androidx.compose.material:material-ripple:1.5.4 +androidx.compose.runtime:runtime-android:1.5.4 +androidx.compose.runtime:runtime-livedata:1.5.4 +androidx.compose.runtime:runtime-saveable-android:1.5.4 +androidx.compose.runtime:runtime-saveable:1.5.4 +androidx.compose.runtime:runtime:1.5.4 +androidx.compose.ui:ui-android:1.5.4 +androidx.compose.ui:ui-geometry-android:1.5.4 +androidx.compose.ui:ui-geometry:1.5.4 +androidx.compose.ui:ui-graphics-android:1.5.4 +androidx.compose.ui:ui-graphics:1.5.4 +androidx.compose.ui:ui-text-android:1.5.4 +androidx.compose.ui:ui-text:1.5.4 +androidx.compose.ui:ui-tooling-preview-android:1.5.4 +androidx.compose.ui:ui-tooling-preview:1.5.4 +androidx.compose.ui:ui-unit-android:1.5.4 +androidx.compose.ui:ui-unit:1.5.4 +androidx.compose.ui:ui-util-android:1.5.4 +androidx.compose.ui:ui-util:1.5.4 +androidx.compose.ui:ui:1.5.4 +androidx.compose:compose-bom:2023.10.01 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core:1.12.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.customview:customview:1.0.0 +androidx.datastore:datastore-core:1.0.0 +androidx.datastore:datastore:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.emoji2:emoji2:1.4.0 +androidx.exifinterface:exifinterface:1.3.6 +androidx.fragment:fragment:1.5.1 +androidx.interpolator:interpolator:1.0.0 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.6.1 +androidx.lifecycle:lifecycle-common:2.6.1 +androidx.lifecycle:lifecycle-livedata-core:2.6.1 +androidx.lifecycle:lifecycle-livedata:2.6.1 +androidx.lifecycle:lifecycle-process:2.6.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 +androidx.lifecycle:lifecycle-runtime:2.6.1 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 +androidx.lifecycle:lifecycle-viewmodel:2.6.1 +androidx.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.metrics:metrics-performance:1.0.0-alpha04 +androidx.print:print:1.0.0 +androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 +androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.room:room-common:2.6.0 +androidx.room:room-ktx:2.6.0 +androidx.room:room-runtime:2.6.0 +androidx.savedstate:savedstate-ktx:1.2.1 +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.vectordrawable:vectordrawable-animated:1.1.0 +androidx.vectordrawable:vectordrawable:1.1.0 +androidx.versionedparcelable:versionedparcelable:1.1.1 +androidx.viewpager:viewpager:1.0.0 +com.caverock:androidsvg-aar:1.4 +com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-backend-cct:3.1.8 +com.google.android.datatransport:transport-runtime:3.1.8 +com.google.android.gms:play-services-ads-identifier:18.0.0 +com.google.android.gms:play-services-base:18.0.1 +com.google.android.gms:play-services-basement:18.1.0 +com.google.android.gms:play-services-cloud-messaging:17.0.1 +com.google.android.gms:play-services-measurement-api:21.4.0 +com.google.android.gms:play-services-measurement-base:21.4.0 +com.google.android.gms:play-services-measurement-impl:21.4.0 +com.google.android.gms:play-services-measurement-sdk-api:21.4.0 +com.google.android.gms:play-services-measurement-sdk:21.4.0 +com.google.android.gms:play-services-measurement:21.4.0 +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.48.1 +com.google.dagger:dagger:2.48.1 +com.google.dagger:hilt-android:2.48.1 +com.google.dagger:hilt-core:2.48.1 +com.google.errorprone:error_prone_annotations:2.11.0 +com.google.firebase:firebase-analytics-ktx:21.4.0 +com.google.firebase:firebase-analytics:21.4.0 +com.google.firebase:firebase-annotations:16.2.0 +com.google.firebase:firebase-bom:32.4.0 +com.google.firebase:firebase-common-ktx:20.4.2 +com.google.firebase:firebase-common:20.4.2 +com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-datatransport:18.1.7 +com.google.firebase:firebase-encoders-json:18.0.0 +com.google.firebase:firebase-encoders-proto:16.0.0 +com.google.firebase:firebase-encoders:17.0.0 +com.google.firebase:firebase-iid-interop:17.1.0 +com.google.firebase:firebase-installations-interop:17.1.1 +com.google.firebase:firebase-installations:17.2.0 +com.google.firebase:firebase-measurement-connector:19.0.0 +com.google.firebase:firebase-messaging-ktx:23.3.0 +com.google.firebase:firebase-messaging:23.3.0 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:31.1-android +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.google.protobuf:protobuf-javalite:3.24.4 +com.google.protobuf:protobuf-kotlin-lite:3.24.4 +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.retrofit2:retrofit:2.9.0 +io.coil-kt:coil-base:2.4.0 +io.coil-kt:coil-compose-base:2.4.0 +io.coil-kt:coil-compose:2.4.0 +io.coil-kt:coil-svg:2.4.0 +io.coil-kt:coil:2.4.0 +javax.inject:javax.inject:1 +org.checkerframework:checker-qual:3.12.0 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 +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.10 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains:annotations:23.0.0 From 743ec8c08121a005ce6f591d3a34bb9b78795024 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Wed, 29 Nov 2023 16:17:30 +0100 Subject: [PATCH 14/70] Update Gradle 8.5 https://docs.gradle.org/8.5/release-notes.html --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 63721 zcmb5Wb9gP!wgnp7wrv|bwr$&XvSZt}Z6`anZSUAlc9NHKf9JdJ;NJVr`=eI(_pMp0 zy1VAAG3FfAOI`{X1O)&90s;U4K;XLp008~hCjbEC_fbYfS%6kTR+JtXK>nW$ZR+`W ze|#J8f4A@M|F5BpfUJb5h>|j$jOe}0oE!`Zf6fM>CR?!y@zU(cL8NsKk`a z6tx5mAkdjD;J=LcJ;;Aw8p!v#ouk>mUDZF@ zK>yvw%+bKu+T{Nk@LZ;zkYy0HBKw06_IWcMHo*0HKpTsEFZhn5qCHH9j z)|XpN&{`!0a>Vl+PmdQc)Yg4A(AG-z!+@Q#eHr&g<9D?7E)_aEB?s_rx>UE9TUq|? z;(ggJt>9l?C|zoO@5)tu?EV0x_7T17q4fF-q3{yZ^ipUbKcRZ4Qftd!xO(#UGhb2y>?*@{xq%`(-`2T^vc=#< zx!+@4pRdk&*1ht2OWk^Z5IAQ0YTAXLkL{(D*$gENaD)7A%^XXrCchN&z2x+*>o2FwPFjWpeaL=!tzv#JOW#( z$B)Nel<+$bkH1KZv3&-}=SiG~w2sbDbAWarg%5>YbC|}*d9hBjBkR(@tyM0T)FO$# zPtRXukGPnOd)~z=?avu+4Co@wF}1T)-uh5jI<1$HLtyDrVak{gw`mcH@Q-@wg{v^c zRzu}hMKFHV<8w}o*yg6p@Sq%=gkd~;`_VGTS?L@yVu`xuGy+dH6YOwcP6ZE`_0rK% zAx5!FjDuss`FQ3eF|mhrWkjux(Pny^k$u_)dyCSEbAsecHsq#8B3n3kDU(zW5yE|( zgc>sFQywFj5}U*qtF9Y(bi*;>B7WJykcAXF86@)z|0-Vm@jt!EPoLA6>r)?@DIobIZ5Sx zsc@OC{b|3%vaMbyeM|O^UxEYlEMHK4r)V-{r)_yz`w1*xV0|lh-LQOP`OP`Pk1aW( z8DSlGN>Ts|n*xj+%If~+E_BxK)~5T#w6Q1WEKt{!Xtbd`J;`2a>8boRo;7u2M&iOop4qcy<)z023=oghSFV zST;?S;ye+dRQe>ygiJ6HCv4;~3DHtJ({fWeE~$H@mKn@Oh6Z(_sO>01JwH5oA4nvK zr5Sr^g+LC zLt(i&ecdmqsIJGNOSUyUpglvhhrY8lGkzO=0USEKNL%8zHshS>Qziu|`eyWP^5xL4 zRP122_dCJl>hZc~?58w~>`P_s18VoU|7(|Eit0-lZRgLTZKNq5{k zE?V=`7=R&ro(X%LTS*f+#H-mGo_j3dm@F_krAYegDLk6UV{`UKE;{YSsn$ z(yz{v1@p|p!0>g04!eRSrSVb>MQYPr8_MA|MpoGzqyd*$@4j|)cD_%^Hrd>SorF>@ zBX+V<@vEB5PRLGR(uP9&U&5=(HVc?6B58NJT_igiAH*q~Wb`dDZpJSKfy5#Aag4IX zj~uv74EQ_Q_1qaXWI!7Vf@ZrdUhZFE;L&P_Xr8l@GMkhc#=plV0+g(ki>+7fO%?Jb zl+bTy7q{w^pTb{>(Xf2q1BVdq?#f=!geqssXp z4pMu*q;iiHmA*IjOj4`4S&|8@gSw*^{|PT}Aw~}ZXU`6=vZB=GGeMm}V6W46|pU&58~P+?LUs%n@J}CSrICkeng6YJ^M? zS(W?K4nOtoBe4tvBXs@@`i?4G$S2W&;$z8VBSM;Mn9 zxcaEiQ9=vS|bIJ>*tf9AH~m&U%2+Dim<)E=}KORp+cZ^!@wI`h1NVBXu{@%hB2Cq(dXx_aQ9x3mr*fwL5!ZryQqi|KFJuzvP zK1)nrKZ7U+B{1ZmJub?4)Ln^J6k!i0t~VO#=q1{?T)%OV?MN}k5M{}vjyZu#M0_*u z8jwZKJ#Df~1jcLXZL7bnCEhB6IzQZ-GcoQJ!16I*39iazoVGugcKA{lhiHg4Ta2fD zk1Utyc5%QzZ$s3;p0N+N8VX{sd!~l*Ta3|t>lhI&G`sr6L~G5Lul`>m z{!^INm?J|&7X=;{XveF!(b*=?9NAp4y&r&N3(GKcW4rS(Ejk|Lzs1PrxPI_owB-`H zg3(Rruh^&)`TKA6+_!n>RdI6pw>Vt1_j&+bKIaMTYLiqhZ#y_=J8`TK{Jd<7l9&sY z^^`hmi7^14s16B6)1O;vJWOF$=$B5ONW;;2&|pUvJlmeUS&F;DbSHCrEb0QBDR|my zIs+pE0Y^`qJTyH-_mP=)Y+u^LHcuZhsM3+P||?+W#V!_6E-8boP#R-*na4!o-Q1 zVthtYhK{mDhF(&7Okzo9dTi03X(AE{8cH$JIg%MEQca`S zy@8{Fjft~~BdzWC(di#X{ny;!yYGK9b@=b|zcKZ{vv4D8i+`ilOPl;PJl{!&5-0!w z^fOl#|}vVg%=n)@_e1BrP)`A zKPgs`O0EO}Y2KWLuo`iGaKu1k#YR6BMySxQf2V++Wo{6EHmK>A~Q5o73yM z-RbxC7Qdh0Cz!nG+7BRZE>~FLI-?&W_rJUl-8FDIaXoNBL)@1hwKa^wOr1($*5h~T zF;%f^%<$p8Y_yu(JEg=c_O!aZ#)Gjh$n(hfJAp$C2he555W5zdrBqjFmo|VY+el;o z=*D_w|GXG|p0**hQ7~9-n|y5k%B}TAF0iarDM!q-jYbR^us(>&y;n^2l0C%@2B}KM zyeRT9)oMt97Agvc4sEKUEy%MpXr2vz*lb zh*L}}iG>-pqDRw7ud{=FvTD?}xjD)w{`KzjNom-$jS^;iw0+7nXSnt1R@G|VqoRhE%12nm+PH?9`(4rM0kfrZzIK9JU=^$YNyLvAIoxl#Q)xxDz!^0@zZ zSCs$nfcxK_vRYM34O<1}QHZ|hp4`ioX3x8(UV(FU$J@o%tw3t4k1QPmlEpZa2IujG&(roX_q*%e`Hq|);0;@k z0z=fZiFckp#JzW0p+2A+D$PC~IsakhJJkG(c;CqAgFfU0Z`u$PzG~-9I1oPHrCw&)@s^Dc~^)#HPW0Ra}J^=|h7Fs*<8|b13ZzG6MP*Q1dkoZ6&A^!}|hbjM{2HpqlSXv_UUg1U4gn z3Q)2VjU^ti1myodv+tjhSZp%D978m~p& z43uZUrraHs80Mq&vcetqfQpQP?m!CFj)44t8Z}k`E798wxg&~aCm+DBoI+nKq}&j^ zlPY3W$)K;KtEajks1`G?-@me7C>{PiiBu+41#yU_c(dITaqE?IQ(DBu+c^Ux!>pCj zLC|HJGU*v+!it1(;3e`6igkH(VA)-S+k(*yqxMgUah3$@C zz`7hEM47xr>j8^g`%*f=6S5n>z%Bt_Fg{Tvmr+MIsCx=0gsu_sF`q2hlkEmisz#Fy zj_0;zUWr;Gz}$BS%Y`meb(=$d%@Crs(OoJ|}m#<7=-A~PQbyN$x%2iXP2@e*nO0b7AwfH8cCUa*Wfu@b)D_>I*%uE4O3 z(lfnB`-Xf*LfC)E}e?%X2kK7DItK6Tf<+M^mX0Ijf_!IP>7c8IZX%8_#0060P{QMuV^B9i<^E`_Qf0pv9(P%_s8D`qvDE9LK9u-jB}J2S`(mCO&XHTS04Z5Ez*vl^T%!^$~EH8M-UdwhegL>3IQ*)(MtuH2Xt1p!fS4o~*rR?WLxlA!sjc2(O znjJn~wQ!Fp9s2e^IWP1C<4%sFF}T4omr}7+4asciyo3DntTgWIzhQpQirM$9{EbQd z3jz9vS@{aOqTQHI|l#aUV@2Q^Wko4T0T04Me4!2nsdrA8QY1%fnAYb~d2GDz@lAtfcHq(P7 zaMBAGo}+NcE-K*@9y;Vt3*(aCaMKXBB*BJcD_Qnxpt75r?GeAQ}*|>pYJE=uZb73 zC>sv)18)q#EGrTG6io*}JLuB_jP3AU1Uiu$D7r|2_zlIGb9 zjhst#ni)Y`$)!fc#reM*$~iaYoz~_Cy7J3ZTiPm)E?%`fbk`3Tu-F#`{i!l5pNEn5 zO-Tw-=TojYhzT{J=?SZj=Z8#|eoF>434b-DXiUsignxXNaR3 zm_}4iWU$gt2Mw5NvZ5(VpF`?X*f2UZDs1TEa1oZCif?Jdgr{>O~7}-$|BZ7I(IKW`{f;@|IZFX*R8&iT= zoWstN8&R;}@2Ka%d3vrLtR|O??ben;k8QbS-WB0VgiCz;<$pBmIZdN!aalyCSEm)crpS9dcD^Y@XT1a3+zpi-`D}e#HV<} z$Y(G&o~PvL-xSVD5D?JqF3?B9rxGWeb=oEGJ3vRp5xfBPlngh1O$yI95EL+T8{GC@ z98i1H9KhZGFl|;`)_=QpM6H?eDPpw~^(aFQWwyXZ8_EEE4#@QeT_URray*mEOGsGc z6|sdXtq!hVZo=d#+9^@lm&L5|q&-GDCyUx#YQiccq;spOBe3V+VKdjJA=IL=Zn%P} zNk=_8u}VhzFf{UYZV0`lUwcD&)9AFx0@Fc6LD9A6Rd1=ga>Mi0)_QxM2ddCVRmZ0d z+J=uXc(?5JLX3=)e)Jm$HS2yF`44IKhwRnm2*669_J=2LlwuF5$1tAo@ROSU@-y+;Foy2IEl2^V1N;fk~YR z?&EP8#t&m0B=?aJeuz~lHjAzRBX>&x=A;gIvb>MD{XEV zV%l-+9N-)i;YH%nKP?>f`=?#`>B(`*t`aiPLoQM(a6(qs4p5KFjDBN?8JGrf3z8>= zi7sD)c)Nm~x{e<^jy4nTx${P~cwz_*a>%0_;ULou3kHCAD7EYkw@l$8TN#LO9jC( z1BeFW`k+bu5e8Ns^a8dPcjEVHM;r6UX+cN=Uy7HU)j-myRU0wHd$A1fNI~`4;I~`zC)3ul#8#^rXVSO*m}Ag>c%_;nj=Nv$rCZ z*~L@C@OZg%Q^m)lc-kcX&a*a5`y&DaRxh6O*dfhLfF+fU5wKs(1v*!TkZidw*)YBP za@r`3+^IHRFeO%!ai%rxy;R;;V^Fr=OJlpBX;(b*3+SIw}7= zIq$*Thr(Zft-RlY)D3e8V;BmD&HOfX+E$H#Y@B3?UL5L~_fA-@*IB-!gItK7PIgG9 zgWuGZK_nuZjHVT_Fv(XxtU%)58;W39vzTI2n&)&4Dmq7&JX6G>XFaAR{7_3QB6zsT z?$L8c*WdN~nZGiscY%5KljQARN;`w$gho=p006z;n(qIQ*Zu<``TMO3n0{ARL@gYh zoRwS*|Niw~cR!?hE{m*y@F`1)vx-JRfqET=dJ5_(076st(=lFfjtKHoYg`k3oNmo_ zNbQEw8&sO5jAYmkD|Zaz_yUb0rC})U!rCHOl}JhbYIDLzLvrZVw0~JO`d*6f;X&?V=#T@ND*cv^I;`sFeq4 z##H5;gpZTb^0Hz@3C*~u0AqqNZ-r%rN3KD~%Gw`0XsIq$(^MEb<~H(2*5G^<2(*aI z%7}WB+TRlMIrEK#s0 z93xn*Ohb=kWFc)BNHG4I(~RPn-R8#0lqyBBz5OM6o5|>x9LK@%HaM}}Y5goCQRt2C z{j*2TtT4ne!Z}vh89mjwiSXG=%DURar~=kGNNaO_+Nkb+tRi~Rkf!7a$*QlavziD( z83s4GmQ^Wf*0Bd04f#0HX@ua_d8 z23~z*53ePD6@xwZ(vdl0DLc=>cPIOPOdca&MyR^jhhKrdQO?_jJh`xV3GKz&2lvP8 zEOwW6L*ufvK;TN{=S&R@pzV^U=QNk^Ec}5H z+2~JvEVA{`uMAr)?Kf|aW>33`)UL@bnfIUQc~L;TsTQ6>r-<^rB8uoNOJ>HWgqMI8 zSW}pZmp_;z_2O5_RD|fGyTxaxk53Hg_3Khc<8AUzV|ZeK{fp|Ne933=1&_^Dbv5^u zB9n=*)k*tjHDRJ@$bp9mrh}qFn*s}npMl5BMDC%Hs0M0g-hW~P*3CNG06G!MOPEQ_ zi}Qs-6M8aMt;sL$vlmVBR^+Ry<64jrm1EI1%#j?c?4b*7>)a{aDw#TfTYKq+SjEFA z(aJ&z_0?0JB83D-i3Vh+o|XV4UP+YJ$9Boid2^M2en@APw&wx7vU~t$r2V`F|7Qfo z>WKgI@eNBZ-+Og<{u2ZiG%>YvH2L3fNpV9J;WLJoBZda)01Rn;o@){01{7E#ke(7U zHK>S#qZ(N=aoae*4X!0A{)nu0R_sKpi1{)u>GVjC+b5Jyl6#AoQ-1_3UDovNSo`T> z?c-@7XX*2GMy?k?{g)7?Sv;SJkmxYPJPs!&QqB12ejq`Lee^-cDveVWL^CTUldb(G zjDGe(O4P=S{4fF=#~oAu>LG>wrU^z_?3yt24FOx>}{^lCGh8?vtvY$^hbZ)9I0E3r3NOlb9I?F-Yc=r$*~l`4N^xzlV~N zl~#oc>U)Yjl0BxV>O*Kr@lKT{Z09OXt2GlvE38nfs+DD7exl|&vT;)>VFXJVZp9Np zDK}aO;R3~ag$X*|hRVY3OPax|PG`@_ESc8E!mHRByJbZQRS38V2F__7MW~sgh!a>98Q2%lUNFO=^xU52|?D=IK#QjwBky-C>zOWlsiiM&1n z;!&1((Xn1$9K}xabq~222gYvx3hnZPg}VMF_GV~5ocE=-v>V=T&RsLBo&`)DOyIj* zLV{h)JU_y*7SdRtDajP_Y+rBkNN*1_TXiKwHH2&p51d(#zv~s#HwbNy?<+(=9WBvo zw2hkk2Dj%kTFhY+$T+W-b7@qD!bkfN#Z2ng@Pd=i3-i?xYfs5Z*1hO?kd7Sp^9`;Y zM2jeGg<-nJD1er@Pc_cSY7wo5dzQX44=%6rn}P_SRbpzsA{6B+!$3B0#;}qwO37G^ zL(V_5JK`XT?OHVk|{_$vQ|oNEpab*BO4F zUTNQ7RUhnRsU`TK#~`)$icsvKh~(pl=3p6m98@k3P#~upd=k*u20SNcb{l^1rUa)>qO997)pYRWMncC8A&&MHlbW?7i^7M`+B$hH~Y|J zd>FYOGQ;j>Zc2e7R{KK7)0>>nn_jYJy&o@sK!4G>-rLKM8Hv)f;hi1D2fAc$+six2 zyVZ@wZ6x|fJ!4KrpCJY=!Mq0;)X)OoS~{Lkh6u8J`eK%u0WtKh6B>GW_)PVc zl}-k`p09qwGtZ@VbYJC!>29V?Dr>>vk?)o(x?!z*9DJ||9qG-&G~#kXxbw{KKYy}J zQKa-dPt~M~E}V?PhW0R26xdA%1T*%ra6SguGu50YHngOTIv)@N|YttEXo#OZfgtP7;H?EeZZxo<}3YlYxtBq znJ!WFR^tmGf0Py}N?kZ(#=VtpC@%xJkDmfcCoBTxq zr_|5gP?u1@vJZbxPZ|G0AW4=tpb84gM2DpJU||(b8kMOV1S3|(yuwZJ&rIiFW(U;5 zUtAW`O6F6Zy+eZ1EDuP~AAHlSY-+A_eI5Gx)%*uro5tljy}kCZU*_d7)oJ>oQSZ3* zneTn`{gnNC&uJd)0aMBzAg021?YJ~b(fmkwZAd696a=0NzBAqBN54KuNDwa*no(^O z6p05bioXUR^uXjpTol*ppHp%1v9e)vkoUAUJyBx3lw0UO39b0?^{}yb!$yca(@DUn zCquRF?t=Zb9`Ed3AI6|L{eX~ijVH`VzSMheKoP7LSSf4g>md>`yi!TkoG5P>Ofp+n z(v~rW+(5L96L{vBb^g51B=(o)?%%xhvT*A5btOpw(TKh^g^4c zw>0%X!_0`{iN%RbVk+A^f{w-4-SSf*fu@FhruNL##F~sF24O~u zyYF<3el2b$$wZ_|uW#@Ak+VAGk#e|kS8nL1g>2B-SNMjMp^8;-FfeofY2fphFHO!{ z*!o4oTb{4e;S<|JEs<1_hPsmAlVNk?_5-Fp5KKU&d#FiNW~Y+pVFk@Cua1I{T+1|+ zHx6rFMor)7L)krbilqsWwy@T+g3DiH5MyVf8Wy}XbEaoFIDr~y;@r&I>FMW{ z?Q+(IgyebZ)-i4jNoXQhq4Muy9Fv+OxU;9_Jmn+<`mEC#%2Q_2bpcgzcinygNI!&^ z=V$)o2&Yz04~+&pPWWn`rrWxJ&}8khR)6B(--!9Q zubo}h+1T)>a@c)H^i``@<^j?|r4*{;tQf78(xn0g39IoZw0(CwY1f<%F>kEaJ zp9u|IeMY5mRdAlw*+gSN^5$Q)ShM<~E=(c8QM+T-Qk)FyKz#Sw0EJ*edYcuOtO#~Cx^(M7w5 z3)rl#L)rF|(Vun2LkFr!rg8Q@=r>9p>(t3Gf_auiJ2Xx9HmxYTa|=MH_SUlYL`mz9 zTTS$`%;D-|Jt}AP1&k7PcnfFNTH0A-*FmxstjBDiZX?}%u%Yq94$fUT&z6od+(Uk> zuqsld#G(b$G8tus=M!N#oPd|PVFX)?M?tCD0tS%2IGTfh}3YA3f&UM)W$_GNV8 zQo+a(ml2Km4o6O%gKTCSDNq+#zCTIQ1*`TIJh~k6Gp;htHBFnne))rlFdGqwC6dx2+La1&Mnko*352k0y z+tQcwndQlX`nc6nb$A9?<-o|r*%aWXV#=6PQic0Ok_D;q>wbv&j7cKc!w4~KF#-{6 z(S%6Za)WpGIWf7jZ3svNG5OLs0>vCL9{V7cgO%zevIVMH{WgP*^D9ws&OqA{yr|m| zKD4*07dGXshJHd#e%x%J+qmS^lS|0Bp?{drv;{@{l9ArPO&?Q5=?OO9=}h$oVe#3b z3Yofj&Cb}WC$PxmRRS)H%&$1-)z7jELS}!u!zQ?A^Y{Tv4QVt*vd@uj-^t2fYRzQj zfxGR>-q|o$3sGn^#VzZ!QQx?h9`njeJry}@x?|k0-GTTA4y3t2E`3DZ!A~D?GiJup z)8%PK2^9OVRlP(24P^4_<|D=H^7}WlWu#LgsdHzB%cPy|f8dD3|A^mh4WXxhLTVu_ z@abE{6Saz|Y{rXYPd4$tfPYo}ef(oQWZ=4Bct-=_9`#Qgp4ma$n$`tOwq#&E18$B; z@Bp)bn3&rEi0>fWWZ@7k5WazfoX`SCO4jQWwVuo+$PmSZn^Hz?O(-tW@*DGxuf)V1 zO_xm&;NVCaHD4dqt(-MlszI3F-p?0!-e$fbiCeuaw66h^TTDLWuaV<@C-`=Xe5WL) zwooG7h>4&*)p3pKMS3O!4>-4jQUN}iAMQ)2*70?hP~)TzzR?-f@?Aqy$$1Iy8VGG$ zMM?8;j!pUX7QQD$gRc_#+=raAS577ga-w?jd`vCiN5lu)dEUkkUPl9!?{$IJNxQys z*E4e$eF&n&+AMRQR2gcaFEjAy*r)G!s(P6D&TfoApMFC_*Ftx0|D0@E-=B7tezU@d zZ{hGiN;YLIoSeRS;9o%dEua4b%4R3;$SugDjP$x;Z!M!@QibuSBb)HY!3zJ7M;^jw zlx6AD50FD&p3JyP*>o+t9YWW8(7P2t!VQQ21pHJOcG_SXQD;(5aX#M6x##5H_Re>6lPyDCjxr*R(+HE%c&QN+b^tbT zXBJk?p)zhJj#I?&Y2n&~XiytG9!1ox;bw5Rbj~)7c(MFBb4>IiRATdhg zmiEFlj@S_hwYYI(ki{}&<;_7(Z0Qkfq>am z&LtL=2qc7rWguk3BtE4zL41@#S;NN*-jWw|7Kx7H7~_%7fPt;TIX}Ubo>;Rmj94V> zNB1=;-9AR7s`Pxn}t_6^3ahlq53e&!Lh85uG zec0vJY_6e`tg7LgfrJ3k!DjR)Bi#L@DHIrZ`sK=<5O0Ip!fxGf*OgGSpP@Hbbe&$9 z;ZI}8lEoC2_7;%L2=w?tb%1oL0V+=Z`7b=P&lNGY;yVBazXRYu;+cQDKvm*7NCxu&i;zub zAJh#11%?w>E2rf2e~C4+rAb-&$^vsdACs7 z@|Ra!OfVM(ke{vyiqh7puf&Yp6cd6{DptUteYfIRWG3pI+5< zBVBI_xkBAc<(pcb$!Y%dTW(b;B;2pOI-(QCsLv@U-D1XJ z(Gk8Q3l7Ws46Aktuj>|s{$6zA&xCPuXL-kB`CgYMs}4IeyG*P51IDwW?8UNQd+$i~ zlxOPtSi5L|gJcF@DwmJA5Ju8HEJ>o{{upwIpb!f{2(vLNBw`7xMbvcw<^{Fj@E~1( z?w`iIMieunS#>nXlmUcSMU+D3rX28f?s7z;X=se6bo8;5vM|O^(D6{A9*ChnGH!RG zP##3>LDC3jZPE4PH32AxrqPk|yIIrq~`aL-=}`okhNu9aT%q z1b)7iJ)CN=V#Ly84N_r7U^SH2FGdE5FpTO2 z630TF$P>GNMu8`rOytb(lB2};`;P4YNwW1<5d3Q~AX#P0aX}R2b2)`rgkp#zTxcGj zAV^cvFbhP|JgWrq_e`~exr~sIR$6p5V?o4Wym3kQ3HA+;Pr$bQ0(PmADVO%MKL!^q z?zAM8j1l4jrq|5X+V!8S*2Wl@=7*pPgciTVK6kS1Ge zMsd_u6DFK$jTnvVtE;qa+8(1sGBu~n&F%dh(&c(Zs4Fc#A=gG^^%^AyH}1^?|8quj zl@Z47h$){PlELJgYZCIHHL= z{U8O>Tw4x3<1{?$8>k-P<}1y9DmAZP_;(3Y*{Sk^H^A=_iSJ@+s5ktgwTXz_2$~W9>VVZsfwCm@s0sQ zeB50_yu@uS+e7QoPvdCwDz{prjo(AFwR%C?z`EL{1`|coJHQTk^nX=tvs1<0arUOJ z!^`*x&&BvTYmemyZ)2p~{%eYX=JVR?DYr(rNgqRMA5E1PR1Iw=prk=L2ldy3r3Vg@27IZx43+ywyzr-X*p*d@tZV+!U#~$-q=8c zgdSuh#r?b4GhEGNai)ayHQpk>5(%j5c@C1K3(W1pb~HeHpaqijJZa-e6vq_8t-^M^ zBJxq|MqZc?pjXPIH}70a5vt!IUh;l}<>VX<-Qcv^u@5(@@M2CHSe_hD$VG-eiV^V( zj7*9T0?di?P$FaD6oo?)<)QT>Npf6Og!GO^GmPV(Km0!=+dE&bk#SNI+C9RGQ|{~O*VC+tXK3!n`5 zHfl6>lwf_aEVV3`0T!aHNZLsj$paS$=LL(?b!Czaa5bbSuZ6#$_@LK<(7yrrl+80| z{tOFd=|ta2Z`^ssozD9BINn45NxUeCQis?-BKmU*Kt=FY-NJ+)8S1ecuFtN-M?&42 zl2$G>u!iNhAk*HoJ^4v^9#ORYp5t^wDj6|lx~5w45#E5wVqI1JQ~9l?nPp1YINf++ zMAdSif~_ETv@Er(EFBI^@L4BULFW>)NI+ejHFP*T}UhWNN`I)RRS8za? z*@`1>9ZB}An%aT5K=_2iQmfE;GcBVHLF!$`I99o5GO`O%O_zLr9AG18>&^HkG(;=V z%}c!OBQ~?MX(9h~tajX{=x)+!cbM7$YzTlmsPOdp2L-?GoW`@{lY9U3f;OUo*BwRB z8A+nv(br0-SH#VxGy#ZrgnGD(=@;HME;yd46EgWJ`EL%oXc&lFpc@Y}^>G(W>h_v_ zlN!`idhX+OjL+~T?19sroAFVGfa5tX-D49w$1g2g_-T|EpHL6}K_aX4$K=LTvwtlF zL*z}j{f+Uoe7{-px3_5iKPA<_7W=>Izkk)!l9ez2w%vi(?Y;i8AxRNLSOGDzNoqoI zP!1uAl}r=_871(G?y`i&)-7{u=%nxk7CZ_Qh#!|ITec zwQn`33GTUM`;D2POWnkqngqJhJRlM>CTONzTG}>^Q0wUunQyn|TAiHzyX2_%ATx%P z%7gW)%4rA9^)M<_%k@`Y?RbC<29sWU&5;@|9thf2#zf8z12$hRcZ!CSb>kUp=4N#y zl3hE#y6>kkA8VY2`W`g5Ip?2qC_BY$>R`iGQLhz2-S>x(RuWv)SPaGdl^)gGw7tjR zH@;jwk!jIaCgSg_*9iF|a);sRUTq30(8I(obh^|}S~}P4U^BIGYqcz;MPpC~Y@k_m zaw4WG1_vz2GdCAX!$_a%GHK**@IrHSkGoN>)e}>yzUTm52on`hYot7cB=oA-h1u|R ztH$11t?54Qg2L+i33FPFKKRm1aOjKST{l1*(nps`>sv%VqeVMWjl5+Gh+9);hIP8? zA@$?}Sc z3qIRpba+y5yf{R6G(u8Z^vkg0Fu&D-7?1s=QZU`Ub{-!Y`I?AGf1VNuc^L3v>)>i# z{DV9W$)>34wnzAXUiV^ZpYKw>UElrN_5Xj6{r_3| z$X5PK`e5$7>~9Dj7gK5ash(dvs`vwfk}&RD`>04;j62zoXESkFBklYaKm5seyiX(P zqQ-;XxlV*yg?Dhlx%xt!b0N3GHp@(p$A;8|%# zZ5m2KL|{on4nr>2_s9Yh=r5ScQ0;aMF)G$-9-Ca6%wA`Pa)i?NGFA|#Yi?{X-4ZO_ z^}%7%vkzvUHa$-^Y#aA+aiR5sa%S|Ebyn`EV<3Pc?ax_f>@sBZF1S;7y$CXd5t5=WGsTKBk8$OfH4v|0?0I=Yp}7c=WBSCg!{0n)XmiU;lfx)**zZaYqmDJelxk$)nZyx5`x$6R|fz(;u zEje5Dtm|a%zK!!tk3{i9$I2b{vXNFy%Bf{50X!x{98+BsDr_u9i>G5%*sqEX|06J0 z^IY{UcEbj6LDwuMh7cH`H@9sVt1l1#8kEQ(LyT@&+K}(ReE`ux8gb0r6L_#bDUo^P z3Ka2lRo52Hdtl_%+pwVs14=q`{d^L58PsU@AMf(hENumaxM{7iAT5sYmWh@hQCO^ zK&}ijo=`VqZ#a3vE?`7QW0ZREL17ZvDfdqKGD?0D4fg{7v%|Yj&_jcKJAB)>=*RS* zto8p6@k%;&^ZF>hvXm&$PCuEp{uqw3VPG$9VMdW5$w-fy2CNNT>E;>ejBgy-m_6`& z97L1p{%srn@O_JQgFpa_#f(_)eb#YS>o>q3(*uB;uZb605(iqM$=NK{nHY=+X2*G) zO3-_Xh%aG}fHWe*==58zBwp%&`mge<8uq8;xIxOd=P%9EK!34^E9sk|(Zq1QSz-JVeP12Fp)-`F|KY$LPwUE?rku zY@OJ)Z9A!ojfzfeyJ9;zv2EM7ZQB)AR5xGa-tMn^bl)FmoIiVyJ@!~@%{}qXXD&Ns zPnfe5U+&ohKefILu_1mPfLGuapX@btta5C#gPB2cjk5m4T}Nfi+Vfka!Yd(L?-c~5 z#ZK4VeQEXNPc4r$K00Fg>g#_W!YZ)cJ?JTS<&68_$#cZT-ME`}tcwqg3#``3M3UPvn+pi}(VNNx6y zFIMVb6OwYU(2`at$gHba*qrMVUl8xk5z-z~fb@Q3Y_+aXuEKH}L+>eW__!IAd@V}L zkw#s%H0v2k5-=vh$^vPCuAi22Luu3uKTf6fPo?*nvj$9(u)4$6tvF-%IM+3pt*cgs z_?wW}J7VAA{_~!?))?s6{M=KPpVhg4fNuU*|3THp@_(q!b*hdl{fjRVFWtu^1dV(f z6iOux9hi&+UK=|%M*~|aqFK{Urfl!TA}UWY#`w(0P!KMe1Si{8|o))Gy6d7;!JQYhgMYmXl?3FfOM2nQGN@~Ap6(G z3+d_5y@=nkpKAhRqf{qQ~k7Z$v&l&@m7Ppt#FSNzKPZM z8LhihcE6i=<(#87E|Wr~HKvVWhkll4iSK$^mUHaxgy8*K$_Zj;zJ`L$naPj+^3zTi z-3NTaaKnD5FPY-~?Tq6QHnmDDRxu0mh0D|zD~Y=vv_qig5r-cIbCpxlju&8Sya)@{ zsmv6XUSi)@(?PvItkiZEeN*)AE~I_?#+Ja-r8$(XiXei2d@Hi7Rx8+rZZb?ZLa{;@*EHeRQ-YDadz~M*YCM4&F-r;E#M+@CSJMJ0oU|PQ^ z=E!HBJDMQ2TN*Y(Ag(ynAL8%^v;=~q?s4plA_hig&5Z0x_^Oab!T)@6kRN$)qEJ6E zNuQjg|G7iwU(N8pI@_6==0CL;lRh1dQF#wePhmu@hADFd3B5KIH#dx(2A zp~K&;Xw}F_N6CU~0)QpQk7s$a+LcTOj1%=WXI(U=Dv!6 z{#<#-)2+gCyyv=Jw?Ab#PVkxPDeH|sAxyG`|Ys}A$PW4TdBv%zDz z^?lwrxWR<%Vzc8Sgt|?FL6ej_*e&rhqJZ3Y>k=X(^dytycR;XDU16}Pc9Vn0>_@H+ zQ;a`GSMEG64=JRAOg%~L)x*w{2re6DVprNp+FcNra4VdNjiaF0M^*>CdPkt(m150rCue?FVdL0nFL$V%5y6N z%eLr5%YN7D06k5ji5*p4v$UMM)G??Q%RB27IvH7vYr_^3>1D-M66#MN8tWGw>WED} z5AhlsanO=STFYFs)Il_0i)l)f<8qn|$DW7ZXhf5xI;m+7M5-%P63XFQrG9>DMqHc} zsgNU9nR`b}E^mL5=@7<1_R~j@q_2U^3h|+`7YH-?C=vme1C3m`Fe0HC>pjt6f_XMh zy~-i-8R46QNYneL4t@)<0VU7({aUO?aH`z4V2+kxgH5pYD5)wCh75JqQY)jIPN=U6 z+qi8cGiOtXG2tXm;_CfpH9ESCz#i5B(42}rBJJF$jh<1sbpj^8&L;gzGHb8M{of+} zzF^8VgML2O9nxBW7AvdEt90vp+#kZxWf@A)o9f9}vKJy9NDBjBW zSt=Hcs=YWCwnfY1UYx*+msp{g!w0HC<_SM!VL1(I2PE?CS}r(eh?{I)mQixmo5^p# zV?2R!R@3GV6hwTCrfHiK#3Orj>I!GS2kYhk1S;aFBD_}u2v;0HYFq}Iz1Z(I4oca4 zxquja8$+8JW_EagDHf$a1OTk5S97umGSDaj)gH=fLs9>_=XvVj^Xj9a#gLdk=&3tl zfmK9MNnIX9v{?%xdw7568 zNrZ|roYs(vC4pHB5RJ8>)^*OuyNC>x7ad)tB_}3SgQ96+-JT^Qi<`xi=)_=$Skwv~ zdqeT9Pa`LYvCAn&rMa2aCDV(TMI#PA5g#RtV|CWpgDYRA^|55LLN^uNh*gOU>Z=a06qJ;$C9z8;n-Pq=qZnc1zUwJ@t)L;&NN+E5m zRkQ(SeM8=l-aoAKGKD>!@?mWTW&~)uF2PYUJ;tB^my`r9n|Ly~0c%diYzqs9W#FTjy?h&X3TnH zXqA{QI82sdjPO->f=^K^f>N`+B`q9&rN0bOXO79S&a9XX8zund(kW7O76f4dcWhIu zER`XSMSFbSL>b;Rp#`CuGJ&p$s~G|76){d?xSA5wVg##_O0DrmyEYppyBr%fyWbbv zp`K84JwRNP$d-pJ!Qk|(RMr?*!wi1if-9G#0p>>1QXKXWFy)eB3ai)l3601q8!9JC zvU#ZWWDNKq9g6fYs?JQ)Q4C_cgTy3FhgKb8s&m)DdmL5zhNK#8wWg!J*7G7Qhe9VU zha?^AQTDpYcuN!B+#1dE*X{<#!M%zfUQbj=zLE{dW0XeQ7-oIsGY6RbkP2re@Q{}r_$iiH0xU%iN*ST`A)-EH6eaZB$GA#v)cLi z*MpA(3bYk$oBDKAzu^kJoSUsDd|856DApz={3u8sbQV@JnRkp2nC|)m;#T=DvIL-O zI4vh;g7824l}*`_p@MT4+d`JZ2%6NQh=N9bmgJ#q!hK@_<`HQq3}Z8Ij>3%~<*= zcv=!oT#5xmeGI92lqm9sGVE%#X$ls;St|F#u!?5Y7syhx6q#MVRa&lBmmn%$C0QzU z);*ldgwwCmzM3uglr}!Z2G+?& zf%Dpo&mD%2ZcNFiN-Z0f;c_Q;A%f@>26f?{d1kxIJD}LxsQkB47SAdwinfMILZdN3 zfj^HmTzS3Ku5BxY>ANutS8WPQ-G>v4^_Qndy==P3pDm+Xc?>rUHl-4+^%Sp5atOja z2oP}ftw-rqnb}+khR3CrRg^ibi6?QYk1*i^;kQGirQ=uB9Sd1NTfT-Rbv;hqnY4neE5H1YUrjS2m+2&@uXiAo- zrKUX|Ohg7(6F(AoP~tj;NZlV#xsfo-5reuQHB$&EIAhyZk;bL;k9ouDmJNBAun;H& zn;Of1z_Qj`x&M;5X;{s~iGzBQTY^kv-k{ksbE*Dl%Qf%N@hQCfY~iUw!=F-*$cpf2 z3wix|aLBV0b;W@z^%7S{>9Z^T^fLOI68_;l@+Qzaxo`nAI8emTV@rRhEKZ z?*z_{oGdI~R*#<2{bkz$G~^Qef}$*4OYTgtL$e9q!FY7EqxJ2`zk6SQc}M(k(_MaV zSLJnTXw&@djco1~a(vhBl^&w=$fa9{Sru>7g8SHahv$&Bl(D@(Zwxo_3r=;VH|uc5 zi1Ny)J!<(KN-EcQ(xlw%PNwK8U>4$9nVOhj(y0l9X^vP1TA>r_7WtSExIOsz`nDOP zs}d>Vxb2Vo2e5x8p(n~Y5ggAyvib>d)6?)|E@{FIz?G3PVGLf7-;BxaP;c?7ddH$z zA+{~k^V=bZuXafOv!RPsE1GrR3J2TH9uB=Z67gok+u`V#}BR86hB1xl}H4v`F+mRfr zYhortD%@IGfh!JB(NUNSDh+qDz?4ztEgCz&bIG-Wg7w-ua4ChgQR_c+z8dT3<1?uX z*G(DKy_LTl*Ea!%v!RhpCXW1WJO6F`bgS-SB;Xw9#! z<*K}=#wVu9$`Yo|e!z-CPYH!nj7s9dEPr-E`DXUBu0n!xX~&|%#G=BeM?X@shQQMf zMvr2!y7p_gD5-!Lnm|a@z8Of^EKboZsTMk%5VsJEm>VsJ4W7Kv{<|#4f-qDE$D-W>gWT%z-!qXnDHhOvLk=?^a1*|0j z{pW{M0{#1VcR5;F!!fIlLVNh_Gj zbnW(_j?0c2q$EHIi@fSMR{OUKBcLr{Y&$hrM8XhPByyZaXy|dd&{hYQRJ9@Fn%h3p7*VQolBIV@Eq`=y%5BU~3RPa^$a?ixp^cCg z+}Q*X+CW9~TL29@OOng(#OAOd!)e$d%sr}^KBJ-?-X&|4HTmtemxmp?cT3uA?md4% zT8yZ0U;6Rg6JHy3fJae{6TMGS?ZUX6+gGTT{Q{)SI85$5FD{g-eR%O0KMpWPY`4@O zx!hen1*8^E(*}{m^V_?}(b5k3hYo=T+$&M32+B`}81~KKZhY;2H{7O-M@vbCzuX0n zW-&HXeyr1%I3$@ns-V1~Lb@wIpkmx|8I~ob1Of7i6BTNysEwI}=!nU%q7(V_^+d*G z7G;07m(CRTJup!`cdYi93r^+LY+`M*>aMuHJm(A8_O8C#A*$!Xvddgpjx5)?_EB*q zgE8o5O>e~9IiSC@WtZpF{4Bj2J5eZ>uUzY%TgWF7wdDE!fSQIAWCP)V{;HsU3ap?4 znRsiiDbtN7i9hapO;(|Ew>Ip2TZSvK9Z^N21%J?OiA_&eP1{(Pu_=%JjKy|HOardq ze?zK^K zA%sjF64*Wufad%H<) z^|t>e*h+Z1#l=5wHexzt9HNDNXgM=-OPWKd^5p!~%SIl>Fo&7BvNpbf8{NXmH)o{r zO=aBJ;meX1^{O%q;kqdw*5k!Y7%t_30 zy{nGRVc&5qt?dBwLs+^Sfp;f`YVMSB#C>z^a9@fpZ!xb|b-JEz1LBX7ci)V@W+kvQ89KWA0T~Lj$aCcfW#nD5bt&Y_< z-q{4ZXDqVg?|0o)j1%l0^_it0WF*LCn-+)c!2y5yS7aZIN$>0LqNnkujV*YVes(v$ zY@_-!Q;!ZyJ}Bg|G-~w@or&u0RO?vlt5*9~yeoPV_UWrO2J54b4#{D(D>jF(R88u2 zo#B^@iF_%S>{iXSol8jpmsZuJ?+;epg>k=$d`?GSegAVp3n$`GVDvK${N*#L_1`44 z{w0fL{2%)0|E+qgZtjX}itZz^KJt4Y;*8uSK}Ft38+3>j|K(PxIXXR-t4VopXo#9# zt|F{LWr-?34y`$nLBVV_*UEgA6AUI65dYIbqpNq9cl&uLJ0~L}<=ESlOm?Y-S@L*d z<7vt}`)TW#f%Rp$Q}6@3=j$7Tze@_uZO@aMn<|si{?S}~maII`VTjs&?}jQ4_cut9$)PEqMukwoXobzaKx^MV z2fQwl+;LSZ$qy%Tys0oo^K=jOw$!YwCv^ei4NBVauL)tN%=wz9M{uf{IB(BxK|lT*pFkmNK_1tV`nb%jH=a0~VNq2RCKY(rG7jz!-D^k)Ec)yS%17pE#o6&eY+ z^qN(hQT$}5F(=4lgNQhlxj?nB4N6ntUY6(?+R#B?W3hY_a*)hnr4PA|vJ<6p`K3Z5Hy z{{8(|ux~NLUW=!?9Qe&WXMTAkQnLXg(g=I@(VG3{HE13OaUT|DljyWXPs2FE@?`iU z4GQlM&Q=T<4&v@Fe<+TuXiZQT3G~vZ&^POfmI1K2h6t4eD}Gk5XFGpbj1n_g*{qmD6Xy z`6Vv|lLZtLmrnv*{Q%xxtcWVj3K4M%$bdBk_a&ar{{GWyu#ljM;dII;*jP;QH z#+^o-A4np{@|Mz+LphTD0`FTyxYq#wY)*&Ls5o{0z9yg2K+K7ZN>j1>N&;r+Z`vI| zDzG1LJZ+sE?m?>x{5LJx^)g&pGEpY=fQ-4}{x=ru;}FL$inHemOg%|R*ZXPodU}Kh zFEd5#+8rGq$Y<_?k-}r5zgQ3jRV=ooHiF|@z_#D4pKVEmn5CGV(9VKCyG|sT9nc=U zEoT67R`C->KY8Wp-fEcjjFm^;Cg(ls|*ABVHq8clBE(;~K^b+S>6uj70g? z&{XQ5U&!Z$SO7zfP+y^8XBbiu*Cv-yJG|l-oe*!s5$@Lh_KpxYL2sx`B|V=dETN>5K+C+CU~a_3cI8{vbu$TNVdGf15*>D zz@f{zIlorkY>TRh7mKuAlN9A0>N>SV`X)+bEHms=mfYTMWt_AJtz_h+JMmrgH?mZt zm=lfdF`t^J*XLg7v+iS)XZROygK=CS@CvUaJo&w2W!Wb@aa?~Drtf`JV^cCMjngVZ zv&xaIBEo8EYWuML+vxCpjjY^s1-ahXJzAV6hTw%ZIy!FjI}aJ+{rE&u#>rs)vzuxz z+$5z=7W?zH2>Eb32dvgHYZtCAf!=OLY-pb4>Ae79rd68E2LkVPj-|jFeyqtBCCwiW zkB@kO_(3wFq)7qwV}bA=zD!*@UhT`geq}ITo%@O(Z5Y80nEX~;0-8kO{oB6|(4fQh z);73T!>3@{ZobPwRv*W?7m0Ml9GmJBCJd&6E?hdj9lV= z4flNfsc(J*DyPv?RCOx!MSvk(M952PJ-G|JeVxWVjN~SNS6n-_Ge3Q;TGE;EQvZg86%wZ`MB zSMQua(i*R8a75!6$QRO^(o7sGoomb+Y{OMy;m~Oa`;P9Yqo>?bJAhqXxLr7_3g_n>f#UVtxG!^F#1+y@os6x(sg z^28bsQ@8rw%Gxk-stAEPRbv^}5sLe=VMbkc@Jjimqjvmd!3E7+QnL>|(^3!R} zD-l1l7*Amu@j+PWLGHXXaFG0Ct2Q=}5YNUxEQHCAU7gA$sSC<5OGylNnQUa>>l%sM zyu}z6i&({U@x^hln**o6r2s-(C-L50tQvz|zHTqW!ir?w&V23tuYEDJVV#5pE|OJu z7^R!A$iM$YCe?8n67l*J-okwfZ+ZTkGvZ)tVPfR;|3gyFjF)8V zyXXN=!*bpyRg9#~Bg1+UDYCt0 ztp4&?t1X0q>uz;ann$OrZs{5*r`(oNvw=$7O#rD|Wuv*wIi)4b zGtq4%BX+kkagv3F9Id6~-c+1&?zny%w5j&nk9SQfo0k4LhdSU_kWGW7axkfpgR`8* z!?UTG*Zi_baA1^0eda8S|@&F z{)Rad0kiLjB|=}XFJhD(S3ssKlveFFmkN{Vl^_nb!o5M!RC=m)V&v2%e?ZoRC@h3> zJ(?pvToFd`*Zc@HFPL#=otWKwtuuQ_dT-Hr{S%pQX<6dqVJ8;f(o)4~VM_kEQkMR+ zs1SCVi~k>M`u1u2xc}>#D!V&6nOOh-E$O&SzYrjJdZpaDv1!R-QGA141WjQe2s0J~ zQ;AXG)F+K#K8_5HVqRoRM%^EduqOnS(j2)|ctA6Q^=|s_WJYU;Z%5bHp08HPL`YF2 zR)Ad1z{zh`=sDs^&V}J z%$Z$!jd7BY5AkT?j`eqMs%!Gm@T8)4w3GYEX~IwgE~`d|@T{WYHkudy(47brgHXx& zBL1yFG6!!!VOSmDxBpefy2{L_u5yTwja&HA!mYA#wg#bc-m%~8aRR|~AvMnind@zs zy>wkShe5&*un^zvSOdlVu%kHsEo>@puMQ`b1}(|)l~E{5)f7gC=E$fP(FC2=F<^|A zxeIm?{EE!3sO!Gr7e{w)Dx(uU#3WrFZ>ibmKSQ1tY?*-Nh1TDHLe+k*;{Rp!Bmd_m zb#^kh`Y*8l|9Cz2e{;RL%_lg{#^Ar+NH|3z*Zye>!alpt{z;4dFAw^^H!6ING*EFc z_yqhr8d!;%nHX9AKhFQZBGrSzfzYCi%C!(Q5*~hX>)0N`vbhZ@N|i;_972WSx*>LH z87?en(;2_`{_JHF`Sv6Wlps;dCcj+8IJ8ca6`DsOQCMb3n# z3)_w%FuJ3>fjeOOtWyq)ag|PmgQbC-s}KRHG~enBcIwqIiGW8R8jFeBNY9|YswRY5 zjGUxdGgUD26wOpwM#8a!Nuqg68*dG@VM~SbOroL_On0N6QdT9?)NeB3@0FCC?Z|E0 z6TPZj(AsPtwCw>*{eDEE}Gby>0q{*lI+g2e&(YQrsY&uGM{O~}(oM@YWmb*F zA0^rr5~UD^qmNljq$F#ARXRZ1igP`MQx4aS6*MS;Ot(1L5jF2NJ;de!NujUYg$dr# z=TEL_zTj2@>ZZN(NYCeVX2==~=aT)R30gETO{G&GM4XN<+!&W&(WcDP%oL8PyIVUC zs5AvMgh6qr-2?^unB@mXK*Dbil^y-GTC+>&N5HkzXtozVf93m~xOUHn8`HpX=$_v2 z61H;Z1qK9o;>->tb8y%#4H)765W4E>TQ1o0PFj)uTOPEvv&}%(_mG0ISmyhnQV33Z$#&yd{ zc{>8V8XK$3u8}04CmAQ#I@XvtmB*s4t8va?-IY4@CN>;)mLb_4!&P3XSw4pA_NzDb zORn!blT-aHk1%Jpi>T~oGLuh{DB)JIGZ9KOsciWs2N7mM1JWM+lna4vkDL?Q)z_Ct z`!mi0jtr+4*L&N7jk&LodVO#6?_qRGVaucqVB8*us6i3BTa^^EI0x%EREQSXV@f!lak6Wf1cNZ8>*artIJ(ADO*=<-an`3zB4d*oO*8D1K!f z*A@P1bZCNtU=p!742MrAj%&5v%Xp_dSX@4YCw%F|%Dk=u|1BOmo)HsVz)nD5USa zR~??e61sO(;PR)iaxK{M%QM_rIua9C^4ppVS$qCT9j2%?*em?`4Z;4@>I(c%M&#cH z>4}*;ej<4cKkbCAjjDsyKS8rIm90O)Jjgyxj5^venBx&7B!xLmzxW3jhj7sR(^3Fz z84EY|p1NauwXUr;FfZjdaAfh%ivyp+^!jBjJuAaKa!yCq=?T_)R!>16?{~p)FQ3LDoMyG%hL#pR!f@P%*;#90rs_y z@9}@r1BmM-SJ#DeuqCQk=J?ixDSwL*wh|G#us;dd{H}3*-Y7Tv5m=bQJMcH+_S`zVtf;!0kt*(zwJ zs+kedTm!A}cMiM!qv(c$o5K%}Yd0|nOd0iLjus&;s0Acvoi-PFrWm?+q9f^FslxGi z6ywB`QpL$rJzWDg(4)C4+!2cLE}UPCTBLa*_=c#*$b2PWrRN46$y~yST3a2$7hEH= zNjux+wna^AzQ=KEa_5#9Ph=G1{S0#hh1L3hQ`@HrVnCx{!fw_a0N5xV(iPdKZ-HOM za)LdgK}1ww*C_>V7hbQnTzjURJL`S%`6nTHcgS+dB6b_;PY1FsrdE8(2K6FN>37!62j_cBlui{jO^$dPkGHV>pXvW0EiOA zqW`YaSUBWg_v^Y5tPJfWLcLpsA8T zG)!x>pKMpt!lv3&KV!-um= zKCir6`bEL_LCFx4Z5bAFXW$g3Cq`?Q%)3q0r852XI*Der*JNuKUZ`C{cCuu8R8nkt z%pnF>R$uY8L+D!V{s^9>IC+bmt<05h**>49R*#vpM*4i0qRB2uPbg8{{s#9yC;Z18 zD7|4m<9qneQ84uX|J&f-g8a|nFKFt34@Bt{CU`v(SYbbn95Q67*)_Esl_;v291s=9 z+#2F2apZU4Tq=x+?V}CjwD(P=U~d<=mfEFuyPB`Ey82V9G#Sk8H_Ob_RnP3s?)S_3 zr%}Pb?;lt_)Nf>@zX~D~TBr;-LS<1I##8z`;0ZCvI_QbXNh8Iv)$LS=*gHr;}dgb=w5$3k2la1keIm|=7<-JD>)U%=Avl0Vj@+&vxn zt-)`vJxJr88D&!}2^{GPXc^nmRf#}nb$4MMkBA21GzB`-Or`-3lq^O^svO7Vs~FdM zv`NvzyG+0T!P8l_&8gH|pzE{N(gv_tgDU7SWeiI-iHC#0Ai%Ixn4&nt{5y3(GQs)i z&uA;~_0shP$0Wh0VooIeyC|lak__#KVJfxa7*mYmZ22@(<^W}FdKjd*U1CqSjNKW% z*z$5$=t^+;Ui=MoDW~A7;)Mj%ibX1_p4gu>RC}Z_pl`U*{_z@+HN?AF{_W z?M_X@o%w8fgFIJ$fIzBeK=v#*`mtY$HC3tqw7q^GCT!P$I%=2N4FY7j9nG8aIm$c9 zeKTxVKN!UJ{#W)zxW|Q^K!3s;(*7Gbn;e@pQBCDS(I|Y0euK#dSQ_W^)sv5pa%<^o zyu}3d?Lx`)3-n5Sy9r#`I{+t6x%I%G(iewGbvor&I^{lhu-!#}*Q3^itvY(^UWXgvthH52zLy&T+B)Pw;5>4D6>74 zO_EBS)>l!zLTVkX@NDqyN2cXTwsUVao7$HcqV2%t$YzdAC&T)dwzExa3*kt9d(}al zA~M}=%2NVNUjZiO7c>04YH)sRelXJYpWSn^aC$|Ji|E13a^-v2MB!Nc*b+=KY7MCm zqIteKfNkONq}uM;PB?vvgQvfKLPMB8u5+Am=d#>g+o&Ysb>dX9EC8q?D$pJH!MTAqa=DS5$cb+;hEvjwVfF{4;M{5U&^_+r zvZdu_rildI!*|*A$TzJ&apQWV@p{!W`=?t(o0{?9y&vM)V)ycGSlI3`;ps(vf2PUq zX745#`cmT*ra7XECC0gKkpu2eyhFEUb?;4@X7weEnLjXj_F~?OzL1U1L0|s6M+kIhmi%`n5vvDALMagi4`wMc=JV{XiO+^ z?s9i7;GgrRW{Mx)d7rj)?(;|b-`iBNPqdwtt%32se@?w4<^KU&585_kZ=`Wy^oLu9 z?DQAh5z%q;UkP48jgMFHTf#mj?#z|=w= z(q6~17Vn}P)J3M?O)x))%a5+>TFW3No~TgP;f}K$#icBh;rSS+R|}l鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862f..1af9e0930 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 01928d7df5dda5a4187e07baf99685551a85a868 Mon Sep 17 00:00:00 2001 From: Ben Trengrove Date: Thu, 30 Nov 2023 17:09:06 +1100 Subject: [PATCH 15/70] Move scrollbar changes out of composition --- .../component/scrollbar/AppScrollbars.kt | 5 +- .../component/scrollbar/Scrollbar.kt | 185 ++++++++++-------- .../component/scrollbar/ScrollbarExt.kt | 13 +- 3 files changed, 114 insertions(+), 89 deletions(-) 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 fa913cb27..beece9071 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 @@ -38,6 +38,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -66,7 +67,7 @@ private const val SCROLLBAR_INACTIVE_TO_DORMANT_TIME_IN_MS = 2_000L @Composable fun ScrollableState.DraggableScrollbar( modifier: Modifier = Modifier, - state: ScrollbarState, + state: State, orientation: Orientation, onThumbMoved: (Float) -> Unit, ) { @@ -96,7 +97,7 @@ fun ScrollableState.DraggableScrollbar( @Composable fun ScrollableState.DecorativeScrollbar( modifier: Modifier = Modifier, - state: ScrollbarState, + state: State, orientation: Orientation, ) { val interactionSource = remember { MutableInteractionSource() } 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 c1281a4c0..b147ac2d2 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 @@ -16,8 +16,9 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar -import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.Orientation.Horizontal +import androidx.compose.foundation.gestures.Orientation.Vertical import androidx.compose.foundation.gestures.detectHorizontalDragGestures import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectVerticalDragGestures @@ -28,31 +29,30 @@ import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.max import androidx.compose.ui.util.packFloats import androidx.compose.ui.util.unpackFloat1 import androidx.compose.ui.util.unpackFloat2 @@ -61,6 +61,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout import kotlin.math.max import kotlin.math.min +import kotlin.math.roundToInt /** * The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll @@ -191,7 +192,7 @@ internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) { fun Scrollbar( modifier: Modifier = Modifier, orientation: Orientation, - state: ScrollbarState, + state: State, minThumbSize: Dp = 40.dp, interactionSource: MutableInteractionSource? = null, thumb: @Composable () -> Unit, @@ -210,23 +211,6 @@ fun Scrollbar( var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) } - val thumbTravelPercent = when { - interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent - else -> interactionThumbTravelPercent - } - val thumbSizePx = max( - a = state.thumbSizePercent * track.size, - b = with(localDensity) { minThumbSize.toPx() }, - ) - val thumbSizeDp by animateDpAsState( - targetValue = with(localDensity) { thumbSizePx.toDp() }, - label = "scrollbar thumb size", - ) - val thumbMovedPx = min( - a = track.size * thumbTravelPercent, - b = track.size - thumbSizePx, - ) - // scrollbar track container Box( modifier = modifier @@ -320,30 +304,67 @@ fun Scrollbar( } }, ) { - val scrollbarThumbMovedDp = max( - a = with(localDensity) { thumbMovedPx.toDp() }, - b = 0.dp, - ) // scrollbar thumb container Box( modifier = Modifier .align(Alignment.TopStart) - .run { - when (orientation) { - Orientation.Horizontal -> width(thumbSizeDp) - Orientation.Vertical -> height(thumbSizeDp) + .layout { measurable, constraints -> + val state = state.value + val thumbSizePx = max( + a = state.thumbSizePercent * track.size, + b = minThumbSize.toPx(), + ) + + val thumbTravelPercent = when { + interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent + else -> interactionThumbTravelPercent + } + + val thumbMovedPx = min( + a = track.size * thumbTravelPercent, + b = track.size - thumbSizePx, + ) + + val scrollbarThumbMovedDp = max( + a = thumbMovedPx, + b = 0f, + ) + + val y = when (orientation) { + Horizontal -> 0 + Vertical -> scrollbarThumbMovedDp + } + val x = when (orientation) { + Horizontal -> scrollbarThumbMovedDp + Vertical -> 0 + } + + val offset = IntOffset(x.toInt(), y.toInt()) + + val constrained = when (orientation) { + Horizontal -> { + Constraints( + minWidth = thumbSizePx.roundToInt(), + maxWidth = thumbSizePx.roundToInt(), + minHeight = constraints.minHeight, + maxHeight = constraints.maxHeight + ) + } + Vertical -> { + Constraints( + minWidth = constraints.minWidth, + maxWidth = constraints.maxWidth, + minHeight = thumbSizePx.roundToInt(), + maxHeight = thumbSizePx.roundToInt() + ) + } + } + + val placeable = measurable.measure(constrained) + layout(placeable.width, placeable.height) { + placeable.place(offset) } } - .offset( - y = when (orientation) { - Orientation.Horizontal -> 0.dp - Orientation.Vertical -> scrollbarThumbMovedDp - }, - x = when (orientation) { - Orientation.Horizontal -> scrollbarThumbMovedDp - Orientation.Vertical -> 0.dp - }, - ), ) { thumb() } @@ -351,53 +372,55 @@ fun Scrollbar( if (onThumbMoved == null) return - // State that will be read inside the effects that follow - // but will not cause re-triggering of them - val updatedState by rememberUpdatedState(state) - // Process presses - LaunchedEffect(pressedOffset) { - // Press ended, reset interactionThumbTravelPercent - if (pressedOffset == Offset.Unspecified) { - interactionThumbTravelPercent = Float.NaN - return@LaunchedEffect - } + LaunchedEffect(Unit) { + snapshotFlow { pressedOffset }.collect { pressedOffset -> + // Press ended, reset interactionThumbTravelPercent + if (pressedOffset == Offset.Unspecified) { + interactionThumbTravelPercent = Float.NaN + return@collect + } - var currentThumbMovedPercent = updatedState.thumbMovedPercent - val destinationThumbMovedPercent = track.thumbPosition( - dimension = orientation.valueOf(pressedOffset), - ) - val isPositive = currentThumbMovedPercent < destinationThumbMovedPercent - val delta = SCROLLBAR_PRESS_DELTA_PCT * if (isPositive) 1f else -1f - - while (currentThumbMovedPercent != destinationThumbMovedPercent) { - currentThumbMovedPercent = when { - isPositive -> min( - a = currentThumbMovedPercent + delta, - b = destinationThumbMovedPercent, - ) + var currentThumbMovedPercent = state.value.thumbMovedPercent + val destinationThumbMovedPercent = track.thumbPosition( + dimension = orientation.valueOf(pressedOffset), + ) + val isPositive = currentThumbMovedPercent < destinationThumbMovedPercent + val delta = SCROLLBAR_PRESS_DELTA_PCT * if (isPositive) 1f else -1f + + while (currentThumbMovedPercent != destinationThumbMovedPercent) { + currentThumbMovedPercent = when { + isPositive -> min( + a = currentThumbMovedPercent + delta, + b = destinationThumbMovedPercent, + ) - else -> max( - a = currentThumbMovedPercent + delta, - b = destinationThumbMovedPercent, - ) + else -> max( + a = currentThumbMovedPercent + delta, + b = destinationThumbMovedPercent, + ) + } + onThumbMoved(currentThumbMovedPercent) + interactionThumbTravelPercent = currentThumbMovedPercent + delay(SCROLLBAR_PRESS_DELAY_MS) } - onThumbMoved(currentThumbMovedPercent) - interactionThumbTravelPercent = currentThumbMovedPercent - delay(SCROLLBAR_PRESS_DELAY_MS) + + } } // Process drags - LaunchedEffect(draggedOffset) { - if (draggedOffset == Offset.Unspecified) { - interactionThumbTravelPercent = Float.NaN - return@LaunchedEffect + LaunchedEffect(Unit) { + snapshotFlow { draggedOffset }.collect { draggedOffset -> + if (draggedOffset == Offset.Unspecified) { + interactionThumbTravelPercent = Float.NaN + return@collect + } + val currentTravel = track.thumbPosition( + dimension = orientation.valueOf(draggedOffset), + ) + onThumbMoved(currentTravel) + interactionThumbTravelPercent = currentTravel } - val currentTravel = track.thumbPosition( - dimension = orientation.valueOf(draggedOffset), - ) - onThumbMoved(currentTravel) - interactionThumbTravelPercent = currentTravel } } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt index 7a0282bf7..67abce1b7 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemInfo import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.produceState import androidx.compose.runtime.snapshotFlow import kotlinx.coroutines.flow.distinctUntilChanged @@ -40,7 +41,7 @@ import kotlin.math.min fun LazyListState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyListItemInfo) -> Int = LazyListItemInfo::index, -): ScrollbarState = produceState( +): State = produceState( initialValue = ScrollbarState.FULL, key1 = this, key2 = itemsAvailable, @@ -91,7 +92,7 @@ fun LazyListState.scrollbarState( .filterNotNull() .distinctUntilChanged() .collect { value = it } -}.value +} /** * Calculates a [ScrollbarState] driven by the changes in a [LazyGridState] @@ -103,7 +104,7 @@ fun LazyListState.scrollbarState( fun LazyGridState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyGridItemInfo) -> Int = LazyGridItemInfo::index, -): ScrollbarState = produceState( +): State = produceState( initialValue = ScrollbarState.FULL, key1 = this, key2 = itemsAvailable, @@ -164,7 +165,7 @@ fun LazyGridState.scrollbarState( .filterNotNull() .distinctUntilChanged() .collect { value = it } -}.value +} /** * Remembers a [ScrollbarState] driven by the changes in a [LazyStaggeredGridState] @@ -177,7 +178,7 @@ fun LazyGridState.scrollbarState( fun LazyStaggeredGridState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyStaggeredGridItemInfo) -> Int = LazyStaggeredGridItemInfo::index, -): ScrollbarState = produceState( +): State = produceState( initialValue = ScrollbarState.FULL, key1 = this, key2 = itemsAvailable, @@ -227,7 +228,7 @@ fun LazyStaggeredGridState.scrollbarState( .filterNotNull() .distinctUntilChanged() .collect { value = it } -}.value +} private inline fun List.floatSumOf(selector: (T) -> Float): Float { var sum = 0f From d8880e98f073fd2da89fb8e6be13d0191ec32ed8 Mon Sep 17 00:00:00 2001 From: Ben Trengrove Date: Fri, 1 Dec 2023 09:38:51 +1100 Subject: [PATCH 16/70] Move state into a state holder and remove redundant Box --- .../component/scrollbar/AppScrollbars.kt | 5 +- .../component/scrollbar/Scrollbar.kt | 196 ++++++----- .../component/scrollbar/ScrollbarExt.kt | 306 +++++++++--------- 3 files changed, 247 insertions(+), 260 deletions(-) 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 beece9071..fa913cb27 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 @@ -38,7 +38,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -67,7 +66,7 @@ private const val SCROLLBAR_INACTIVE_TO_DORMANT_TIME_IN_MS = 2_000L @Composable fun ScrollableState.DraggableScrollbar( modifier: Modifier = Modifier, - state: State, + state: ScrollbarState, orientation: Orientation, onThumbMoved: (Float) -> Unit, ) { @@ -97,7 +96,7 @@ fun ScrollableState.DraggableScrollbar( @Composable fun ScrollableState.DecorativeScrollbar( modifier: Modifier = Modifier, - state: State, + state: ScrollbarState, orientation: Orientation, ) { val interactionSource = remember { MutableInteractionSource() } 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 b147ac2d2..bb3b08c3a 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 @@ -32,9 +32,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -44,10 +44,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInRoot -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset @@ -75,21 +75,53 @@ private const val SCROLLBAR_PRESS_DELAY_MS = 10L */ private const val SCROLLBAR_PRESS_DELTA_PCT = 0.02f +class ScrollbarState { + private var packedValue by mutableLongStateOf(0L) + + internal fun onScroll(stateValue: ScrollbarStateValue) { + packedValue = stateValue.packedValue + } + + /** + * Returns the thumb size of the scrollbar as a percentage of the total track size + */ + val thumbSizePercent + get() = unpackFloat1(packedValue) + + /** + * Returns the distance the thumb has traveled as a percentage of total track size + */ + val thumbMovedPercent + get() = unpackFloat2(packedValue) +} + +/** + * Returns the size of the scrollbar track in pixels + */ +private val ScrollbarTrack.size + get() = unpackFloat2(packedValue) - unpackFloat1(packedValue) + +/** + * Returns the position of the scrollbar thumb on the track as a percentage + */ +private fun ScrollbarTrack.thumbPosition( + dimension: Float, +): Float = max( + a = min( + a = dimension / size, + b = 1f, + ), + b = 0f, +) + /** * Class definition for the core properties of a scroll bar */ @Immutable @JvmInline -value class ScrollbarState internal constructor( +value class ScrollbarStateValue internal constructor( internal val packedValue: Long, -) { - companion object { - val FULL = ScrollbarState( - thumbSizePercent = 1f, - thumbMovedPercent = 0f, - ) - } -} +) /** * Class definition for the core properties of a scroll bar track @@ -106,54 +138,23 @@ private value class ScrollbarTrack( } /** - * Creates a [ScrollbarState] with the listed properties + * Creates a [ScrollbarStateValue] with the listed properties * @param thumbSizePercent the thumb size of the scrollbar as a percentage of the total track size. * Refers to either the thumb width (for horizontal scrollbars) * or height (for vertical scrollbars). * @param thumbMovedPercent the distance the thumb has traveled as a percentage of total * track size. */ -fun ScrollbarState( +fun scrollbarStateValue( thumbSizePercent: Float, thumbMovedPercent: Float, -) = ScrollbarState( +) = ScrollbarStateValue( packFloats( val1 = thumbSizePercent, val2 = thumbMovedPercent, ), ) -/** - * Returns the thumb size of the scrollbar as a percentage of the total track size - */ -val ScrollbarState.thumbSizePercent - get() = unpackFloat1(packedValue) - -/** - * Returns the distance the thumb has traveled as a percentage of total track size - */ -val ScrollbarState.thumbMovedPercent - get() = unpackFloat2(packedValue) - -/** - * Returns the size of the scrollbar track in pixels - */ -private val ScrollbarTrack.size - get() = unpackFloat2(packedValue) - unpackFloat1(packedValue) - -/** - * Returns the position of the scrollbar thumb on the track as a percentage - */ -private fun ScrollbarTrack.thumbPosition( - dimension: Float, -): Float = max( - a = min( - a = dimension / size, - b = 1f, - ), - b = 0f, -) - /** * Returns the value of [offset] along the axis specified by [this] */ @@ -192,14 +193,12 @@ internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) { fun Scrollbar( modifier: Modifier = Modifier, orientation: Orientation, - state: State, + state: ScrollbarState, minThumbSize: Dp = 40.dp, interactionSource: MutableInteractionSource? = null, thumb: @Composable () -> Unit, onThumbMoved: ((Float) -> Unit)? = null, ) { - val localDensity = LocalDensity.current - // Using Offset.Unspecified and Float.NaN instead of null // to prevent unnecessary boxing of primitives var pressedOffset by remember { mutableStateOf(Offset.Unspecified) } @@ -305,68 +304,57 @@ fun Scrollbar( }, ) { // scrollbar thumb container - Box( - modifier = Modifier - .align(Alignment.TopStart) - .layout { measurable, constraints -> - val state = state.value - val thumbSizePx = max( - a = state.thumbSizePercent * track.size, - b = minThumbSize.toPx(), - ) + Layout(content = { thumb() }) { measurables, constraints -> + val measurable = measurables.first() - val thumbTravelPercent = when { - interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent - else -> interactionThumbTravelPercent - } - - val thumbMovedPx = min( - a = track.size * thumbTravelPercent, - b = track.size - thumbSizePx, - ) + val thumbSizePx = max( + a = state.thumbSizePercent * track.size, + b = minThumbSize.toPx(), + ) - val scrollbarThumbMovedDp = max( - a = thumbMovedPx, - b = 0f, - ) + val thumbTravelPercent = when { + interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent + else -> interactionThumbTravelPercent + } - val y = when (orientation) { - Horizontal -> 0 - Vertical -> scrollbarThumbMovedDp - } - val x = when (orientation) { - Horizontal -> scrollbarThumbMovedDp - Vertical -> 0 - } + val thumbMovedPx = min( + a = track.size * thumbTravelPercent, + b = track.size - thumbSizePx, + ) - val offset = IntOffset(x.toInt(), y.toInt()) + val scrollbarThumbMovedPx = max( + a = thumbMovedPx.roundToInt(), + b = 0, + ) - val constrained = when (orientation) { - Horizontal -> { - Constraints( - minWidth = thumbSizePx.roundToInt(), - maxWidth = thumbSizePx.roundToInt(), - minHeight = constraints.minHeight, - maxHeight = constraints.maxHeight - ) - } - Vertical -> { - Constraints( - minWidth = constraints.minWidth, - maxWidth = constraints.maxWidth, - minHeight = thumbSizePx.roundToInt(), - maxHeight = thumbSizePx.roundToInt() - ) - } - } + val y = when (orientation) { + Horizontal -> 0 + Vertical -> scrollbarThumbMovedPx + } + val x = when (orientation) { + Horizontal -> scrollbarThumbMovedPx + Vertical -> 0 + } - val placeable = measurable.measure(constrained) - layout(placeable.width, placeable.height) { - placeable.place(offset) - } + val updatedConstraints = when (orientation) { + Horizontal -> { + constraints.copy( + minWidth = thumbSizePx.roundToInt(), + maxWidth = thumbSizePx.roundToInt() + ) } - ) { - thumb() + Vertical -> { + constraints.copy( + minHeight = thumbSizePx.roundToInt(), + maxHeight = thumbSizePx.roundToInt() + ) + } + } + + val placeable = measurable.measure(updatedConstraints) + layout(placeable.width, placeable.height) { + placeable.place(x, y) + } } } @@ -381,7 +369,7 @@ fun Scrollbar( return@collect } - var currentThumbMovedPercent = state.value.thumbMovedPercent + var currentThumbMovedPercent = state.thumbMovedPercent val destinationThumbMovedPercent = track.thumbPosition( dimension = orientation.valueOf(pressedOffset), ) diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt index 67abce1b7..a55f62f5f 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt @@ -24,8 +24,8 @@ import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemInfo import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.produceState +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull @@ -41,57 +41,57 @@ import kotlin.math.min fun LazyListState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyListItemInfo) -> Int = LazyListItemInfo::index, -): State = produceState( - initialValue = ScrollbarState.FULL, - key1 = this, - key2 = itemsAvailable, -) { - snapshotFlow { - if (itemsAvailable == 0) return@snapshotFlow null - - val visibleItemsInfo = layoutInfo.visibleItemsInfo - if (visibleItemsInfo.isEmpty()) return@snapshotFlow null - - val firstIndex = min( - a = interpolateFirstItemIndex( - visibleItems = visibleItemsInfo, - itemSize = { it.size }, - offset = { it.offset }, - nextItemOnMainAxis = { first -> visibleItemsInfo.find { it != first } }, - itemIndex = itemIndex, - ), - b = itemsAvailable.toFloat(), - ) - if (firstIndex.isNaN()) return@snapshotFlow null - - val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> - itemVisibilityPercentage( - itemSize = itemInfo.size, - itemStartOffset = itemInfo.offset, - viewportStartOffset = layoutInfo.viewportStartOffset, - viewportEndOffset = layoutInfo.viewportEndOffset, +): ScrollbarState { + val state = remember { ScrollbarState() } + LaunchedEffect(this, itemsAvailable) { + snapshotFlow { + if (itemsAvailable == 0) return@snapshotFlow null + + val visibleItemsInfo = layoutInfo.visibleItemsInfo + if (visibleItemsInfo.isEmpty()) return@snapshotFlow null + + val firstIndex = min( + a = interpolateFirstItemIndex( + visibleItems = visibleItemsInfo, + itemSize = { it.size }, + offset = { it.offset }, + nextItemOnMainAxis = { first -> visibleItemsInfo.find { it != first } }, + itemIndex = itemIndex, + ), + b = itemsAvailable.toFloat(), + ) + if (firstIndex.isNaN()) return@snapshotFlow null + + val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> + itemVisibilityPercentage( + itemSize = itemInfo.size, + itemStartOffset = itemInfo.offset, + viewportStartOffset = layoutInfo.viewportStartOffset, + viewportEndOffset = layoutInfo.viewportEndOffset, + ) + } + + val thumbTravelPercent = min( + a = firstIndex / itemsAvailable, + b = 1f, + ) + val thumbSizePercent = min( + a = itemsVisible / itemsAvailable, + b = 1f, + ) + scrollbarStateValue( + thumbSizePercent = thumbSizePercent, + thumbMovedPercent = when { + layoutInfo.reverseLayout -> 1f - thumbTravelPercent + else -> thumbTravelPercent + }, ) } - - val thumbTravelPercent = min( - a = firstIndex / itemsAvailable, - b = 1f, - ) - val thumbSizePercent = min( - a = itemsVisible / itemsAvailable, - b = 1f, - ) - ScrollbarState( - thumbSizePercent = thumbSizePercent, - thumbMovedPercent = when { - layoutInfo.reverseLayout -> 1f - thumbTravelPercent - else -> thumbTravelPercent - }, - ) + .filterNotNull() + .distinctUntilChanged() + .collect { state.onScroll(it) } } - .filterNotNull() - .distinctUntilChanged() - .collect { value = it } + return state } /** @@ -104,67 +104,67 @@ fun LazyListState.scrollbarState( fun LazyGridState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyGridItemInfo) -> Int = LazyGridItemInfo::index, -): State = produceState( - initialValue = ScrollbarState.FULL, - key1 = this, - key2 = itemsAvailable, -) { - snapshotFlow { - if (itemsAvailable == 0) return@snapshotFlow null - - val visibleItemsInfo = layoutInfo.visibleItemsInfo - if (visibleItemsInfo.isEmpty()) return@snapshotFlow null - - val firstIndex = min( - a = interpolateFirstItemIndex( - visibleItems = visibleItemsInfo, - itemSize = { layoutInfo.orientation.valueOf(it.size) }, - offset = { layoutInfo.orientation.valueOf(it.offset) }, - nextItemOnMainAxis = { first -> - when (layoutInfo.orientation) { - Orientation.Vertical -> visibleItemsInfo.find { - it != first && it.row != first.row - } - - Orientation.Horizontal -> visibleItemsInfo.find { - it != first && it.column != first.column +): ScrollbarState { + val state = remember { ScrollbarState() } + LaunchedEffect(this, itemsAvailable) { + snapshotFlow { + if (itemsAvailable == 0) return@snapshotFlow null + + val visibleItemsInfo = layoutInfo.visibleItemsInfo + if (visibleItemsInfo.isEmpty()) return@snapshotFlow null + + val firstIndex = min( + a = interpolateFirstItemIndex( + visibleItems = visibleItemsInfo, + itemSize = { layoutInfo.orientation.valueOf(it.size) }, + offset = { layoutInfo.orientation.valueOf(it.offset) }, + nextItemOnMainAxis = { first -> + when (layoutInfo.orientation) { + Orientation.Vertical -> visibleItemsInfo.find { + it != first && it.row != first.row + } + + Orientation.Horizontal -> visibleItemsInfo.find { + it != first && it.column != first.column + } } - } + }, + itemIndex = itemIndex, + ), + b = itemsAvailable.toFloat(), + ) + if (firstIndex.isNaN()) return@snapshotFlow null + + val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> + itemVisibilityPercentage( + itemSize = layoutInfo.orientation.valueOf(itemInfo.size), + itemStartOffset = layoutInfo.orientation.valueOf(itemInfo.offset), + viewportStartOffset = layoutInfo.viewportStartOffset, + viewportEndOffset = layoutInfo.viewportEndOffset, + ) + } + + val thumbTravelPercent = min( + a = firstIndex / itemsAvailable, + b = 1f, + ) + val thumbSizePercent = min( + a = itemsVisible / itemsAvailable, + b = 1f, + ) + scrollbarStateValue( + thumbSizePercent = thumbSizePercent, + thumbMovedPercent = when { + layoutInfo.reverseLayout -> 1f - thumbTravelPercent + else -> thumbTravelPercent }, - itemIndex = itemIndex, - ), - b = itemsAvailable.toFloat(), - ) - if (firstIndex.isNaN()) return@snapshotFlow null - - val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> - itemVisibilityPercentage( - itemSize = layoutInfo.orientation.valueOf(itemInfo.size), - itemStartOffset = layoutInfo.orientation.valueOf(itemInfo.offset), - viewportStartOffset = layoutInfo.viewportStartOffset, - viewportEndOffset = layoutInfo.viewportEndOffset, ) } - - val thumbTravelPercent = min( - a = firstIndex / itemsAvailable, - b = 1f, - ) - val thumbSizePercent = min( - a = itemsVisible / itemsAvailable, - b = 1f, - ) - ScrollbarState( - thumbSizePercent = thumbSizePercent, - thumbMovedPercent = when { - layoutInfo.reverseLayout -> 1f - thumbTravelPercent - else -> thumbTravelPercent - }, - ) + .filterNotNull() + .distinctUntilChanged() + .collect { state.onScroll(it) } } - .filterNotNull() - .distinctUntilChanged() - .collect { value = it } + return state } /** @@ -178,56 +178,56 @@ fun LazyGridState.scrollbarState( fun LazyStaggeredGridState.scrollbarState( itemsAvailable: Int, itemIndex: (LazyStaggeredGridItemInfo) -> Int = LazyStaggeredGridItemInfo::index, -): State = produceState( - initialValue = ScrollbarState.FULL, - key1 = this, - key2 = itemsAvailable, -) { - snapshotFlow { - if (itemsAvailable == 0) return@snapshotFlow null - - val visibleItemsInfo = layoutInfo.visibleItemsInfo - if (visibleItemsInfo.isEmpty()) return@snapshotFlow null - - val firstIndex = min( - a = interpolateFirstItemIndex( - visibleItems = visibleItemsInfo, - itemSize = { layoutInfo.orientation.valueOf(it.size) }, - offset = { layoutInfo.orientation.valueOf(it.offset) }, - nextItemOnMainAxis = { first -> - visibleItemsInfo.find { it != first && it.lane == first.lane } - }, - itemIndex = itemIndex, - ), - b = itemsAvailable.toFloat(), - ) - if (firstIndex.isNaN()) return@snapshotFlow null - - val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> - itemVisibilityPercentage( - itemSize = layoutInfo.orientation.valueOf(itemInfo.size), - itemStartOffset = layoutInfo.orientation.valueOf(itemInfo.offset), - viewportStartOffset = layoutInfo.viewportStartOffset, - viewportEndOffset = layoutInfo.viewportEndOffset, +): ScrollbarState { + val state = remember { ScrollbarState() } + LaunchedEffect(this, itemsAvailable) { + snapshotFlow { + if (itemsAvailable == 0) return@snapshotFlow null + + val visibleItemsInfo = layoutInfo.visibleItemsInfo + if (visibleItemsInfo.isEmpty()) return@snapshotFlow null + + val firstIndex = min( + a = interpolateFirstItemIndex( + visibleItems = visibleItemsInfo, + itemSize = { layoutInfo.orientation.valueOf(it.size) }, + offset = { layoutInfo.orientation.valueOf(it.offset) }, + nextItemOnMainAxis = { first -> + visibleItemsInfo.find { it != first && it.lane == first.lane } + }, + itemIndex = itemIndex, + ), + b = itemsAvailable.toFloat(), + ) + if (firstIndex.isNaN()) return@snapshotFlow null + + val itemsVisible = visibleItemsInfo.floatSumOf { itemInfo -> + itemVisibilityPercentage( + itemSize = layoutInfo.orientation.valueOf(itemInfo.size), + itemStartOffset = layoutInfo.orientation.valueOf(itemInfo.offset), + viewportStartOffset = layoutInfo.viewportStartOffset, + viewportEndOffset = layoutInfo.viewportEndOffset, + ) + } + + val thumbTravelPercent = min( + a = firstIndex / itemsAvailable, + b = 1f, + ) + val thumbSizePercent = min( + a = itemsVisible / itemsAvailable, + b = 1f, + ) + scrollbarStateValue( + thumbSizePercent = thumbSizePercent, + thumbMovedPercent = thumbTravelPercent, ) } - - val thumbTravelPercent = min( - a = firstIndex / itemsAvailable, - b = 1f, - ) - val thumbSizePercent = min( - a = itemsVisible / itemsAvailable, - b = 1f, - ) - ScrollbarState( - thumbSizePercent = thumbSizePercent, - thumbMovedPercent = thumbTravelPercent, - ) + .filterNotNull() + .distinctUntilChanged() + .collect { state.onScroll(it) } } - .filterNotNull() - .distinctUntilChanged() - .collect { value = it } + return state } private inline fun List.floatSumOf(selector: (T) -> Float): Float { From 72da8e2e56806c55f531e1f8dda7941e75307a4c Mon Sep 17 00:00:00 2001 From: Ben Trengrove Date: Fri, 1 Dec 2023 10:08:59 +1100 Subject: [PATCH 17/70] Use custom Modifier.Node instead of background --- .../component/scrollbar/AppScrollbars.kt | 87 +++++++++++++++---- 1 file changed, 69 insertions(+), 18 deletions(-) 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 fa913cb27..71b673a71 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 @@ -16,6 +16,7 @@ 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 @@ -38,17 +39,31 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorProducer +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawOutline +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.node.DrawModifierNode +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Active import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Dormant import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Inactive +import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.launch /** * The time period for showing the scrollbar thumb after interacting with it, before it fades away @@ -130,12 +145,7 @@ private fun ScrollableState.DraggableScrollbarThumb( Horizontal -> height(12.dp).fillMaxWidth() } } - .background( - color = scrollbarThumbColor( - interactionSource = interactionSource, - ), - shape = RoundedCornerShape(16.dp), - ), + .scrollThumb(this, interactionSource), ) } @@ -155,31 +165,72 @@ private fun ScrollableState.DecorativeScrollbarThumb( Horizontal -> height(2.dp).fillMaxWidth() } } - .background( - color = scrollbarThumbColor( - interactionSource = interactionSource, - ), - shape = RoundedCornerShape(16.dp), - ), + .scrollThumb(this, interactionSource), ) } +// 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, + interactionSource: InteractionSource +): Modifier { + val colorState = scrollbarThumbColor(scrollableState, interactionSource) + return this then ScrollThumbElement { colorState.value } +} + +private data class ScrollThumbElement(val colorProducer: ColorProducer) + : ModifierNodeElement() { + override fun create(): ScrollThumbNode = ScrollThumbNode(colorProducer) + override fun update(node: ScrollThumbNode) { + node.colorProducer = colorProducer + node.invalidateDraw() + } +} + +private class ScrollThumbNode(var colorProducer: ColorProducer): DrawModifierNode, Modifier.Node() { + private val shape = RoundedCornerShape(16.dp) + + // naive cache outline calculation if size is the same + private var lastSize: Size? = null + private var lastLayoutDirection: LayoutDirection? = null + private var lastOutline: Outline? = null + + override fun ContentDrawScope.draw() { + val color = colorProducer() + val outline = + if (size == lastSize && layoutDirection == lastLayoutDirection) { + lastOutline!! + } else { + shape.createOutline(size, layoutDirection, this) + } + if (color != Color.Unspecified) drawOutline(outline, color = color) + + lastOutline = outline + lastSize = size + lastLayoutDirection = layoutDirection + } +} + /** * The color of the scrollbar thumb as a function of its interaction state. * @param interactionSource source of interactions in the scrolling container */ @Composable -private fun ScrollableState.scrollbarThumbColor( - interactionSource: InteractionSource, -): Color { +private fun scrollbarThumbColor( + scrollableState: ScrollableState, + interactionSource: InteractionSource +): State { var state by remember { mutableStateOf(Dormant) } val pressed by interactionSource.collectIsPressedAsState() val hovered by interactionSource.collectIsHoveredAsState() val dragged by interactionSource.collectIsDraggedAsState() - val active = (canScrollForward || canScrollForward) && - (pressed || hovered || dragged || isScrollInProgress) + val active = (scrollableState.canScrollForward || scrollableState.canScrollForward) && + (pressed || hovered || dragged || scrollableState.isScrollInProgress) - val color by animateColorAsState( + val color = animateColorAsState( targetValue = when (state) { Active -> MaterialTheme.colorScheme.onSurface.copy(0.5f) Inactive -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f) From 22df321b9eced5edc4d073757a2c9226157c6875 Mon Sep 17 00:00:00 2001 From: Ben Trengrove Date: Fri, 1 Dec 2023 10:51:45 +1100 Subject: [PATCH 18/70] Apply spotless --- .../component/scrollbar/AppScrollbars.kt | 15 +++++---------- .../designsystem/component/scrollbar/Scrollbar.kt | 8 ++------ 2 files changed, 7 insertions(+), 16 deletions(-) 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 71b673a71..5b6776352 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 @@ -20,7 +20,6 @@ import android.annotation.SuppressLint import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Spring import androidx.compose.animation.core.SpringSpec -import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation.Horizontal import androidx.compose.foundation.gestures.Orientation.Vertical @@ -49,7 +48,6 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorProducer import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.node.DrawModifierNode @@ -60,10 +58,7 @@ import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Active import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Dormant import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Inactive -import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.launch /** * The time period for showing the scrollbar thumb after interacting with it, before it fades away @@ -175,14 +170,14 @@ private fun ScrollableState.DecorativeScrollbarThumb( @Composable private fun Modifier.scrollThumb( scrollableState: ScrollableState, - interactionSource: InteractionSource + interactionSource: InteractionSource, ): Modifier { val colorState = scrollbarThumbColor(scrollableState, interactionSource) return this then ScrollThumbElement { colorState.value } } -private data class ScrollThumbElement(val colorProducer: ColorProducer) - : ModifierNodeElement() { +private data class ScrollThumbElement(val colorProducer: ColorProducer) : + ModifierNodeElement() { override fun create(): ScrollThumbNode = ScrollThumbNode(colorProducer) override fun update(node: ScrollThumbNode) { node.colorProducer = colorProducer @@ -190,7 +185,7 @@ private data class ScrollThumbElement(val colorProducer: ColorProducer) } } -private class ScrollThumbNode(var colorProducer: ColorProducer): DrawModifierNode, Modifier.Node() { +private class ScrollThumbNode(var colorProducer: ColorProducer) : DrawModifierNode, Modifier.Node() { private val shape = RoundedCornerShape(16.dp) // naive cache outline calculation if size is the same @@ -221,7 +216,7 @@ private class ScrollThumbNode(var colorProducer: ColorProducer): DrawModifierNod @Composable private fun scrollbarThumbColor( scrollableState: ScrollableState, - interactionSource: InteractionSource + interactionSource: InteractionSource, ): State { var state by remember { mutableStateOf(Dormant) } val pressed by interactionSource.collectIsPressedAsState() 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 bb3b08c3a..5041eff7b 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 @@ -39,7 +39,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerInputChange @@ -48,7 +47,6 @@ import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInRoot -import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize @@ -340,13 +338,13 @@ fun Scrollbar( Horizontal -> { constraints.copy( minWidth = thumbSizePx.roundToInt(), - maxWidth = thumbSizePx.roundToInt() + maxWidth = thumbSizePx.roundToInt(), ) } Vertical -> { constraints.copy( minHeight = thumbSizePx.roundToInt(), - maxHeight = thumbSizePx.roundToInt() + maxHeight = thumbSizePx.roundToInt(), ) } } @@ -392,8 +390,6 @@ fun Scrollbar( interactionThumbTravelPercent = currentThumbMovedPercent delay(SCROLLBAR_PRESS_DELAY_MS) } - - } } From 3142ecb4955c82f7443eb6f40ed384f5877cb384 Mon Sep 17 00:00:00 2001 From: Ben Trengrove Date: Tue, 5 Dec 2023 11:03:55 +1100 Subject: [PATCH 19/70] Remove redundant mutable state --- .../google/samples/apps/nowinandroid/core/ui/NewsFeed.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt index e2904afc3..afdb584a2 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt @@ -30,9 +30,6 @@ import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.items import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext @@ -64,9 +61,6 @@ fun LazyStaggeredGridScope.newsFeed( key = { it.id }, contentType = { "newsFeedItem" }, ) { userNewsResource -> - val resourceUrl by remember { - mutableStateOf(Uri.parse(userNewsResource.url)) - } val context = LocalContext.current val analyticsHelper = LocalAnalyticsHelper.current val backgroundColor = MaterialTheme.colorScheme.background.toArgb() @@ -79,7 +73,8 @@ fun LazyStaggeredGridScope.newsFeed( analyticsHelper.logNewsResourceOpened( newsResourceId = userNewsResource.id, ) - launchCustomChromeTab(context, resourceUrl, backgroundColor) + launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor) + onNewsResourceViewed(userNewsResource.id) }, hasBeenViewed = userNewsResource.hasBeenViewed, From e334a727c35c3dc28a8bab954e822450fedbda5d Mon Sep 17 00:00:00 2001 From: dahunsi Date: Mon, 4 Dec 2023 16:13:11 -0800 Subject: [PATCH 20/70] Fix typo in app scrollbars Change-Id: Ie72f1410fa8e5a68a520321c34d4fad251016486 --- .../core/designsystem/component/scrollbar/AppScrollbars.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5b6776352..fe14bce14 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 @@ -222,7 +222,7 @@ private fun scrollbarThumbColor( val pressed by interactionSource.collectIsPressedAsState() val hovered by interactionSource.collectIsHoveredAsState() val dragged by interactionSource.collectIsDraggedAsState() - val active = (scrollableState.canScrollForward || scrollableState.canScrollForward) && + val active = (scrollableState.canScrollForward || scrollableState.canScrollBackward) && (pressed || hovered || dragged || scrollableState.isScrollInProgress) val color = animateColorAsState( From a96e0709d83d0df6c3432018661dcd204354be51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:06:44 +0000 Subject: [PATCH 21/70] Bump actions/setup-java from 3 to 4 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/Build.yaml | 4 ++-- .github/workflows/Release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index ca161b0a4..9c103ca46 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -31,7 +31,7 @@ jobs: run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 @@ -128,7 +128,7 @@ jobs: run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index f85215b74..f4901b9e2 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -21,7 +21,7 @@ jobs: run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 From af26182193a4752c0c7a64ef2636166f3d8a04a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:10:32 +0000 Subject: [PATCH 22/70] Bump hilt from 2.48.1 to 2.49 Bumps `hilt` from 2.48.1 to 2.49. Updates `com.google.dagger:hilt-android` from 2.48.1 to 2.49 - [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.48.1...dagger-2.49) Updates `com.google.dagger:hilt-android-testing` from 2.48.1 to 2.49 - [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.48.1...dagger-2.49) Updates `com.google.dagger:hilt-android-compiler` from 2.48.1 to 2.49 - [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.48.1...dagger-2.49) Updates `com.google.dagger.hilt.android` from 2.48.1 to 2.49 - [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.48.1...dagger-2.49) --- 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 aa2a32e90..c1f426bd3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" googleOss = "17.0.1" googleOssPlugin = "0.10.6" -hilt = "2.48.1" +hilt = "2.49" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" From b80e8a5e3f851e62ebe021a75bc8f5697b1f9c54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:10:36 +0000 Subject: [PATCH 23/70] Bump org.jetbrains.kotlinx:kotlinx-datetime from 0.4.1 to 0.5.0 Bumps [org.jetbrains.kotlinx:kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) from 0.4.1 to 0.5.0. - [Release notes](https://github.com/Kotlin/kotlinx-datetime/releases) - [Changelog](https://github.com/Kotlin/kotlinx-datetime/blob/master/CHANGELOG.md) - [Commits](https://github.com/Kotlin/kotlinx-datetime/compare/v0.4.1...v0.5.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlinx:kotlinx-datetime 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 aa2a32e90..41c92c2f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,7 +44,7 @@ jacoco = "0.8.7" junit4 = "4.13.2" kotlin = "1.9.10" kotlinxCoroutines = "1.7.3" -kotlinxDatetime = "0.4.1" +kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" ksp = "1.9.10-1.0.13" lint = "31.1.3" From b75ddce6308dc71df28bae69d1da26761bc34152 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:10:38 +0000 Subject: [PATCH 24/70] Bump androidx.compose.runtime:runtime-tracing Bumps androidx.compose.runtime:runtime-tracing from 1.0.0-alpha03 to 1.0.0-beta01. --- updated-dependencies: - dependency-name: androidx.compose.runtime:runtime-tracing dependency-type: direct:production update-type: version-update:semver-patch ... 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 aa2a32e90..d265e2a73 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" androidxComposeBom = "2023.10.01" androidxComposeCompiler = "1.5.3" -androidxComposeRuntimeTracing = "1.0.0-alpha03" +androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.0.0" From 9a8bfd86eda6c3e8f5b93ddf37ff6b2ef6617ad9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:10:43 +0000 Subject: [PATCH 25/70] Bump androidxMacroBenchmark from 1.2.0 to 1.2.2 Bumps `androidxMacroBenchmark` from 1.2.0 to 1.2.2. Updates `androidx.benchmark:benchmark-macro-junit4` from 1.2.0 to 1.2.2 Updates `androidx.baselineprofile` from 1.2.0 to 1.2.2 --- updated-dependencies: - dependency-name: androidx.benchmark:benchmark-macro-junit4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: androidx.baselineprofile dependency-type: direct:production update-type: version-update:semver-patch ... 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 aa2a32e90..a82684d1a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ androidxEspresso = "3.5.1" androidxHiltNavigationCompose = "1.0.0" androidxJunit = "1.1.5" androidxLifecycle = "2.6.2" -androidxMacroBenchmark = "1.2.0" +androidxMacroBenchmark = "1.2.2" androidxMetrics = "1.0.0-alpha04" androidxNavigation = "2.7.4" androidxProfileinstaller = "1.3.1" From 07d987733108db806f6fecfc90341cc34e1859b6 Mon Sep 17 00:00:00 2001 From: Yuya <140081005+oikvpqya@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:59:00 +0900 Subject: [PATCH 26/70] Update thumb movement range by thumb size, in app scrollbars Change-Id: I4438debd5037965a837758e0c78b110ab8197da5 --- .../component/scrollbar/Scrollbar.kt | 33 ++++++++++++------- .../component/scrollbar/ThumbExt.kt | 3 +- 2 files changed, 23 insertions(+), 13 deletions(-) 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 5041eff7b..1e5850f30 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 @@ -91,6 +91,12 @@ class ScrollbarState { */ val thumbMovedPercent get() = unpackFloat2(packedValue) + + /** + * Returns the max distance the thumb can travel as a percentage of total track size + */ + val thumbTrackSizePercent + get() = 1f - unpackFloat1(packedValue) } /** @@ -310,27 +316,30 @@ fun Scrollbar( b = minThumbSize.toPx(), ) - val thumbTravelPercent = when { - interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent - else -> interactionThumbTravelPercent + val trackSizePx = when (state.thumbTrackSizePercent) { + 0f -> track.size + else -> (track.size - thumbSizePx) / state.thumbTrackSizePercent } - val thumbMovedPx = min( - a = track.size * thumbTravelPercent, - b = track.size - thumbSizePx, + val thumbTravelPercent = max( + a = min( + a = when { + interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent + else -> interactionThumbTravelPercent + }, + b = state.thumbTrackSizePercent, + ), + b = 0f, ) - val scrollbarThumbMovedPx = max( - a = thumbMovedPx.roundToInt(), - b = 0, - ) + val thumbMovedPx = trackSizePx * thumbTravelPercent val y = when (orientation) { Horizontal -> 0 - Vertical -> scrollbarThumbMovedPx + Vertical -> thumbMovedPx.roundToInt() } val x = when (orientation) { - Horizontal -> scrollbarThumbMovedPx + Horizontal -> thumbMovedPx.roundToInt() Vertical -> 0 } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt index 847580361..a267ec2ec 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ThumbExt.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import kotlin.math.roundToInt /** * Remembers a function to react to [Scrollbar] thumb position displacements for a [LazyListState] @@ -79,7 +80,7 @@ private inline fun rememberDraggableScroller( LaunchedEffect(percentage) { if (percentage.isNaN()) return@LaunchedEffect - val indexToFind = (itemCount * percentage).toInt() + val indexToFind = (itemCount * percentage).roundToInt() scroll(indexToFind) } return remember { From 8a992996a4a59c721d5257d8b8fe2661e552ebec Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Wed, 6 Dec 2023 14:56:38 -0800 Subject: [PATCH 27/70] Enable stricter validation for build-logic convention plugins Change-Id: I1955393c76e91bebfe4b7e349734c81a10263c8c --- .github/workflows/Build.yaml | 3 +++ build-logic/convention/build.gradle.kts | 7 +++++++ .../com/google/samples/apps/nowinandroid/Badging.kt | 9 +++++++++ .../google/samples/apps/nowinandroid/PrintTestApks.kt | 7 +++++++ 4 files changed, 26 insertions(+) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index ca161b0a4..d47342c6a 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -39,6 +39,9 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2 + - name: Check build-logic + run: ./gradlew check -p build-logic + - name: Check spotless run: ./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index b6650aa75..e75ed468f 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -43,6 +43,13 @@ dependencies { compileOnly(libs.ksp.gradlePlugin) } +tasks { + validatePlugins { + enableStricterValidation.set(true) + failOnWarning.set(true) + } +} + gradlePlugin { plugins { register("androidApplicationCompose") { diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt index bcaa00f9f..2a776e429 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt @@ -26,11 +26,14 @@ import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.configurationcache.extensions.capitalized import org.gradle.kotlin.dsl.register @@ -40,14 +43,17 @@ import java.io.File import java.nio.file.Files import javax.inject.Inject +@CacheableTask abstract class GenerateBadgingTask : DefaultTask() { @get:OutputFile abstract val badging: RegularFileProperty + @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val apk: RegularFileProperty + @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val aapt2Executable: RegularFileProperty @@ -68,6 +74,7 @@ abstract class GenerateBadgingTask : DefaultTask() { } } +@CacheableTask abstract class CheckBadgingTask : DefaultTask() { // In order for the task to be up-to-date when the inputs have not changed, @@ -76,9 +83,11 @@ abstract class CheckBadgingTask : DefaultTask() { @get:OutputDirectory abstract val output: DirectoryProperty + @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val goldenBadging: RegularFileProperty + @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val generatedBadging: RegularFileProperty diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt index 6c08216cc..94bf6e127 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt @@ -30,7 +30,10 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault import java.io.File internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtension<*, *, *>) { @@ -62,10 +65,14 @@ internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtensio } } +@DisableCachingByDefault(because = "Prints output") internal abstract class PrintApkLocationTask : DefaultTask() { + + @get:PathSensitive(PathSensitivity.RELATIVE) @get:InputDirectory abstract val apkFolder: DirectoryProperty + @get:PathSensitive(PathSensitivity.RELATIVE) @get:InputFiles abstract val sources: ListProperty From 46b0a8108097bb2cf949e19c5caf094e51eb8d4e Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Thu, 7 Dec 2023 12:19:06 -0800 Subject: [PATCH 28/70] Use normal propery setting Change-Id: I31aef2007b63c067ad80a9f65f2b9594d114f13a --- build-logic/convention/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index e75ed468f..ea57a7c8c 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { tasks { validatePlugins { - enableStricterValidation.set(true) - failOnWarning.set(true) + enableStricterValidation = true + failOnWarning = true } } From e8f4537e7c526a4a10e7e9cb9d1f31910c013179 Mon Sep 17 00:00:00 2001 From: Yuya <140081005+oikvpqya@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:11:28 +0900 Subject: [PATCH 29/70] Update thumbTrackSizePercent calculation for readability Change-Id: I7ef067a950b4b5911b8c2e6a8c1f72c3e95d938d --- .../core/designsystem/component/scrollbar/Scrollbar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1e5850f30..8c85e5be5 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 @@ -96,7 +96,7 @@ class ScrollbarState { * Returns the max distance the thumb can travel as a percentage of total track size */ val thumbTrackSizePercent - get() = 1f - unpackFloat1(packedValue) + get() = 1f - thumbSizePercent } /** From 5df9cce37cf8a5df494fe1772802d21ba6c13147 Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Fri, 8 Dec 2023 11:32:01 -0800 Subject: [PATCH 30/70] Add truth for better badging diff output Change-Id: Id3fab0107bc08adf44ddf07a30e0327e06897d47 --- build-logic/convention/build.gradle.kts | 1 + .../samples/apps/nowinandroid/Badging.kt | 20 +++++++------------ gradle/libs.versions.toml | 2 ++ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index ea57a7c8c..e11a1b42a 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { compileOnly(libs.firebase.performance.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.ksp.gradlePlugin) + implementation(libs.truth) } tasks { diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt index 2a776e429..c59d3ffb8 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt @@ -20,8 +20,8 @@ import com.android.build.api.artifact.SingleArtifact import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.gradle.BaseExtension import com.android.SdkConstants +import com.google.common.truth.Truth.assertWithMessage import org.gradle.api.DefaultTask -import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -40,7 +40,6 @@ import org.gradle.kotlin.dsl.register import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.process.ExecOperations import java.io.File -import java.nio.file.Files import javax.inject.Inject @CacheableTask @@ -98,17 +97,12 @@ abstract class CheckBadgingTask : DefaultTask() { @TaskAction fun taskAction() { - if ( - Files.mismatch( - goldenBadging.get().asFile.toPath(), - generatedBadging.get().asFile.toPath(), - ) != -1L - ) { - throw GradleException( - "Generated badging is different from golden badging! " + - "If this change is intended, run ./gradlew ${updateBadgingTaskName.get()}", - ) - } + assertWithMessage( + "Generated badging is different from golden badging! " + + "If this change is intended, run ./gradlew ${updateBadgingTaskName.get()}", + ) + .that(generatedBadging.get().asFile.readText()) + .isEqualTo(goldenBadging.get().asFile.readText()) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index aa2a32e90..4f55e6290 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,7 @@ robolectric = "4.11.1" roborazzi = "1.6.0" room = "2.6.0" secrets = "2.0.1" +truth = "1.1.5" turbine = "1.0.0" [libraries] @@ -140,6 +141,7 @@ roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", versi room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } # Dependencies of the included build-logic From 8d0a9bacd48e5a35f9124f2848dcc74dac59e470 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 10:25:22 +0100 Subject: [PATCH 31/70] Update max concurrent PRs for dependabot We currently have many pending updates that are not listed and the default 5 concurrent PRs is very limiting. See documentation on https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 00ada2a1c..d77a706b3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,7 @@ updates: - "org.jetbrains.kotlin.jvm" - "com.google.devtools.ksp" - "androidx.compose.compiler:compiler" + open-pull-requests-limit: 10 registries: maven-google: type: "maven-repository" From 02adbe0aada8da1448c1661e8c19743e29980afa Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 09:29:17 +0000 Subject: [PATCH 32/70] Remove unnecessary FlowPreview opt-in --- .../kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt | 1 - 1 file changed, 1 deletion(-) 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 04ee4e56e..d7cadf8d7 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 @@ -86,7 +86,6 @@ private fun Project.configureKotlin() { "-opt-in=kotlin.RequiresOptIn", // Enable experimental coroutines APIs, including Flow "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-opt-in=kotlinx.coroutines.FlowPreview", ) } } From e305b07271278eba15d7e3dc4bc4a184aa28cceb Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 09:32:00 +0000 Subject: [PATCH 33/70] Remove unnecessary RequiresOptIn opt-in --- .../kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt | 1 - 1 file changed, 1 deletion(-) 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 04ee4e56e..b453cf722 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 @@ -83,7 +83,6 @@ private fun Project.configureKotlin() { val warningsAsErrors: String? by project allWarningsAsErrors = warningsAsErrors.toBoolean() freeCompilerArgs = freeCompilerArgs + listOf( - "-opt-in=kotlin.RequiresOptIn", // Enable experimental coroutines APIs, including Flow "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", From 5f7d4ea1d57cf497ff830875ae6d2fa0fcc7f7ee Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 11:28:12 +0100 Subject: [PATCH 34/70] Replace custom `RoomSchemaArgProvider` in favor of the new Room Gradle plugin > Since Room 2.6.0, a Gradle plugin was released to solve various existing issues in Room regarding having inputs and outputs of schemas via Gradle annotation processor options. > Changelog: https://developer.android.com/jetpack/androidx/releases/room#2.6.0-alpha02 See https://github.com/gradle/android-cache-fix-gradle-plugin/issues/544 for reference. --- build-logic/convention/build.gradle.kts | 1 + .../kotlin/AndroidRoomConventionPlugin.kt | 24 ++++--------------- build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index b6650aa75..d9f825294 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { compileOnly(libs.firebase.performance.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.ksp.gradlePlugin) + compileOnly(libs.room.gradlePlugin) } gradlePlugin { diff --git a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt index b67fb1b26..29d31f9e6 100644 --- a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt @@ -14,29 +14,25 @@ * limitations under the License. */ -import com.google.devtools.ksp.gradle.KspExtension +import androidx.room.gradle.RoomExtension import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.process.CommandLineArgumentProvider -import java.io.File class AndroidRoomConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { + pluginManager.apply("androidx.room") pluginManager.apply("com.google.devtools.ksp") - extensions.configure { + extensions.configure { // The schemas directory contains a schema file for each version of the Room database. // This is required to enable Room auto migrations. // See https://developer.android.com/reference/kotlin/androidx/room/AutoMigration. - arg(RoomSchemaArgProvider(File(projectDir, "schemas"))) + schemaDirectory("$projectDir/schemas") } dependencies { @@ -46,16 +42,4 @@ class AndroidRoomConventionPlugin : Plugin { } } } - - /** - * https://issuetracker.google.com/issues/132245929 - * [Export schemas](https://developer.android.com/training/data-storage/room/migrating-db-versions#export-schemas) - */ - class RoomSchemaArgProvider( - @get:InputDirectory - @get:PathSensitive(PathSensitivity.RELATIVE) - val schemaDir: File, - ) : CommandLineArgumentProvider { - override fun asArguments() = listOf("room.schemaLocation=${schemaDir.path}") - } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7b54f9058..d6c076b06 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,4 +43,5 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.roborazzi) apply false alias(libs.plugins.secrets) apply false + alias(libs.plugins.room) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1eb9116e8..dc08a2fef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -148,6 +148,7 @@ firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "fir firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } +room-gradlePlugin = { group = "androidx.room", name = "room-gradle-plugin", version.ref = "room" } work-testing = { group = "androidx.work", name = "work-testing", version = "2.8.1" } [plugins] @@ -164,6 +165,7 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } 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" } # Plugins defined by this project From f27a2e176e539c4c9fb00ec941bec1aef426137f Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 17:42:45 +0100 Subject: [PATCH 35/70] Reduce overall build complexity - Reduce the visibility of multiple Hilt `Module`s and implementations of public interfaces - Correctly configure the visibility of dependencies: - `api` when it's part of it's public `api` - `implementation` when it's part of it's internal `implementation` - Remove unnecessary dependencies in build.gradle.kts files - Remove unnecessary dependencies provided by plugins - Remove unnecessary applied plugins (i.e. roborazzi) - Sort dependencies in `build.gradle.kts` - Delete unused entries in `libs.versions.toml` --- app-nia-catalog/build.gradle.kts | 3 +- app/build.gradle.kts | 43 ++++++++----------- ...droidApplicationComposeConventionPlugin.kt | 2 - .../kotlin/AndroidFeatureConventionPlugin.kt | 16 ------- .../kotlin/AndroidHiltConventionPlugin.kt | 2 - .../AndroidLibraryComposeConventionPlugin.kt | 2 - .../kotlin/AndroidLibraryConventionPlugin.kt | 3 -- .../apps/nowinandroid/AndroidCompose.kt | 5 --- build.gradle.kts | 1 + core/analytics/build.gradle.kts | 7 ++- .../core/analytics/AnalyticsModule.kt | 2 +- .../core/analytics/StubAnalyticsHelper.kt | 2 +- .../core/analytics/AnalyticsModule.kt | 2 +- .../core/analytics/FirebaseAnalyticsHelper.kt | 2 +- core/common/build.gradle.kts | 4 +- .../core/network/di/CoroutineScopesModule.kt | 2 +- core/data-test/build.gradle.kts | 4 +- core/data/build.gradle.kts | 16 +++---- .../nowinandroid/core/data/di/DataModule.kt | 14 +++--- .../di/UserNewsResourceRepositoryModule.kt | 2 +- .../data/repository/AnalyticsExtensions.kt | 12 +++--- .../DefaultRecentSearchRepository.kt | 2 +- .../DefaultSearchContentsRepository.kt | 2 +- .../repository/OfflineFirstNewsRepository.kt | 2 +- .../OfflineFirstTopicsRepository.kt | 2 +- .../OfflineFirstUserDataRepository.kt | 2 +- .../util/ConnectivityManagerNetworkMonitor.kt | 2 +- core/database/build.gradle.kts | 3 +- .../nowinandroid/core/database/DaosModule.kt | 2 +- .../core/database/DatabaseMigrations.kt | 2 +- .../core/database/DatabaseModule.kt | 2 +- .../nowinandroid/core/database/NiaDatabase.kt | 2 +- .../core/database/util/InstantConverter.kt | 2 +- core/datastore-proto/build.gradle.kts | 2 +- core/datastore-test/build.gradle.kts | 7 +-- .../datastore/test/TestDataStoreModule.kt | 2 +- core/datastore/build.gradle.kts | 9 ++-- .../core/datastore/IntToStringIdsMigration.kt | 2 +- .../core/datastore/ListToMapMigration.kt | 2 +- .../core/datastore/di/DataStoreModule.kt | 2 +- core/designsystem/build.gradle.kts | 10 ++++- core/domain/build.gradle.kts | 9 ++-- core/model/build.gradle.kts | 2 +- core/network/build.gradle.kts | 10 ++--- .../core/network/di/FlavoredNetworkModule.kt | 2 +- .../core/network/di/NetworkModule.kt | 2 +- .../network/retrofit/RetrofitNiaNetwork.kt | 2 +- .../core/network/di/FlavoredNetworkModule.kt | 2 +- core/notifications/build.gradle.kts | 13 ++---- .../core/notifications/NotificationsModule.kt | 2 +- .../core/notifications/NoOpNotifier.kt | 2 +- .../core/notifications/SystemTrayNotifier.kt | 2 +- .../core/notifications/NotificationsModule.kt | 2 +- core/testing/build.gradle.kts | 29 ++++++------- .../core/testing/di/TestDispatcherModule.kt | 2 +- .../core/testing/di/TestDispatchersModule.kt | 2 +- core/ui/build.gradle.kts | 21 ++------- feature/bookmarks/build.gradle.kts | 6 ++- feature/foryou/build.gradle.kts | 10 ++++- feature/interests/build.gradle.kts | 9 ++++ feature/search/build.gradle.kts | 7 ++- feature/settings/build.gradle.kts | 9 ++-- feature/topic/build.gradle.kts | 6 ++- gradle/libs.versions.toml | 15 +------ sync/sync-test/build.gradle.kts | 4 +- .../core/sync/test/NeverSyncingSyncManager.kt | 2 +- .../core/sync/test/TestSyncModule.kt | 2 +- sync/work/build.gradle.kts | 20 ++++----- .../apps/nowinandroid/sync/di/SyncModule.kt | 6 +-- .../sync/status/StubSyncSubscriber.kt | 2 +- .../sync/status/WorkManagerSyncManager.kt | 2 +- .../sync/workers/AnalyticsExtensions.kt | 4 +- .../nowinandroid/sync/workers/SyncWorker.kt | 2 +- .../apps/nowinandroid/sync/di/SyncModule.kt | 8 ++-- .../sync/services/SyncNotificationsService.kt | 2 +- .../sync/status/FirebaseSyncSubscriber.kt | 2 +- 76 files changed, 191 insertions(+), 238 deletions(-) diff --git a/app-nia-catalog/build.gradle.kts b/app-nia-catalog/build.gradle.kts index e02f6bc0b..0991ea0c5 100644 --- a/app-nia-catalog/build.gradle.kts +++ b/app-nia-catalog/build.gradle.kts @@ -65,9 +65,10 @@ android { } dependencies { + implementation(libs.androidx.activity.compose) + implementation(projects.core.designsystem) implementation(projects.core.ui) - implementation(libs.androidx.activity.compose) } dependencyGuard { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b003c4a54..38166f7ff 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,6 +25,7 @@ plugins { alias(libs.plugins.nowinandroid.android.application.firebase) id("com.google.android.gms.oss-licenses-plugin") alias(libs.plugins.baselineprofile) + alias(libs.plugins.roborazzi) } android { @@ -96,47 +97,41 @@ dependencies { implementation(projects.core.data) implementation(projects.core.model) implementation(projects.core.analytics) - implementation(projects.sync.work) - androidTestImplementation(projects.core.testing) - androidTestImplementation(projects.core.datastoreTest) - androidTestImplementation(projects.core.dataTest) - androidTestImplementation(projects.core.network) - androidTestImplementation(libs.androidx.navigation.testing) - androidTestImplementation(libs.accompanist.testharness) - androidTestImplementation(kotlin("test")) - debugImplementation(libs.androidx.compose.ui.testManifest) - debugImplementation(projects.uiTestHiltManifest) - implementation(libs.androidx.activity.compose) - implementation(libs.androidx.appcompat) implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.splashscreen) - implementation(libs.androidx.compose.runtime) + 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.hilt.navigation.compose) implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.window.manager) implementation(libs.androidx.profileinstaller) implementation(libs.kotlinx.coroutines.guava) implementation(libs.coil.kt) - baselineProfile(project(":benchmarks")) + debugImplementation(libs.androidx.compose.ui.testManifest) + debugImplementation(projects.uiTestHiltManifest) + + kspTest(libs.hilt.compiler) - // Core functions - testImplementation(projects.core.testing) - testImplementation(projects.core.datastoreTest) testImplementation(projects.core.dataTest) - testImplementation(projects.core.network) - testImplementation(libs.androidx.navigation.testing) + testImplementation(projects.core.testing) testImplementation(libs.accompanist.testharness) + testImplementation(libs.hilt.android.testing) testImplementation(libs.work.testing) - testImplementation(kotlin("test")) - kspTest(libs.hilt.compiler) + testDemoImplementation(libs.robolectric) + testDemoImplementation(libs.roborazzi) + + androidTestImplementation(projects.core.testing) + androidTestImplementation(projects.core.dataTest) + androidTestImplementation(projects.core.datastoreTest) + androidTestImplementation(libs.androidx.navigation.testing) + androidTestImplementation(libs.accompanist.testharness) + androidTestImplementation(libs.hilt.android.testing) + + baselineProfile(projects.benchmarks) } baselineProfile { diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt index bb79715e4..3eeed97cf 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt @@ -24,8 +24,6 @@ class AndroidApplicationComposeConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("com.android.application") - // Screenshot Tests - pluginManager.apply("io.github.takahirom.roborazzi") val extension = extensions.getByType() configureAndroidCompose(extension) diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index cc42d60fd..7a334beb3 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -21,7 +21,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.kotlin class AndroidFeatureConventionPlugin : Plugin { override fun apply(target: Project) { @@ -39,27 +38,12 @@ class AndroidFeatureConventionPlugin : Plugin { } dependencies { - add("implementation", project(":core:model")) add("implementation", project(":core:ui")) add("implementation", project(":core:designsystem")) - add("implementation", project(":core:data")) - add("implementation", project(":core:common")) - add("implementation", project(":core:domain")) - add("implementation", project(":core:analytics")) - - add("testImplementation", kotlin("test")) - add("testImplementation", project(":core:testing")) - add("androidTestImplementation", kotlin("test")) - add("androidTestImplementation", project(":core:testing")) - - add("implementation", libs.findLibrary("coil.kt").get()) - add("implementation", libs.findLibrary("coil.kt.compose").get()) 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("kotlinx.coroutines.android").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt index b24594570..fcb4f823e 100644 --- a/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt @@ -30,8 +30,6 @@ class AndroidHiltConventionPlugin : Plugin { dependencies { "implementation"(libs.findLibrary("hilt.android").get()) "ksp"(libs.findLibrary("hilt.compiler").get()) - "kspAndroidTest"(libs.findLibrary("hilt.compiler").get()) - "kspTest"(libs.findLibrary("hilt.compiler").get()) } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt index 05f442354..dd9eead63 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -26,8 +26,6 @@ class AndroidLibraryComposeConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("com.android.library") - // Screenshot Tests - pluginManager.apply("io.github.takahirom.roborazzi") val extension = extensions.getByType() configureAndroidCompose(extension) diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index ef84cfbb4..c4d4a8fc4 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -48,9 +48,6 @@ class AndroidLibraryConventionPlugin : Plugin { } dependencies { add("testImplementation", kotlin("test")) - add("testImplementation", project(":core:testing")) - add("androidTestImplementation", kotlin("test")) - add("androidTestImplementation", project(":core:testing")) } } } 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 614d4f2d0..039987cac 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,11 +41,6 @@ internal fun Project.configureAndroidCompose( val bom = libs.findLibrary("androidx-compose-bom").get() add("implementation", platform(bom)) add("androidTestImplementation", platform(bom)) - // Add ComponentActivity to debug manifest - add("debugImplementation", libs.findLibrary("androidx.compose.ui.testManifest").get()) - // Screenshot Tests on JVM - add("testImplementation", libs.findLibrary("robolectric").get()) - add("testImplementation", libs.findLibrary("roborazzi").get()) } testOptions { diff --git a/build.gradle.kts b/build.gradle.kts index a2c39f493..647c8f5d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,6 +32,7 @@ buildscript { // Lists all plugins used throughout the project without applying them. plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false alias(libs.plugins.android.test) apply false alias(libs.plugins.baselineprofile) apply false alias(libs.plugins.kotlin.jvm) apply false diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts index 0f712085c..023574e6f 100644 --- a/core/analytics/build.gradle.kts +++ b/core/analytics/build.gradle.kts @@ -24,9 +24,8 @@ android { } dependencies { - implementation(platform(libs.firebase.bom)) implementation(libs.androidx.compose.runtime) - implementation(libs.androidx.core.ktx) - implementation(libs.firebase.analytics) - implementation(libs.kotlinx.coroutines.android) + + prodImplementation(platform(libs.firebase.bom)) + prodImplementation(libs.firebase.analytics) } diff --git a/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt b/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt index 78ebec9e5..4ad6b6dc2 100644 --- a/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt +++ b/core/analytics/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/analytics/AnalyticsModule.kt @@ -23,7 +23,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -abstract class AnalyticsModule { +internal abstract class AnalyticsModule { @Binds abstract fun bindsAnalyticsHelper(analyticsHelperImpl: StubAnalyticsHelper): AnalyticsHelper } diff --git a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt b/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt index 2ff022287..f570be4a9 100644 --- a/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt +++ b/core/analytics/src/main/kotlin/com/google/samples/apps/nowinandroid/core/analytics/StubAnalyticsHelper.kt @@ -27,7 +27,7 @@ private const val TAG = "StubAnalyticsHelper" * analytics events should be sent to a backend. */ @Singleton -class StubAnalyticsHelper @Inject constructor() : AnalyticsHelper { +internal class StubAnalyticsHelper @Inject constructor() : AnalyticsHelper { override fun logEvent(event: AnalyticsEvent) { Log.d(TAG, "Received analytics event: $event") } 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 index 9f875ae6d..957b987d9 100644 --- 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 @@ -28,7 +28,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -abstract class AnalyticsModule { +internal abstract class AnalyticsModule { @Binds abstract fun bindsAnalyticsHelper(analyticsHelperImpl: FirebaseAnalyticsHelper): AnalyticsHelper 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 index 75dfbc468..5a4b7f362 100644 --- 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 @@ -23,7 +23,7 @@ import javax.inject.Inject /** * Implementation of `AnalyticsHelper` which logs events to a Firebase backend. */ -class FirebaseAnalyticsHelper @Inject constructor( +internal class FirebaseAnalyticsHelper @Inject constructor( private val firebaseAnalytics: FirebaseAnalytics, ) : AnalyticsHelper { diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index d539d1892..51ae627dc 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -24,6 +24,6 @@ android { } dependencies { - implementation(libs.kotlinx.coroutines.android) - testImplementation(projects.core.testing) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.turbine) } \ No newline at end of file diff --git a/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt b/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt index c265394a8..6e7ca6bb3 100644 --- a/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt +++ b/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt @@ -34,7 +34,7 @@ annotation class ApplicationScope @Module @InstallIn(SingletonComponent::class) -object CoroutineScopesModule { +internal object CoroutineScopesModule { @Provides @Singleton @ApplicationScope diff --git a/core/data-test/build.gradle.kts b/core/data-test/build.gradle.kts index 7ca3ecd0d..b166df288 100644 --- a/core/data-test/build.gradle.kts +++ b/core/data-test/build.gradle.kts @@ -24,6 +24,6 @@ android { dependencies { api(projects.core.data) - implementation(projects.core.testing) - implementation(projects.core.common) + + implementation(libs.hilt.android.testing) } diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index e730a9eb3..142637ff9 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -31,18 +31,16 @@ android { } dependencies { + api(projects.core.common) + api(projects.core.database) + api(projects.core.datastore) + api(projects.core.network) + implementation(projects.core.analytics) - implementation(projects.core.common) - implementation(projects.core.database) - implementation(projects.core.datastore) - implementation(projects.core.model) - implementation(projects.core.network) implementation(projects.core.notifications) - implementation(libs.androidx.core.ktx) - implementation(libs.kotlinx.coroutines.android) - implementation(libs.kotlinx.datetime) - implementation(libs.kotlinx.serialization.json) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.kotlinx.serialization.json) testImplementation(projects.core.datastoreTest) testImplementation(projects.core.testing) } 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 26f0bbc51..e135d7f58 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 @@ -35,35 +35,35 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -interface DataModule { +abstract class DataModule { @Binds - fun bindsTopicRepository( + internal abstract fun bindsTopicRepository( topicsRepository: OfflineFirstTopicsRepository, ): TopicsRepository @Binds - fun bindsNewsResourceRepository( + internal abstract fun bindsNewsResourceRepository( newsRepository: OfflineFirstNewsRepository, ): NewsRepository @Binds - fun bindsUserDataRepository( + internal abstract fun bindsUserDataRepository( userDataRepository: OfflineFirstUserDataRepository, ): UserDataRepository @Binds - fun bindsRecentSearchRepository( + internal abstract fun bindsRecentSearchRepository( recentSearchRepository: DefaultRecentSearchRepository, ): RecentSearchRepository @Binds - fun bindsSearchContentsRepository( + internal abstract fun bindsSearchContentsRepository( searchContentsRepository: DefaultSearchContentsRepository, ): SearchContentsRepository @Binds - fun bindsNetworkMonitor( + internal abstract fun bindsNetworkMonitor( networkMonitor: ConnectivityManagerNetworkMonitor, ): NetworkMonitor } diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt index 1a7a80fff..7f4e27b41 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt @@ -25,7 +25,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -interface UserNewsResourceRepositoryModule { +internal interface UserNewsResourceRepositoryModule { @Binds fun bindsUserNewsResourceRepository( userDataRepository: CompositeUserNewsResourceRepository, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt index d36f509d9..3d2f657dd 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt @@ -20,7 +20,7 @@ import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper -fun AnalyticsHelper.logNewsResourceBookmarkToggled(newsResourceId: String, isBookmarked: Boolean) { +internal fun AnalyticsHelper.logNewsResourceBookmarkToggled(newsResourceId: String, isBookmarked: Boolean) { val eventType = if (isBookmarked) "news_resource_saved" else "news_resource_unsaved" val paramKey = if (isBookmarked) "saved_news_resource_id" else "unsaved_news_resource_id" logEvent( @@ -33,7 +33,7 @@ fun AnalyticsHelper.logNewsResourceBookmarkToggled(newsResourceId: String, isBoo ) } -fun AnalyticsHelper.logTopicFollowToggled(followedTopicId: String, isFollowed: Boolean) { +internal fun AnalyticsHelper.logTopicFollowToggled(followedTopicId: String, isFollowed: Boolean) { val eventType = if (isFollowed) "topic_followed" else "topic_unfollowed" val paramKey = if (isFollowed) "followed_topic_id" else "unfollowed_topic_id" logEvent( @@ -46,7 +46,7 @@ fun AnalyticsHelper.logTopicFollowToggled(followedTopicId: String, isFollowed: B ) } -fun AnalyticsHelper.logThemeChanged(themeName: String) = +internal fun AnalyticsHelper.logThemeChanged(themeName: String) = logEvent( AnalyticsEvent( type = "theme_changed", @@ -56,7 +56,7 @@ fun AnalyticsHelper.logThemeChanged(themeName: String) = ), ) -fun AnalyticsHelper.logDarkThemeConfigChanged(darkThemeConfigName: String) = +internal fun AnalyticsHelper.logDarkThemeConfigChanged(darkThemeConfigName: String) = logEvent( AnalyticsEvent( type = "dark_theme_config_changed", @@ -66,7 +66,7 @@ fun AnalyticsHelper.logDarkThemeConfigChanged(darkThemeConfigName: String) = ), ) -fun AnalyticsHelper.logDynamicColorPreferenceChanged(useDynamicColor: Boolean) = +internal fun AnalyticsHelper.logDynamicColorPreferenceChanged(useDynamicColor: Boolean) = logEvent( AnalyticsEvent( type = "dynamic_color_preference_changed", @@ -76,7 +76,7 @@ fun AnalyticsHelper.logDynamicColorPreferenceChanged(useDynamicColor: Boolean) = ), ) -fun AnalyticsHelper.logOnboardingStateChanged(shouldHideOnboarding: Boolean) { +internal fun AnalyticsHelper.logOnboardingStateChanged(shouldHideOnboarding: Boolean) { val eventType = if (shouldHideOnboarding) "onboarding_complete" else "onboarding_reset" logEvent( AnalyticsEvent(type = eventType), diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt index 702c2dcd2..2e5c4463b 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.map import kotlinx.datetime.Clock import javax.inject.Inject -class DefaultRecentSearchRepository @Inject constructor( +internal class DefaultRecentSearchRepository @Inject constructor( 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/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt index dc3caa143..3bacb8a14 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt @@ -36,7 +36,7 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext import javax.inject.Inject -class DefaultSearchContentsRepository @Inject constructor( +internal class DefaultSearchContentsRepository @Inject constructor( private val newsResourceDao: NewsResourceDao, private val newsResourceFtsDao: NewsResourceFtsDao, private val topicDao: TopicDao, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt index ce395ad1c..d33c904e5 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt @@ -45,7 +45,7 @@ private const val SYNC_BATCH_SIZE = 40 * Disk storage backed implementation of the [NewsRepository]. * Reads are exclusively from local storage to support offline access. */ -class OfflineFirstNewsRepository @Inject constructor( +internal class OfflineFirstNewsRepository @Inject constructor( 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/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt index 13dd19e49..5c8cecce8 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt @@ -34,7 +34,7 @@ import javax.inject.Inject * Disk storage backed implementation of the [TopicsRepository]. * Reads are exclusively from local storage to support offline access. */ -class OfflineFirstTopicsRepository @Inject constructor( +internal class OfflineFirstTopicsRepository @Inject constructor( 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/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt index a6e41c9da..c0b1bcc33 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt @@ -25,7 +25,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserData import kotlinx.coroutines.flow.Flow import javax.inject.Inject -class OfflineFirstUserDataRepository @Inject constructor( +internal class OfflineFirstUserDataRepository @Inject constructor( private val niaPreferencesDataSource: NiaPreferencesDataSource, private val analyticsHelper: AnalyticsHelper, ) : UserDataRepository { diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt index c88125be8..e9599c555 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import javax.inject.Inject -class ConnectivityManagerNetworkMonitor @Inject constructor( +internal class ConnectivityManagerNetworkMonitor @Inject constructor( @ApplicationContext private val context: Context, ) : NetworkMonitor { override val isOnline: Flow = callbackFlow { diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index a1075286d..4a6bcb66a 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -30,9 +30,8 @@ android { } dependencies { - implementation(projects.core.model) + api(projects.core.model) - implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.datetime) androidTestImplementation(projects.core.testing) diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt index 34840a733..afbbf42e6 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt @@ -28,7 +28,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -object DaosModule { +internal object DaosModule { @Provides fun providesTopicsDao( database: NiaDatabase, diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt index 09e0849fe..4e396c9e2 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt @@ -28,7 +28,7 @@ import androidx.room.migration.AutoMigrationSpec * from and Y is the schema version you're migrating to. The class should implement * `AutoMigrationSpec`. */ -object DatabaseMigrations { +internal object DatabaseMigrations { @RenameColumn( tableName = "topics", diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index 7d89cd1ac..c6e33f284 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -27,7 +27,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object DatabaseModule { +internal object DatabaseModule { @Provides @Singleton fun providesNiaDatabase( diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt index fd6b75e49..87fd82af1 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt @@ -63,7 +63,7 @@ import com.google.samples.apps.nowinandroid.core.database.util.InstantConverter @TypeConverters( InstantConverter::class, ) -abstract class NiaDatabase : RoomDatabase() { +internal abstract class NiaDatabase : RoomDatabase() { abstract fun topicDao(): TopicDao abstract fun newsResourceDao(): NewsResourceDao abstract fun topicFtsDao(): TopicFtsDao diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt index 4e880886c..0b79c2099 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt @@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.util import androidx.room.TypeConverter import kotlinx.datetime.Instant -class InstantConverter { +internal class InstantConverter { @TypeConverter fun longToInstant(value: Long?): Instant? = value?.let(Instant::fromEpochMilliseconds) diff --git a/core/datastore-proto/build.gradle.kts b/core/datastore-proto/build.gradle.kts index 157b5336e..511518dde 100644 --- a/core/datastore-proto/build.gradle.kts +++ b/core/datastore-proto/build.gradle.kts @@ -51,5 +51,5 @@ androidComponents.beforeVariants { } dependencies { - implementation(libs.protobuf.kotlin.lite) + api(libs.protobuf.kotlin.lite) } diff --git a/core/datastore-test/build.gradle.kts b/core/datastore-test/build.gradle.kts index 04b15f044..53e5e2c0c 100644 --- a/core/datastore-test/build.gradle.kts +++ b/core/datastore-test/build.gradle.kts @@ -23,10 +23,7 @@ android { } dependencies { - api(projects.core.datastore) - api(libs.androidx.dataStore.core) - - implementation(libs.protobuf.kotlin.lite) + implementation(libs.hilt.android.testing) implementation(projects.core.common) - implementation(projects.core.testing) + implementation(projects.core.datastore) } diff --git a/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt b/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt index b86003e83..295b2978a 100644 --- a/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt +++ b/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt @@ -35,7 +35,7 @@ import javax.inject.Singleton components = [SingletonComponent::class], replaces = [DataStoreModule::class], ) -object TestDataStoreModule { +internal object TestDataStoreModule { @Provides @Singleton diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index afe036640..34ea5ee78 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -33,13 +33,12 @@ android { } dependencies { + api(libs.androidx.dataStore.core) api(projects.core.datastoreProto) + api(projects.core.model) + implementation(projects.core.common) - implementation(projects.core.model) - implementation(libs.androidx.dataStore.core) - implementation(libs.kotlinx.coroutines.android) - implementation(libs.protobuf.kotlin.lite) testImplementation(projects.core.datastoreTest) - testImplementation(projects.core.testing) + testImplementation(libs.kotlinx.coroutines.test) } diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt index 98632c652..ef9c1dd03 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt @@ -21,7 +21,7 @@ import androidx.datastore.core.DataMigration /** * Migrates saved ids from [Int] to [String] types */ -object IntToStringIdsMigration : DataMigration { +internal object IntToStringIdsMigration : DataMigration { override suspend fun cleanUp() = Unit diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt index 5bff23340..faa491e74 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt @@ -21,7 +21,7 @@ import androidx.datastore.core.DataMigration /** * Migrates from using lists to maps for user data. */ -object ListToMapMigration : DataMigration { +internal object ListToMapMigration : DataMigration { override suspend fun cleanUp() = Unit diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt index ac9eaf767..8e0d7d4d8 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt @@ -41,7 +41,7 @@ object DataStoreModule { @Provides @Singleton - fun providesUserPreferencesDataStore( + internal fun providesUserPreferencesDataStore( @ApplicationContext context: Context, @Dispatcher(IO) ioDispatcher: CoroutineDispatcher, @ApplicationScope scope: CoroutineScope, diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 7bd1d12d8..d68117d06 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -17,6 +17,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) + alias(libs.plugins.roborazzi) } android { @@ -39,8 +40,15 @@ dependencies { debugApi(libs.androidx.compose.ui.tooling) - implementation(libs.androidx.core.ktx) implementation(libs.coil.kt.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.testing) + + androidTestImplementation(libs.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts index b81d62102..191877459 100644 --- a/core/domain/build.gradle.kts +++ b/core/domain/build.gradle.kts @@ -24,13 +24,10 @@ android { } dependencies { - implementation(projects.core.data) - implementation(projects.core.model) - implementation(libs.hilt.android) - implementation(libs.kotlinx.coroutines.android) - implementation(libs.kotlinx.datetime) + api(projects.core.data) + api(projects.core.model) - ksp(libs.hilt.compiler) + implementation(libs.javax.inject) testImplementation(projects.core.testing) } \ No newline at end of file diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 393e3aa7d..5d6cabfdf 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -19,5 +19,5 @@ plugins { } dependencies { - implementation(libs.kotlinx.datetime) + api(libs.kotlinx.datetime) } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index dce97031f..689a99e73 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -39,16 +39,16 @@ secrets { } dependencies { - implementation(projects.core.common) - implementation(projects.core.model) + api(libs.kotlinx.datetime) + api(projects.core.common) + api(projects.core.model) + implementation(libs.coil.kt) implementation(libs.coil.kt.svg) - implementation(libs.kotlinx.coroutines.android) - implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.json) implementation(libs.okhttp.logging) implementation(libs.retrofit.core) implementation(libs.retrofit.kotlin.serialization) - testImplementation(projects.core.testing) + testImplementation(libs.kotlinx.coroutines.test) } diff --git a/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt b/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt index a6162a9cc..850b00201 100644 --- a/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt +++ b/core/network/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt @@ -25,7 +25,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -interface FlavoredNetworkModule { +internal interface FlavoredNetworkModule { @Binds fun binds(impl: FakeNiaNetworkDataSource): NiaNetworkDataSource diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index 98534ba93..21d93c0e4 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -35,7 +35,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object NetworkModule { +internal object NetworkModule { @Provides @Singleton diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index b86036ff0..321e856fe 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -71,7 +71,7 @@ private data class NetworkResponse( * [Retrofit] backed [NiaNetworkDataSource] */ @Singleton -class RetrofitNiaNetwork @Inject constructor( +internal class RetrofitNiaNetwork @Inject constructor( networkJson: Json, okhttpCallFactory: Call.Factory, ) : NiaNetworkDataSource { 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 index 51a8a6f19..bff1ca5be 100644 --- 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 @@ -25,7 +25,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -interface FlavoredNetworkModule { +internal interface FlavoredNetworkModule { @Binds fun binds(impl: RetrofitNiaNetwork): NiaNetworkDataSource diff --git a/core/notifications/build.gradle.kts b/core/notifications/build.gradle.kts index 31b15a805..92871b72b 100644 --- a/core/notifications/build.gradle.kts +++ b/core/notifications/build.gradle.kts @@ -15,7 +15,6 @@ */ plugins { alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.compose) alias(libs.plugins.nowinandroid.android.hilt) } @@ -24,14 +23,10 @@ android { } dependencies { - implementation(projects.core.common) - implementation(projects.core.model) + api(projects.core.model) - implementation(libs.kotlinx.coroutines.android) - implementation(libs.androidx.browser) - implementation(libs.androidx.compose.runtime) - implementation(libs.androidx.core.ktx) + implementation(projects.core.common) - implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.cloud.messaging) + compileOnly(platform(libs.androidx.compose.bom)) + compileOnly(libs.androidx.compose.runtime) } diff --git a/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt b/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt index 9bb2b3fb9..99ba10fa7 100644 --- a/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt +++ b/core/notifications/src/demo/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NotificationsModule.kt @@ -23,7 +23,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -abstract class NotificationsModule { +internal abstract class NotificationsModule { @Binds abstract fun bindNotifier( notifier: NoOpNotifier, diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt index d17005bca..863c1a662 100644 --- a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt +++ b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/NoOpNotifier.kt @@ -22,6 +22,6 @@ import javax.inject.Inject /** * Implementation of [Notifier] which does nothing. Useful for tests and previews. */ -class NoOpNotifier @Inject constructor() : Notifier { +internal class NoOpNotifier @Inject constructor() : Notifier { override fun postNewsNotifications(newsResources: List) = Unit } diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt index 7e74f819c..73c47879f 100644 --- a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt +++ b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt @@ -50,7 +50,7 @@ private const val FOR_YOU_PATH = "foryou" * Implementation of [Notifier] that displays notifications in the system tray. */ @Singleton -class SystemTrayNotifier @Inject constructor( +internal class SystemTrayNotifier @Inject constructor( @ApplicationContext private val context: Context, ) : Notifier { 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 index 3c05e9c6e..c2e1f76ca 100644 --- 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 @@ -23,7 +23,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -abstract class NotificationsModule { +internal abstract class NotificationsModule { @Binds abstract fun bindNotifier( notifier: SystemTrayNotifier, diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts index 8ad91a0d5..275555d80 100644 --- a/core/testing/build.gradle.kts +++ b/core/testing/build.gradle.kts @@ -24,28 +24,23 @@ android { } dependencies { - api(libs.accompanist.testharness) - api(libs.androidx.activity.compose) + api(kotlin("test")) api(libs.androidx.compose.ui.test) - api(libs.androidx.test.core) - api(libs.androidx.test.espresso.core) - api(libs.androidx.test.rules) - api(libs.androidx.test.runner) - api(libs.hilt.android.testing) - api(libs.junit4) - api(libs.kotlinx.coroutines.test) api(libs.roborazzi) - api(libs.robolectric.shadows) - api(libs.turbine) + api(projects.core.analytics) + api(projects.core.data) + api(projects.core.model) + api(projects.core.notifications) 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.data) implementation(projects.core.designsystem) - implementation(projects.core.domain) - implementation(projects.core.model) - implementation(projects.core.notifications) - implementation(projects.core.analytics) - implementation(libs.kotlinx.datetime) } diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt index 66d52dabe..09c739243 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatcherModule.kt @@ -26,7 +26,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object TestDispatcherModule { +internal object TestDispatcherModule { @Provides @Singleton fun providesTestDispatcher(): TestDispatcher = UnconfinedTestDispatcher() diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt index f2134105a..4f5d15be1 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/di/TestDispatchersModule.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.test.TestDispatcher components = [SingletonComponent::class], replaces = [DispatchersModule::class], ) -object TestDispatchersModule { +internal object TestDispatchersModule { @Provides @Dispatcher(IO) fun providesIODispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index c9527d09e..5d8a65d44 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -27,29 +27,14 @@ android { } dependencies { - 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.runtime.livedata) - api(libs.androidx.compose.ui.tooling.preview) - api(libs.androidx.compose.ui.util) api(libs.androidx.metrics) - api(libs.androidx.tracing.ktx) + api(projects.core.analytics) + api(projects.core.designsystem) + api(projects.core.model) - debugApi(libs.androidx.compose.ui.tooling) - - implementation(projects.core.analytics) - implementation(projects.core.designsystem) - implementation(projects.core.domain) - implementation(projects.core.model) - implementation(libs.androidx.activity.compose) implementation(libs.androidx.browser) - implementation(libs.androidx.core.ktx) implementation(libs.coil.kt) implementation(libs.coil.kt.compose) - implementation(libs.kotlinx.datetime) androidTestImplementation(projects.core.testing) } diff --git a/feature/bookmarks/build.gradle.kts b/feature/bookmarks/build.gradle.kts index 32394f911..4e97176a2 100644 --- a/feature/bookmarks/build.gradle.kts +++ b/feature/bookmarks/build.gradle.kts @@ -25,5 +25,9 @@ android { } dependencies { - implementation(libs.androidx.compose.material3.windowSizeClass) + implementation(projects.core.data) + + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) } diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index afe769ab8..3f990b902 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -25,7 +25,13 @@ android { } dependencies { - implementation(libs.kotlinx.datetime) - implementation(libs.androidx.activity.compose) implementation(libs.accompanist.permissions) + implementation(projects.core.data) + implementation(projects.core.domain) + + testImplementation(libs.hilt.android.testing) + testImplementation(libs.robolectric) + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) } diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index 20b1ef1aa..f0bca9729 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -22,3 +22,12 @@ plugins { android { namespace = "com.google.samples.apps.nowinandroid.feature.interests" } + +dependencies { + implementation(projects.core.data) + implementation(projects.core.domain) + + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) +} \ No newline at end of file diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index d96f290e3..206f4c0f9 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -25,9 +25,14 @@ android { } dependencies { + implementation(projects.core.data) + implementation(projects.core.domain) implementation(projects.feature.bookmarks) implementation(projects.feature.foryou) implementation(projects.feature.interests) - implementation(libs.kotlinx.datetime) + + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) } diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 4f5d649b7..4b9a72bdd 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -26,7 +26,10 @@ android { dependencies { implementation(libs.androidx.appcompat) - implementation(libs.google.oss.licenses) { - exclude(group = "androidx.appcompat") - } + implementation(libs.google.oss.licenses) + implementation(projects.core.data) + + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) } diff --git a/feature/topic/build.gradle.kts b/feature/topic/build.gradle.kts index cc0ecc868..d457b2f73 100644 --- a/feature/topic/build.gradle.kts +++ b/feature/topic/build.gradle.kts @@ -25,5 +25,9 @@ android { } dependencies { - implementation(libs.kotlinx.datetime) + implementation(projects.core.data) + + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index aa2a32e90..67b267cdc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,26 +9,22 @@ androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" androidxComposeBom = "2023.10.01" androidxComposeCompiler = "1.5.3" -androidxComposeRuntimeTracing = "1.0.0-alpha03" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.0.0" androidxEspresso = "3.5.1" androidxHiltNavigationCompose = "1.0.0" -androidxJunit = "1.1.5" androidxLifecycle = "2.6.2" androidxMacroBenchmark = "1.2.0" androidxMetrics = "1.0.0-alpha04" androidxNavigation = "2.7.4" androidxProfileinstaller = "1.3.1" -androidxStartup = "1.1.1" androidxTestCore = "1.5.0" androidxTestExt = "1.1.5" androidxTestRules = "1.5.0" androidxTestRunner = "1.5.2" androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" -androidxWindowManager = "1.1.0" androidxWork = "2.9.0-rc01" coil = "2.4.0" dependencyGuard = "0.4.3" @@ -41,7 +37,6 @@ googleOssPlugin = "0.10.6" hilt = "2.48.1" hiltExt = "1.1.0" jacoco = "0.8.7" -junit4 = "4.13.2" kotlin = "1.9.10" kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.4.1" @@ -68,15 +63,12 @@ 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" } 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-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" } -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" } @@ -86,7 +78,6 @@ androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } -androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" } androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } @@ -100,7 +91,6 @@ androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = " androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" } androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxUiAutomator" } androidx-tracing-ktx = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "androidxTracing" } -androidx-window-manager = { module = "androidx.window:window", version.ref = "androidxWindowManager" } 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" } @@ -111,7 +101,6 @@ firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.r firebase-cloud-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" } firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } firebase-performance = { group = "com.google.firebase", name = "firebase-perf-ktx" } -firebase-performance-gradle = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" } 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" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } @@ -119,9 +108,8 @@ hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testi hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } hilt-ext-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltExt" } hilt-ext-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltExt" } -junit4 = { group = "junit", name = "junit", version.ref = "junit4" } +javax-inject = { module = "javax.inject:javax.inject", version = "1" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } -kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } @@ -145,7 +133,6 @@ turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" } firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "firebaseCrashlyticsPlugin" } firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } diff --git a/sync/sync-test/build.gradle.kts b/sync/sync-test/build.gradle.kts index 02e573ae5..0fac2cf83 100644 --- a/sync/sync-test/build.gradle.kts +++ b/sync/sync-test/build.gradle.kts @@ -23,7 +23,7 @@ android { } dependencies { - api(projects.sync.work) + implementation(libs.hilt.android.testing) implementation(projects.core.data) - implementation(projects.core.testing) + implementation(projects.sync.work) } diff --git a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt index 2b0b4fb6a..c13b409e6 100644 --- a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt +++ b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject -class NeverSyncingSyncManager @Inject constructor() : SyncManager { +internal class NeverSyncingSyncManager @Inject constructor() : SyncManager { override val isSyncing: Flow = flowOf(false) override fun requestSync() = Unit } diff --git a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt index 0089450b5..d1c0f562f 100644 --- a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt +++ b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt @@ -28,7 +28,7 @@ import dagger.hilt.testing.TestInstallIn components = [SingletonComponent::class], replaces = [SyncModule::class], ) -interface TestSyncModule { +internal interface TestSyncModule { @Binds fun bindsSyncStatusMonitor( syncStatusMonitor: NeverSyncingSyncManager, diff --git a/sync/work/build.gradle.kts b/sync/work/build.gradle.kts index 867aef17c..7e61c7389 100644 --- a/sync/work/build.gradle.kts +++ b/sync/work/build.gradle.kts @@ -27,23 +27,19 @@ android { } dependencies { - implementation(projects.core.analytics) - implementation(projects.core.common) - implementation(projects.core.data) - implementation(projects.core.datastore) - implementation(projects.core.model) - implementation(libs.androidx.lifecycle.livedata.ktx) + ksp(libs.hilt.ext.compiler) + implementation(libs.androidx.tracing.ktx) implementation(libs.androidx.work.ktx) implementation(libs.hilt.ext.work) - implementation(libs.kotlinx.coroutines.android) + implementation(projects.core.analytics) + implementation(projects.core.data) prodImplementation(libs.firebase.cloud.messaging) + prodImplementation(platform(libs.firebase.bom)) - ksp(libs.hilt.ext.compiler) - - testImplementation(projects.core.testing) - - androidTestImplementation(projects.core.testing) androidTestImplementation(libs.androidx.work.testing) + androidTestImplementation(libs.hilt.android.testing) + androidTestImplementation(libs.kotlinx.coroutines.guava) + androidTestImplementation(projects.core.testing) } diff --git a/sync/work/src/demo/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt b/sync/work/src/demo/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt index 40d094cd2..91ef476f6 100644 --- a/sync/work/src/demo/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt +++ b/sync/work/src/demo/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt @@ -27,14 +27,14 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -interface SyncModule { +abstract class SyncModule { @Binds - fun bindsSyncStatusMonitor( + internal abstract fun bindsSyncStatusMonitor( syncStatusMonitor: WorkManagerSyncManager, ): SyncManager @Binds - fun bindsSyncSubscriber( + internal abstract fun bindsSyncSubscriber( syncSubscriber: StubSyncSubscriber, ): SyncSubscriber } diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/StubSyncSubscriber.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/StubSyncSubscriber.kt index 0ef90fb29..83286eeec 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/StubSyncSubscriber.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/StubSyncSubscriber.kt @@ -24,7 +24,7 @@ private const val TAG = "StubSyncSubscriber" /** * Stub implementation of [SyncSubscriber] */ -class StubSyncSubscriber @Inject constructor() : SyncSubscriber { +internal class StubSyncSubscriber @Inject constructor() : SyncSubscriber { override suspend fun subscribe() { Log.d(TAG, "Subscribing to sync") } diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt index 9131e4888..77a993736 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt @@ -33,7 +33,7 @@ import javax.inject.Inject /** * [SyncManager] backed by [WorkInfo] from [WorkManager] */ -class WorkManagerSyncManager @Inject constructor( +internal class WorkManagerSyncManager @Inject constructor( @ApplicationContext private val context: Context, ) : SyncManager { override val isSyncing: Flow = diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/AnalyticsExtensions.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/AnalyticsExtensions.kt index d5250b330..f2d9283c0 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/AnalyticsExtensions.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/AnalyticsExtensions.kt @@ -19,12 +19,12 @@ package com.google.samples.apps.nowinandroid.sync.workers import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper -fun AnalyticsHelper.logSyncStarted() = +internal fun AnalyticsHelper.logSyncStarted() = logEvent( AnalyticsEvent(type = "network_sync_started"), ) -fun AnalyticsHelper.logSyncFinished(syncedSuccessfully: Boolean) { +internal fun AnalyticsHelper.logSyncFinished(syncedSuccessfully: Boolean) { val eventType = if (syncedSuccessfully) "network_sync_successful" else "network_sync_failed" logEvent( AnalyticsEvent(type = eventType), diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt index 1948b49a3..ea5f36042 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.withContext * sync functionality. */ @HiltWorker -class SyncWorker @AssistedInject constructor( +internal class SyncWorker @AssistedInject constructor( @Assisted private val appContext: Context, @Assisted workerParams: WorkerParameters, private val niaPreferences: NiaPreferencesDataSource, diff --git a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt index af4508406..df6db419b 100644 --- a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt +++ b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/di/SyncModule.kt @@ -32,20 +32,20 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -interface SyncModule { +abstract class SyncModule { @Binds - fun bindsSyncStatusMonitor( + internal abstract fun bindsSyncStatusMonitor( syncStatusMonitor: WorkManagerSyncManager, ): SyncManager @Binds - fun bindsSyncSubscriber( + internal abstract fun bindsSyncSubscriber( syncSubscriber: FirebaseSyncSubscriber, ): SyncSubscriber companion object { @Provides @Singleton - fun provideFirebaseMessaging(): FirebaseMessaging = Firebase.messaging + internal fun provideFirebaseMessaging(): FirebaseMessaging = Firebase.messaging } } diff --git a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/services/SyncNotificationsService.kt b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/services/SyncNotificationsService.kt index e51e30164..c7297dd1a 100644 --- a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/services/SyncNotificationsService.kt +++ b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/services/SyncNotificationsService.kt @@ -25,7 +25,7 @@ import javax.inject.Inject private const val SYNC_TOPIC_SENDER = "/topics/sync" @AndroidEntryPoint -class SyncNotificationsService : FirebaseMessagingService() { +internal class SyncNotificationsService : FirebaseMessagingService() { @Inject lateinit var syncManager: SyncManager diff --git a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/status/FirebaseSyncSubscriber.kt b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/status/FirebaseSyncSubscriber.kt index c2405bccc..2c48488e6 100644 --- a/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/status/FirebaseSyncSubscriber.kt +++ b/sync/work/src/prod/kotlin/com/google/samples/apps/nowinandroid/sync/status/FirebaseSyncSubscriber.kt @@ -24,7 +24,7 @@ import javax.inject.Inject /** * Implementation of [SyncSubscriber] that subscribes to the FCM [SYNC_TOPIC] */ -class FirebaseSyncSubscriber @Inject constructor( +internal class FirebaseSyncSubscriber @Inject constructor( private val firebaseMessaging: FirebaseMessaging, ) : SyncSubscriber { override suspend fun subscribe() { From 65a8b0d3b549dbed9719561d239f3fccd33ac45a Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 20:45:33 +0100 Subject: [PATCH 36/70] dependencyGuardBaseline --- .../dependencies/releaseRuntimeClasspath.txt | 87 +++---------------- .../prodReleaseRuntimeClasspath.txt | 13 +-- 2 files changed, 14 insertions(+), 86 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 786b15d57..7db29f30c 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -26,7 +26,6 @@ androidx.compose.material:material-icons-extended:1.5.4 androidx.compose.material:material-ripple-android:1.5.4 androidx.compose.material:material-ripple:1.5.4 androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-livedata:1.5.4 androidx.compose.runtime:runtime-saveable-android:1.5.4 androidx.compose.runtime:runtime-saveable:1.5.4 androidx.compose.runtime:runtime:1.5.4 @@ -46,18 +45,14 @@ androidx.compose.ui:ui-util:1.5.4 androidx.compose.ui:ui:1.5.4 androidx.compose:compose-bom:2023.10.01 androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.12.0 -androidx.core:core:1.12.0 +androidx.core:core-ktx:1.10.0 +androidx.core:core:1.10.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.0.0 -androidx.datastore:datastore-core:1.0.0 -androidx.datastore:datastore:1.0.0 -androidx.documentfile:documentfile:1.0.0 androidx.emoji2:emoji2:1.4.0 androidx.exifinterface:exifinterface:1.3.6 androidx.fragment:fragment:1.5.1 androidx.interpolator:interpolator:1.0.0 -androidx.legacy:legacy-support-core-utils:1.0.0 androidx.lifecycle:lifecycle-common-java8:2.6.1 androidx.lifecycle:lifecycle-common:2.6.1 androidx.lifecycle:lifecycle-livedata-core:2.6.1 @@ -69,99 +64,39 @@ androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 androidx.lifecycle:lifecycle-viewmodel:2.6.1 androidx.loader:loader:1.0.0 -androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.metrics:metrics-performance:1.0.0-alpha04 -androidx.print:print:1.0.0 -androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 -androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 androidx.profileinstaller:profileinstaller:1.3.1 -androidx.room:room-common:2.6.0 -androidx.room:room-ktx:2.6.0 -androidx.room:room-runtime:2.6.0 androidx.savedstate:savedstate-ktx:1.2.1 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:1.0.0 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 -com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-drawablepainter:0.30.1 -com.google.android.datatransport:transport-api:3.0.0 -com.google.android.datatransport:transport-backend-cct:3.1.8 -com.google.android.datatransport:transport-runtime:3.1.8 -com.google.android.gms:play-services-ads-identifier:18.0.0 -com.google.android.gms:play-services-base:18.0.1 -com.google.android.gms:play-services-basement:18.1.0 -com.google.android.gms:play-services-cloud-messaging:17.0.1 -com.google.android.gms:play-services-measurement-api:21.4.0 -com.google.android.gms:play-services-measurement-base:21.4.0 -com.google.android.gms:play-services-measurement-impl:21.4.0 -com.google.android.gms:play-services-measurement-sdk-api:21.4.0 -com.google.android.gms:play-services-measurement-sdk:21.4.0 -com.google.android.gms:play-services-measurement:21.4.0 -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.48.1 com.google.dagger:dagger:2.48.1 com.google.dagger:hilt-android:2.48.1 com.google.dagger:hilt-core:2.48.1 -com.google.errorprone:error_prone_annotations:2.11.0 -com.google.firebase:firebase-analytics-ktx:21.4.0 -com.google.firebase:firebase-analytics:21.4.0 -com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:32.4.0 -com.google.firebase:firebase-common-ktx:20.4.2 -com.google.firebase:firebase-common:20.4.2 -com.google.firebase:firebase-components:17.1.5 -com.google.firebase:firebase-datatransport:18.1.7 -com.google.firebase:firebase-encoders-json:18.0.0 -com.google.firebase:firebase-encoders-proto:16.0.0 -com.google.firebase:firebase-encoders:17.0.0 -com.google.firebase:firebase-iid-interop:17.1.0 -com.google.firebase:firebase-installations-interop:17.1.1 -com.google.firebase:firebase-installations:17.2.0 -com.google.firebase:firebase-measurement-connector:19.0.0 -com.google.firebase:firebase-messaging-ktx:23.3.0 -com.google.firebase:firebase-messaging:23.3.0 -com.google.guava:failureaccess:1.0.1 -com.google.guava:guava:31.1-android -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava -com.google.j2objc:j2objc-annotations:1.3 -com.google.protobuf:protobuf-javalite:3.24.4 -com.google.protobuf:protobuf-kotlin-lite:3.24.4 -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.retrofit2:retrofit:2.9.0 +com.google.guava:listenablefuture:1.0 +com.squareup.okhttp3:okhttp:4.11.0 +com.squareup.okio:okio-jvm:3.3.0 +com.squareup.okio:okio:3.3.0 io.coil-kt:coil-base:2.4.0 io.coil-kt:coil-compose-base:2.4.0 io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil-svg:2.4.0 io.coil-kt:coil:2.4.0 javax.inject:javax.inject:1 -org.checkerframework:checker-qual:3.12.0 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 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.10 -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 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1 +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.1 +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 -org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0 -org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index 8123217b9..878a5c462 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -29,10 +29,8 @@ androidx.compose.material:material-icons-extended:1.5.4 androidx.compose.material:material-ripple-android:1.5.4 androidx.compose.material:material-ripple:1.5.4 androidx.compose.runtime:runtime-android:1.5.4 -androidx.compose.runtime:runtime-livedata: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-alpha03 androidx.compose.runtime:runtime:1.5.4 androidx.compose.ui:ui-android:1.5.4 androidx.compose.ui:ui-geometry-android:1.5.4 @@ -74,9 +72,7 @@ androidx.interpolator:interpolator:1.0.0 androidx.legacy:legacy-support-core-utils:1.0.0 androidx.lifecycle:lifecycle-common-java8:2.6.2 androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.2 androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata-ktx:2.6.2 androidx.lifecycle:lifecycle-livedata:2.6.2 androidx.lifecycle:lifecycle-process:2.6.2 androidx.lifecycle:lifecycle-runtime-compose:2.6.2 @@ -108,16 +104,13 @@ 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.2.0-alpha02 -androidx.tracing:tracing-perfetto-common:1.0.0-alpha11 -androidx.tracing:tracing-perfetto:1.0.0-alpha11 -androidx.tracing:tracing:1.2.0-alpha02 +androidx.tracing:tracing-ktx:1.1.0 +androidx.tracing:tracing:1.1.0 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.extensions.core:core:1.0.0 -androidx.window:window:1.1.0 +androidx.window:window:1.0.0 androidx.work:work-runtime-ktx:2.9.0-rc01 androidx.work:work-runtime:2.9.0-rc01 com.caverock:androidsvg-aar:1.4 From 2b739c0ffe5e49ab719b71f8d7a4b9624785621c Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 9 Dec 2023 21:23:13 +0100 Subject: [PATCH 37/70] Fix badging, which is not "stable" when it comes to ordering --- app/prodRelease-badging.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/prodRelease-badging.txt b/app/prodRelease-badging.txt index 6c3a859c7..9ae76fff2 100644 --- a/app/prodRelease-badging.txt +++ b/app/prodRelease-badging.txt @@ -105,9 +105,9 @@ application-icon-640:'res/mipmap-anydpi-v26/ic_launcher.xml' application-icon-65534:'res/mipmap-anydpi-v26/ic_launcher.xml' application: label='Now in Android' icon='res/mipmap-anydpi-v26/ic_launcher.xml' launchable-activity: name='com.google.samples.apps.nowinandroid.MainActivity' label='' icon='' +uses-library-not-required:'android.ext.adservices' uses-library-not-required:'androidx.window.extensions' uses-library-not-required:'androidx.window.sidecar' -uses-library-not-required:'android.ext.adservices' feature-group: label='' uses-feature: name='android.hardware.faketouch' uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps' From 0490e014f6b1e99811d26dacff92268c6704f29d Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 14 Dec 2023 14:26:34 -0800 Subject: [PATCH 39/70] Update Spotless and ktlint --- .editorconfig | 1 + gradle/init.gradle.kts | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 83be1d5a0..2d5a46b3c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,3 +4,4 @@ [*.{kt,kts}] ij_kotlin_allow_trailing_comma=true ij_kotlin_allow_trailing_comma_on_call_site=true +ktlint_function_naming_ignore_when_annotated_with=Composable diff --git a/gradle/init.gradle.kts b/gradle/init.gradle.kts index 72f3eebe0..fe79fa01e 100644 --- a/gradle/init.gradle.kts +++ b/gradle/init.gradle.kts @@ -14,10 +14,10 @@ * limitations under the License. */ -val ktlintVersion = "0.48.1" +val ktlintVersion = "1.0.1" initscript { - val spotlessVersion = "6.22.0" + val spotlessVersion = "6.23.3" repositories { mavenCentral() @@ -35,7 +35,11 @@ rootProject { kotlin { target("**/*.kt") targetExclude("**/build/**/*.kt") - ktlint(ktlintVersion).userData(mapOf("android" to "true")) + ktlint(ktlintVersion).editorConfigOverride( + mapOf( + "android" to "true", + ), + ) licenseHeaderFile(rootProject.file("spotless/copyright.kt")) } format("kts") { From ff599eac37ad179fbf2bdcc1783f4c3493c972af Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 14 Dec 2023 14:46:37 -0800 Subject: [PATCH 40/70] Ignore property-naming and discouraged-comment-location --- gradle/init.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle/init.gradle.kts b/gradle/init.gradle.kts index fe79fa01e..a06d659fa 100644 --- a/gradle/init.gradle.kts +++ b/gradle/init.gradle.kts @@ -38,6 +38,8 @@ rootProject { ktlint(ktlintVersion).editorConfigOverride( mapOf( "android" to "true", + "ktlint_standard_property-naming" to "disabled", + "ktlint_standard_discouraged-comment-location" to "disabled", ), ) licenseHeaderFile(rootProject.file("spotless/copyright.kt")) From fb5bd225be83aff2c444aca999c92986c793a11f Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 14 Dec 2023 14:49:25 -0800 Subject: [PATCH 41/70] Spotless --- .../samples/apps/nowinandroid/MainActivity.kt | 2 +- .../core/analytics/AnalyticsModule.kt | 4 +- .../nowinandroid/core/result/ResultKtTest.kt | 2 +- .../datastore/IntToStringIdsMigrationTest.kt | 4 +- .../core/datastore/ListToMapMigrationTest.kt | 6 +- .../core/designsystem/component/Button.kt | 2 +- .../component/scrollbar/AppScrollbars.kt | 4 +- .../LoadingWheelScreenshotTests.kt | 2 +- .../core/designsystem/TagScreenshotTests.kt | 2 +- .../core/model/data/DarkThemeConfig.kt | 4 +- .../core/model/data/ThemeBrand.kt | 3 +- .../fake/FakeNiaNetworkDataSourceTest.kt | 80 +++++++++---------- .../testing/data/FollowableTopicTestData.kt | 3 +- .../testing/data/NewsResourcesTestData.kt | 3 +- .../core/testing/data/TopicsTestData.kt | 3 +- .../testing/data/UserNewsResourcesTestData.kt | 3 +- ...FollowableTopicPreviewParameterProvider.kt | 3 +- ...serNewsResourcePreviewParameterProvider.kt | 3 +- .../SearchUiStatePreviewParameterProvider.kt | 3 +- .../feature/settings/SettingsDialog.kt | 3 +- 20 files changed, 77 insertions(+), 62 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 7fe1bc674..1f2bc49b9 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 @@ -179,7 +179,7 @@ class MainActivity : ComponentActivity() { To see quick turnaround of the ProfileVerifier, we recommend using `speed-profile`. If you don't do either of these steps, you might only see the profile status reported as "enqueued for compilation" when running the sample locally. - */ + */ withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() Log.d(TAG, "ProfileInstaller status code: ${status.profileInstallResultCode}") 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 index 9f875ae6d..0d2e0e274 100644 --- 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 @@ -35,6 +35,8 @@ abstract class AnalyticsModule { companion object { @Provides @Singleton - fun provideFirebaseAnalytics(): FirebaseAnalytics { return Firebase.analytics } + fun provideFirebaseAnalytics(): FirebaseAnalytics { + return Firebase.analytics + } } } diff --git a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt index 4f1229e9d..512a1a2f2 100644 --- a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt +++ b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt @@ -25,7 +25,7 @@ import kotlin.test.assertEquals class ResultKtTest { @Test - fun Result_catches_errors() = runTest { + fun result_catches_errors() = runTest { flow { emit(1) throw Exception("Test Done") diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt index 8b97cff34..392ca2550 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt @@ -27,7 +27,7 @@ import kotlin.test.assertTrue class IntToStringIdsMigrationTest { @Test - fun IntToStringIdsMigration_should_migrate_topic_ids() = runTest { + fun intToStringIdsMigration_should_migrate_topic_ids() = runTest { // Set up existing preferences with topic int ids val preMigrationUserPreferences = userPreferences { deprecatedIntFollowedTopicIds.addAll(listOf(1, 2, 3)) @@ -56,7 +56,7 @@ class IntToStringIdsMigrationTest { } @Test - fun IntToStringIdsMigration_should_migrate_author_ids() = runTest { + fun intToStringIdsMigration_should_migrate_author_ids() = runTest { // Set up existing preferences with author int ids val preMigrationUserPreferences = userPreferences { deprecatedIntFollowedAuthorIds.addAll(listOf(4, 5, 6)) diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt index f7e083b45..41404d6a9 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt @@ -24,7 +24,7 @@ import kotlin.test.assertTrue class ListToMapMigrationTest { @Test - fun ListToMapMigration_should_migrate_topic_ids() = runTest { + fun listToMapMigration_should_migrate_topic_ids() = runTest { // Set up existing preferences with topic ids val preMigrationUserPreferences = userPreferences { deprecatedFollowedTopicIds.addAll(listOf("1", "2", "3")) @@ -50,7 +50,7 @@ class ListToMapMigrationTest { } @Test - fun ListToMapMigration_should_migrate_author_ids() = runTest { + fun listToMapMigration_should_migrate_author_ids() = runTest { // Set up existing preferences with author ids val preMigrationUserPreferences = userPreferences { deprecatedFollowedAuthorIds.addAll(listOf("4", "5", "6")) @@ -76,7 +76,7 @@ class ListToMapMigrationTest { } @Test - fun ListToMapMigration_should_migrate_bookmarks() = runTest { + fun listToMapMigration_should_migrate_bookmarks() = runTest { // Set up existing preferences with bookmarks val preMigrationUserPreferences = userPreferences { deprecatedBookmarkedNewsResourceIds.addAll(listOf("7", "8", "9")) diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt index 966014fd9..a5ecc86c1 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt @@ -278,7 +278,7 @@ fun NiaButtonPreview() { @ThemePreviews @Composable fun NiaOutlinedButtonPreview() { - NiaTheme() { + NiaTheme { NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) { NiaOutlinedButton(onClick = {}, text = { Text("Test button") }) } 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 fe14bce14..c8102073a 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 @@ -251,5 +251,7 @@ private fun scrollbarThumbColor( } private enum class ThumbState { - Active, Inactive, Dormant + Active, + Inactive, + Dormant, } diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt index 75a4aec3a..9f80d04e5 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt @@ -66,7 +66,7 @@ class LoadingWheelScreenshotTests() { fun loadingWheelAnimation() { composeTestRule.mainClock.autoAdvance = false composeTestRule.setContent { - NiaTheme() { + NiaTheme { NiaLoadingWheel(contentDesc = "") } } diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt index d9edfd6c6..16ed71aeb 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt @@ -47,7 +47,7 @@ class TagScreenshotTests() { val composeTestRule = createAndroidComposeRule() @Test - fun Tag_multipleThemes() { + fun tag_multipleThemes() { composeTestRule.captureMultiTheme("Tag") { NiaTopicTag(followed = true, onClick = {}) { Text("TOPIC") diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt index f130a70db..dcbcaa531 100644 --- a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt @@ -17,5 +17,7 @@ package com.google.samples.apps.nowinandroid.core.model.data enum class DarkThemeConfig { - FOLLOW_SYSTEM, LIGHT, DARK + FOLLOW_SYSTEM, + LIGHT, + DARK, } diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt index d8953df3c..431d4b573 100644 --- a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt @@ -17,5 +17,6 @@ package com.google.samples.apps.nowinandroid.core.model.data enum class ThemeBrand { - DEFAULT, ANDROID + DEFAULT, + ANDROID, } 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 76c2accf2..147252cf1 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 @@ -45,46 +45,46 @@ class FakeNiaNetworkDataSourceTest { } @Test - fun testDeserializationOfTopics() = runTest(testDispatcher) { - assertEquals( - /* ktlint-disable max-line-length */ - NetworkTopic( - id = "1", - name = "Headlines", - shortDescription = "News you'll definitely be interested in", - longDescription = "The latest events and announcements from the world of Android development.", - url = "", - 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", - ), - /* ktlint-enable max-line-length */ - subject.getTopics().first(), - ) - } + fun testDeserializationOfTopics() = + @Suppress("ktlint:standard:max-line-length") + runTest(testDispatcher) { + assertEquals( + NetworkTopic( + id = "1", + name = "Headlines", + shortDescription = "News you'll definitely be interested in", + longDescription = "The latest events and announcements from the world of Android development.", + url = "", + 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", + ), + subject.getTopics().first(), + ) + } @Test - fun testDeserializationOfNewsResources() = runTest(testDispatcher) { - assertEquals( - /* ktlint-disable max-line-length */ - NetworkNewsResource( - 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 = LocalDateTime( - year = 2022, - monthNumber = 5, - dayOfMonth = 4, - hour = 23, - minute = 0, - second = 0, - nanosecond = 0, - ).toInstant(TimeZone.UTC), - type = "Codelab", - topics = listOf("2", "3", "10"), - ), - /* ktlint-enable max-line-length */ - subject.getNewsResources().find { it.id == "125" }, - ) - } + fun testDeserializationOfNewsResources() = + @Suppress("ktlint:standard:max-line-length") + runTest(testDispatcher) { + assertEquals( + NetworkNewsResource( + 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 = LocalDateTime( + year = 2022, + monthNumber = 5, + dayOfMonth = 4, + hour = 23, + minute = 0, + second = 0, + nanosecond = 0, + ).toInstant(TimeZone.UTC), + type = "Codelab", + topics = listOf("2", "3", "10"), + ), + subject.getNewsResources().find { it.id == "125" }, + ) + } } diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt index a96326a62..9b85516e7 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt @@ -14,12 +14,13 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.testing.data import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic -/* ktlint-disable max-line-length */ val followableTopicTestData: List = listOf( FollowableTopic( topic = Topic( diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt index ef845dc58..b3fff7ca0 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/NewsResourcesTestData.kt @@ -14,12 +14,13 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.testing.data import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import kotlinx.datetime.Instant -/* ktlint-disable max-line-length */ val newsResourcesTestData: List = listOf( NewsResource( id = "1", diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt index 90e9166a5..fc6ef62a6 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/TopicsTestData.kt @@ -14,11 +14,12 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.testing.data import com.google.samples.apps.nowinandroid.core.model.data.Topic -/* ktlint-disable max-line-length */ val topicsTestData: List = listOf( Topic( id = "2", diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt index 97acad088..4174391df 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.testing.data import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig @@ -26,7 +28,6 @@ import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant -/* ktlint-disable max-line-length */ val userNewsResourcesTestData: List = UserData( bookmarkedNewsResources = setOf("1", "4"), viewedNewsResources = setOf("1", "2", "4"), diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt index 2132f5b16..16ae1eced 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt @@ -14,13 +14,14 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic -/* ktlint-disable max-line-length */ /** * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) * provides list of [FollowableTopic] for Composable previews. diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt index 493788314..3189e8403 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.core.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -29,7 +31,6 @@ import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant -/* ktlint-disable max-line-length */ /** * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) * provides list of [UserNewsResource] for Composable previews. diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt index 4268893da..257d8b68e 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.feature.search import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -21,7 +23,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.topics -/* ktlint-disable max-line-length */ /** * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) * provides list of [SearchResultUiState] for Composable previews. diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index 01ec30e74..b6ce77d1a 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("ktlint:standard:max-line-length") + package com.google.samples.apps.nowinandroid.feature.settings import android.content.Intent @@ -316,7 +318,6 @@ private fun PreviewSettingsDialogLoading() { } } -/* ktlint-disable max-line-length */ private const val PRIVACY_POLICY_URL = "https://policies.google.com/privacy" private const val BRAND_GUIDELINES_URL = "https://developer.android.com/distribute/marketing-tools/brand-guidelines" private const val FEEDBACK_URL = "https://goo.gle/nia-app-feedback" From f8f932705a71d47f4925f5cad9bbbcacdb3cc0c1 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 14 Dec 2023 14:56:11 -0800 Subject: [PATCH 42/70] Remove violations: discouraged-comment-location --- .../samples/apps/nowinandroid/startup/StartupBenchmark.kt | 3 ++- .../apps/nowinandroid/core/model/data/FollowableTopic.kt | 3 ++- .../apps/nowinandroid/core/testing/util/ScreenshotHelper.kt | 6 ++++-- .../samples/apps/nowinandroid/core/ui/NewsResourceCard.kt | 6 ++++-- .../apps/nowinandroid/feature/foryou/ForYouScreen.kt | 3 ++- .../apps/nowinandroid/feature/interests/InterestsItem.kt | 3 ++- .../apps/nowinandroid/feature/search/SearchScreen.kt | 3 ++- gradle/init.gradle.kts | 1 - 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt index ace7f14e4..96bea89b8 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt @@ -60,7 +60,8 @@ class StartupBenchmark { packageName = PACKAGE_NAME, metrics = listOf(StartupTimingMetric()), compilationMode = compilationMode, - iterations = 20, // More iterations result in higher statistical significance. + // More iterations result in higher statistical significance. + iterations = 20, startupMode = COLD, setupBlock = { pressHome() diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt index cef319c5f..ae1d525c5 100644 --- a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt @@ -19,7 +19,8 @@ package com.google.samples.apps.nowinandroid.core.model.data /** * A [topic] with the additional information for whether or not it is followed. */ -data class FollowableTopic( // TODO consider changing to UserTopic and flattening +// TODO consider changing to UserTopic and flattening +data class FollowableTopic( val topic: Topic, val isFollowed: Boolean, ) diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt index e84fe7d33..0f00ff16d 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt @@ -38,8 +38,10 @@ import org.robolectric.RuntimeEnvironment val DefaultRoborazziOptions = RoborazziOptions( - compareOptions = CompareOptions(changeThreshold = 0f), // Pixel-perfect matching - recordOptions = RecordOptions(resizeScale = 0.5), // Reduce the size of the PNGs + // Pixel-perfect matching + compareOptions = CompareOptions(changeThreshold = 0f), + // Reduce the size of the PNGs + recordOptions = RecordOptions(resizeScale = 0.5), ) enum class DefaultTestDevices(val description: String, val spec: String) { 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 de4aec9d7..114e845d4 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 @@ -186,7 +186,8 @@ fun NewsResourceHeaderImage( painterResource(drawable.ic_placeholder_default) }, // TODO b/226661685: Investigate using alt text of image to populate content description - contentDescription = null, // decorative image, + // decorative image, + contentDescription = null, ) } } @@ -295,7 +296,8 @@ fun NewsResourceTopics( modifier: Modifier = Modifier, ) { Row( - modifier = modifier.horizontalScroll(rememberScrollState()), // causes narrow chips + // causes narrow chips + modifier = modifier.horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(4.dp), ) { for (followableTopic in topics) { 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 65b5ecbc4..f0899b448 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 @@ -437,7 +437,8 @@ fun TopicIcon( DynamicAsyncImage( placeholder = painterResource(R.drawable.ic_icon_placeholder), imageUrl = imageUrl, - contentDescription = null, // decorative + // decorative + contentDescription = null, modifier = modifier .padding(10.dp) .size(32.dp), 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 7456ba92b..a9e329e9c 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 @@ -99,7 +99,8 @@ private fun InterestsIcon(topicImageUrl: String, modifier: Modifier = Modifier) .background(MaterialTheme.colorScheme.surface) .padding(4.dp), imageVector = NiaIcons.Person, - contentDescription = null, // decorative image + // decorative image + contentDescription = null, ) } else { DynamicAsyncImage( 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 65b65f61d..72842ec61 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 @@ -328,7 +328,8 @@ private fun SearchResultBody( topics.forEach { followableTopic -> val topicId = followableTopic.topic.id item( - key = "topic-$topicId", // Append a prefix to distinguish a key for news resources + // Append a prefix to distinguish a key for news resources + key = "topic-$topicId", span = StaggeredGridItemSpan.FullLine, ) { InterestsItem( diff --git a/gradle/init.gradle.kts b/gradle/init.gradle.kts index a06d659fa..f2e6114aa 100644 --- a/gradle/init.gradle.kts +++ b/gradle/init.gradle.kts @@ -39,7 +39,6 @@ rootProject { mapOf( "android" to "true", "ktlint_standard_property-naming" to "disabled", - "ktlint_standard_discouraged-comment-location" to "disabled", ), ) licenseHeaderFile(rootProject.file("spotless/copyright.kt")) From 32e1ec0447294ada76c83bc1f54a88f3487b0e04 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 14 Dec 2023 16:18:27 -0800 Subject: [PATCH 43/70] Remove violations: property-naming --- .../apps/nowinandroid/navigation/NiaNavHost.kt | 4 ++-- .../samples/apps/nowinandroid/ui/NiaAppState.kt | 12 ++++++------ .../core/designsystem/component/Button.kt | 4 ++-- .../core/designsystem/component/Chip.kt | 14 +++++++------- .../core/designsystem/component/IconButton.kt | 4 ++-- .../core/designsystem/component/Tag.kt | 8 ++++---- .../bookmarks/navigation/BookmarksNavigation.kt | 6 +++--- .../feature/foryou/navigation/ForYouNavigation.kt | 6 +++--- .../interests/navigation/InterestsNavigation.kt | 6 +++--- .../feature/search/navigation/SearchNavigation.kt | 6 +++--- .../feature/topic/navigation/TopicNavigation.kt | 8 ++++---- .../feature/topic/TopicViewModelTest.kt | 4 ++-- gradle/init.gradle.kts | 1 - .../sync/initializers/SyncInitializer.kt | 4 ++-- .../sync/status/WorkManagerSyncManager.kt | 6 +++--- 15 files changed, 46 insertions(+), 47 deletions(-) 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 3d58ed5a6..70651ed12 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 @@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.compose.NavHost import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksScreen -import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouNavigationRoute +import com.google.samples.apps.nowinandroid.feature.foryou.navigation.FOR_YOU_NAVIGATION_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.search.navigation.searchScreen @@ -41,7 +41,7 @@ fun NiaNavHost( appState: NiaAppState, onShowSnackbar: suspend (String, String?) -> Boolean, modifier: Modifier = Modifier, - startDestination: String = forYouNavigationRoute, + startDestination: String = FOR_YOU_NAVIGATION_ROUTE, ) { val navController = appState.navController NavHost( 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 09e70069e..9c587cc5c 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 @@ -33,11 +33,11 @@ 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.ui.TrackDisposableJank -import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksRoute +import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.BOOKMARKS_ROUTE import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.navigateToBookmarks -import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouNavigationRoute +import com.google.samples.apps.nowinandroid.feature.foryou.navigation.FOR_YOU_NAVIGATION_ROUTE import com.google.samples.apps.nowinandroid.feature.foryou.navigation.navigateToForYou -import com.google.samples.apps.nowinandroid.feature.interests.navigation.interestsRoute +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.search.navigation.navigateToSearch import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination @@ -91,9 +91,9 @@ class NiaAppState( val currentTopLevelDestination: TopLevelDestination? @Composable get() = when (currentDestination?.route) { - forYouNavigationRoute -> FOR_YOU - bookmarksRoute -> BOOKMARKS - interestsRoute -> INTERESTS + FOR_YOU_NAVIGATION_ROUTE -> FOR_YOU + BOOKMARKS_ROUTE -> BOOKMARKS + INTERESTS_ROUTE -> INTERESTS else -> null } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt index a5ecc86c1..d490ff13e 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Button.kt @@ -134,7 +134,7 @@ fun NiaOutlinedButton( MaterialTheme.colorScheme.outline } else { MaterialTheme.colorScheme.onSurface.copy( - alpha = NiaButtonDefaults.DisabledOutlinedButtonBorderAlpha, + alpha = NiaButtonDefaults.DISABLED_OUTLINED_BUTTON_BORDER_ALPHA, ) }, ), @@ -315,7 +315,7 @@ fun NiaButtonLeadingIconPreview() { object NiaButtonDefaults { // TODO: File bug // OutlinedButton border color doesn't respect disabled state by default - const val DisabledOutlinedButtonBorderAlpha = 0.12f + const val DISABLED_OUTLINED_BUTTON_BORDER_ALPHA = 0.12f // TODO: File bug // OutlinedButton default border width isn't exposed via ButtonDefaults diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt index d1b7d124d..106f0b839 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Chip.kt @@ -76,10 +76,10 @@ fun NiaFilterChip( borderColor = MaterialTheme.colorScheme.onBackground, selectedBorderColor = MaterialTheme.colorScheme.onBackground, disabledBorderColor = MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaChipDefaults.DisabledChipContentAlpha, + alpha = NiaChipDefaults.DISABLED_CHIP_CONTENT_ALPHA, ), disabledSelectedBorderColor = MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaChipDefaults.DisabledChipContentAlpha, + alpha = NiaChipDefaults.DISABLED_CHIP_CONTENT_ALPHA, ), selectedBorderWidth = NiaChipDefaults.ChipBorderWidth, ), @@ -88,16 +88,16 @@ fun NiaFilterChip( iconColor = MaterialTheme.colorScheme.onBackground, disabledContainerColor = if (selected) { MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaChipDefaults.DisabledChipContainerAlpha, + alpha = NiaChipDefaults.DISABLED_CHIP_CONTAINER_ALPHA, ) } else { Color.Transparent }, disabledLabelColor = MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaChipDefaults.DisabledChipContentAlpha, + alpha = NiaChipDefaults.DISABLED_CHIP_CONTENT_ALPHA, ), disabledLeadingIconColor = MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaChipDefaults.DisabledChipContentAlpha, + alpha = NiaChipDefaults.DISABLED_CHIP_CONTENT_ALPHA, ), selectedContainerColor = MaterialTheme.colorScheme.primaryContainer, selectedLabelColor = MaterialTheme.colorScheme.onBackground, @@ -124,7 +124,7 @@ fun ChipPreview() { object NiaChipDefaults { // TODO: File bug // FilterChip default values aren't exposed via FilterChipDefaults - const val DisabledChipContainerAlpha = 0.12f - const val DisabledChipContentAlpha = 0.38f + const val DISABLED_CHIP_CONTAINER_ALPHA = 0.12f + const val DISABLED_CHIP_CONTENT_ALPHA = 0.38f val ChipBorderWidth = 1.dp } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt index 503342d30..43ec11f0b 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/IconButton.kt @@ -60,7 +60,7 @@ fun NiaIconToggleButton( checkedContentColor = MaterialTheme.colorScheme.onPrimaryContainer, disabledContainerColor = if (checked) { MaterialTheme.colorScheme.onBackground.copy( - alpha = NiaIconButtonDefaults.DisabledIconButtonContainerAlpha, + alpha = NiaIconButtonDefaults.DISABLED_ICON_BUTTON_CONTAINER_ALPHA, ) } else { Color.Transparent @@ -123,5 +123,5 @@ fun IconButtonPreviewUnchecked() { object NiaIconButtonDefaults { // TODO: File bug // IconToggleButton disabled container alpha not exposed by IconButtonDefaults - const val DisabledIconButtonContainerAlpha = 0.12f + const val DISABLED_ICON_BUTTON_CONTAINER_ALPHA = 0.12f } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt index 8ca1588a8..290845936 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Tag.kt @@ -40,7 +40,7 @@ fun NiaTopicTag( MaterialTheme.colorScheme.primaryContainer } else { MaterialTheme.colorScheme.surfaceVariant.copy( - alpha = NiaTagDefaults.UnfollowedTopicTagContainerAlpha, + alpha = NiaTagDefaults.UNFOLLOWED_TOPIC_TAG_CONTAINER_ALPHA, ) } TextButton( @@ -50,7 +50,7 @@ fun NiaTopicTag( containerColor = containerColor, contentColor = contentColorFor(backgroundColor = containerColor), disabledContainerColor = MaterialTheme.colorScheme.onSurface.copy( - alpha = NiaTagDefaults.DisabledTopicTagContainerAlpha, + alpha = NiaTagDefaults.DISABLED_TOPIC_TAG_CONTAINER_ALPHA, ), ), ) { @@ -75,9 +75,9 @@ fun TagPreview() { * Now in Android tag default values. */ object NiaTagDefaults { - const val UnfollowedTopicTagContainerAlpha = 0.5f + const val UNFOLLOWED_TOPIC_TAG_CONTAINER_ALPHA = 0.5f // TODO: File bug // Button disabled container alpha value not exposed by ButtonDefaults - const val DisabledTopicTagContainerAlpha = 0.12f + const val DISABLED_TOPIC_TAG_CONTAINER_ALPHA = 0.12f } diff --git a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt index ebcde4ab1..81fa114e2 100644 --- a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt +++ b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt @@ -22,17 +22,17 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksRoute -const val bookmarksRoute = "bookmarks_route" +const val BOOKMARKS_ROUTE = "bookmarks_route" fun NavController.navigateToBookmarks(navOptions: NavOptions? = null) { - this.navigate(bookmarksRoute, navOptions) + this.navigate(BOOKMARKS_ROUTE, navOptions) } fun NavGraphBuilder.bookmarksScreen( onTopicClick: (String) -> Unit, onShowSnackbar: suspend (String, String?) -> Boolean, ) { - composable(route = bookmarksRoute) { + composable(route = BOOKMARKS_ROUTE) { BookmarksRoute(onTopicClick, onShowSnackbar) } } diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt index 705495cc2..264345be4 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt @@ -26,17 +26,17 @@ import androidx.navigation.navDeepLink import com.google.samples.apps.nowinandroid.feature.foryou.ForYouRoute const val LINKED_NEWS_RESOURCE_ID = "linkedNewsResourceId" -const val forYouNavigationRoute = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" +const val FOR_YOU_NAVIGATION_ROUTE = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" private const val DEEP_LINK_URI_PATTERN = "https://www.nowinandroid.apps.samples.google.com/foryou/{$LINKED_NEWS_RESOURCE_ID}" fun NavController.navigateToForYou(navOptions: NavOptions? = null) { - this.navigate(forYouNavigationRoute, navOptions) + this.navigate(FOR_YOU_NAVIGATION_ROUTE, navOptions) } fun NavGraphBuilder.forYouScreen(onTopicClick: (String) -> Unit) { composable( - route = forYouNavigationRoute, + route = FOR_YOU_NAVIGATION_ROUTE, deepLinks = listOf( navDeepLink { uriPattern = DEEP_LINK_URI_PATTERN }, ), 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 7558ec48d..831247e27 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 @@ -24,7 +24,7 @@ import androidx.navigation.navigation import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute private const val INTERESTS_GRAPH_ROUTE_PATTERN = "interests_graph" -const val interestsRoute = "interests_route" +const val INTERESTS_ROUTE = "interests_route" fun NavController.navigateToInterestsGraph(navOptions: NavOptions? = null) { this.navigate(INTERESTS_GRAPH_ROUTE_PATTERN, navOptions) @@ -36,9 +36,9 @@ fun NavGraphBuilder.interestsGraph( ) { navigation( route = INTERESTS_GRAPH_ROUTE_PATTERN, - startDestination = interestsRoute, + startDestination = INTERESTS_ROUTE, ) { - composable(route = interestsRoute) { + composable(route = INTERESTS_ROUTE) { InterestsRoute(onTopicClick) } nestedGraphs() diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt index 42bf3f475..a449600b2 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt @@ -22,10 +22,10 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.google.samples.apps.nowinandroid.feature.search.SearchRoute -const val searchRoute = "search_route" +const val SEARCH_ROUTE = "search_route" fun NavController.navigateToSearch(navOptions: NavOptions? = null) { - this.navigate(searchRoute, navOptions) + this.navigate(SEARCH_ROUTE, navOptions) } fun NavGraphBuilder.searchScreen( @@ -35,7 +35,7 @@ fun NavGraphBuilder.searchScreen( ) { // TODO: Handle back stack for each top-level destination. At the moment each top-level // destination may have own search screen's back stack. - composable(route = searchRoute) { + composable(route = SEARCH_ROUTE) { SearchRoute( onBackClick = onBackClick, onInterestsClick = onInterestsClick, 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 c29b57d47..8052f766e 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 @@ -31,11 +31,11 @@ import kotlin.text.Charsets.UTF_8 private val URL_CHARACTER_ENCODING = UTF_8.name() @VisibleForTesting -internal const val topicIdArg = "topicId" +internal const val TOPIC_ID_ARG = "topicId" internal class TopicArgs(val topicId: String) { constructor(savedStateHandle: SavedStateHandle) : - this(URLDecoder.decode(checkNotNull(savedStateHandle[topicIdArg]), URL_CHARACTER_ENCODING)) + this(URLDecoder.decode(checkNotNull(savedStateHandle[TOPIC_ID_ARG]), URL_CHARACTER_ENCODING)) } fun NavController.navigateToTopic(topicId: String) { @@ -50,9 +50,9 @@ fun NavGraphBuilder.topicScreen( onTopicClick: (String) -> Unit, ) { composable( - route = "topic_route/{$topicIdArg}", + route = "topic_route/{$TOPIC_ID_ARG}", arguments = listOf( - navArgument(topicIdArg) { type = NavType.StringType }, + navArgument(TOPIC_ID_ARG) { type = NavType.StringType }, ), ) { TopicRoute(onBackClick = onBackClick, onTopicClick = onTopicClick) diff --git a/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt b/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt index 8c6253d60..a9c9d96dc 100644 --- a/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt +++ b/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt @@ -25,7 +25,7 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepo import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule -import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicIdArg +import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ID_ARG import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first @@ -60,7 +60,7 @@ class TopicViewModelTest { @Before fun setup() { viewModel = TopicViewModel( - savedStateHandle = SavedStateHandle(mapOf(topicIdArg to testInputTopics[0].topic.id)), + savedStateHandle = SavedStateHandle(mapOf(TOPIC_ID_ARG to testInputTopics[0].topic.id)), userDataRepository = userDataRepository, topicsRepository = topicsRepository, userNewsResourceRepository = userNewsResourceRepository, diff --git a/gradle/init.gradle.kts b/gradle/init.gradle.kts index f2e6114aa..fe79fa01e 100644 --- a/gradle/init.gradle.kts +++ b/gradle/init.gradle.kts @@ -38,7 +38,6 @@ rootProject { ktlint(ktlintVersion).editorConfigOverride( mapOf( "android" to "true", - "ktlint_standard_property-naming" to "disabled", ), ) licenseHeaderFile(rootProject.file("spotless/copyright.kt")) diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/initializers/SyncInitializer.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/initializers/SyncInitializer.kt index 00f61f17d..0a631534b 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/initializers/SyncInitializer.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/initializers/SyncInitializer.kt @@ -28,7 +28,7 @@ object Sync { WorkManager.getInstance(context).apply { // Run sync on app startup and ensure only one sync worker runs at any time enqueueUniqueWork( - SyncWorkName, + SYNC_WORK_NAME, ExistingWorkPolicy.KEEP, SyncWorker.startUpSyncWork(), ) @@ -37,4 +37,4 @@ object Sync { } // This name should not be changed otherwise the app may have concurrent sync requests running -internal const val SyncWorkName = "SyncWorkName" +internal const val SYNC_WORK_NAME = "SyncWorkName" diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt index 9131e4888..1d251588e 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/status/WorkManagerSyncManager.kt @@ -22,7 +22,7 @@ import androidx.work.WorkInfo import androidx.work.WorkInfo.State import androidx.work.WorkManager import com.google.samples.apps.nowinandroid.core.data.util.SyncManager -import com.google.samples.apps.nowinandroid.sync.initializers.SyncWorkName +import com.google.samples.apps.nowinandroid.sync.initializers.SYNC_WORK_NAME import com.google.samples.apps.nowinandroid.sync.workers.SyncWorker import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow @@ -37,7 +37,7 @@ class WorkManagerSyncManager @Inject constructor( @ApplicationContext private val context: Context, ) : SyncManager { override val isSyncing: Flow = - WorkManager.getInstance(context).getWorkInfosForUniqueWorkFlow(SyncWorkName) + WorkManager.getInstance(context).getWorkInfosForUniqueWorkFlow(SYNC_WORK_NAME) .map(List::anyRunning) .conflate() @@ -45,7 +45,7 @@ class WorkManagerSyncManager @Inject constructor( val workManager = WorkManager.getInstance(context) // Run sync on app startup and ensure only one sync worker runs at any time workManager.enqueueUniqueWork( - SyncWorkName, + SYNC_WORK_NAME, ExistingWorkPolicy.KEEP, SyncWorker.startUpSyncWork(), ) From 83764082ffb7871d175cd915197a309f837797b5 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sat, 16 Dec 2023 10:50:38 +0000 Subject: [PATCH 44/70] Move app screenshot tests to match dependencies declaration Fixes #990 --- .../ui/NiaAppScreenSizesScreenshotTests.kt | 2 +- ...ompactWidth_compactHeight_showsNavigationBar.png | Bin ...mpactWidth_expandedHeight_showsNavigationBar.png | Bin ...compactWidth_mediumHeight_showsNavigationBar.png | Bin ...andedWidth_compactHeight_showsNavigationRail.png | Bin ...ndedWidth_expandedHeight_showsNavigationRail.png | Bin ...pandedWidth_mediumHeight_showsNavigationRail.png | Bin ...ediumWidth_compactHeight_showsNavigationRail.png | Bin ...diumWidth_expandedHeight_showsNavigationRail.png | Bin ...mediumWidth_mediumHeight_showsNavigationRail.png | Bin 10 files changed, 1 insertion(+), 1 deletion(-) rename app/src/{testDemo => testDemoDebug}/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt (99%) rename app/src/{testDemo => testDemoDebug}/screenshots/compactWidth_compactHeight_showsNavigationBar.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/compactWidth_expandedHeight_showsNavigationBar.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/compactWidth_mediumHeight_showsNavigationBar.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/expandedWidth_compactHeight_showsNavigationRail.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/mediumWidth_compactHeight_showsNavigationRail.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png (100%) rename app/src/{testDemo => testDemoDebug}/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png (100%) diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt similarity index 99% rename from app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt rename to app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index dcbc1e5c0..63a7c7f91 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -154,7 +154,7 @@ class NiaAppScreenSizesScreenshotTests { composeTestRule.onRoot() .captureRoboImage( - "src/testDemo/screenshots/$screenshotName.png", + "src/testDemoDebug/screenshots/$screenshotName.png", roborazziOptions = DefaultRoborazziOptions, ) } diff --git a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png b/app/src/testDemoDebug/screenshots/compactWidth_compactHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png rename to app/src/testDemoDebug/screenshots/compactWidth_compactHeight_showsNavigationBar.png diff --git a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png b/app/src/testDemoDebug/screenshots/compactWidth_expandedHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png rename to app/src/testDemoDebug/screenshots/compactWidth_expandedHeight_showsNavigationBar.png diff --git a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png b/app/src/testDemoDebug/screenshots/compactWidth_mediumHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png rename to app/src/testDemoDebug/screenshots/compactWidth_mediumHeight_showsNavigationBar.png diff --git a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/expandedWidth_compactHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/expandedWidth_compactHeight_showsNavigationRail.png diff --git a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png diff --git a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png diff --git a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/mediumWidth_compactHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/mediumWidth_compactHeight_showsNavigationRail.png diff --git a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png diff --git a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemoDebug/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png rename to app/src/testDemoDebug/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png From f9a498caf6b5f67c8646813be21ad757ff182ff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 08:44:06 +0000 Subject: [PATCH 45/70] Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/Build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index d47342c6a..791cf6127 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -91,14 +91,14 @@ jobs: -x collectProdNonMinifiedBenchmarkBaselineProfile - name: Upload build outputs (APKs) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: APKs path: '**/build/outputs/apk/**/*.apk' - name: Upload test results (XML) if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results path: '**/build/test-results/test*UnitTest/**.xml' @@ -108,7 +108,7 @@ jobs: - name: Upload lint reports (HTML) if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: lint-reports path: '**/build/reports/lint-results-*.html' @@ -154,7 +154,7 @@ jobs: - name: Upload test reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-reports-${{ matrix.api-level }} path: '**/build/reports/androidTests' From 28bb3f1b2fe56e0842eed15aa1f43243afdc634f Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 19 Dec 2023 13:46:40 +0000 Subject: [PATCH 46/70] Add comment explaining why the launcher icon colors differ per build variant Change-Id: I30126402370162ccbe53e177652583e4472f2030 --- app/src/benchmark/res/values-night/colors.xml | 2 ++ app/src/benchmark/res/values/colors.xml | 2 ++ app/src/debug/res/values-night/colors.xml | 2 ++ app/src/debug/res/values/colors.xml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml index 677eb4e03..7e62bc52c 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/benchmark/res/values-night/colors.xml @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> + #FFFFFF #FF006780 diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index d33b7ba72..8524b17d2 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> + #000000 #FF006780 diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml index d6a4c98e0..2d36bde01 100644 --- a/app/src/debug/res/values-night/colors.xml +++ b/app/src/debug/res/values-night/colors.xml @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> + #FFFFFF #FFA23F16 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index 6365ddb3f..56a856913 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> + #000000 #FFA23F16 From e6b5560f078e371fef4513848f833b0087fffd63 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 19 Dec 2023 14:59:53 +0000 Subject: [PATCH 47/70] Revert "Move app screenshot tests to match dependencies declaration" --- .../ui/NiaAppScreenSizesScreenshotTests.kt | 2 +- ...ompactWidth_compactHeight_showsNavigationBar.png | Bin ...mpactWidth_expandedHeight_showsNavigationBar.png | Bin ...compactWidth_mediumHeight_showsNavigationBar.png | Bin ...andedWidth_compactHeight_showsNavigationRail.png | Bin ...ndedWidth_expandedHeight_showsNavigationRail.png | Bin ...pandedWidth_mediumHeight_showsNavigationRail.png | Bin ...ediumWidth_compactHeight_showsNavigationRail.png | Bin ...diumWidth_expandedHeight_showsNavigationRail.png | Bin ...mediumWidth_mediumHeight_showsNavigationRail.png | Bin 10 files changed, 1 insertion(+), 1 deletion(-) rename app/src/{testDemoDebug => testDemo}/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt (99%) rename app/src/{testDemoDebug => testDemo}/screenshots/compactWidth_compactHeight_showsNavigationBar.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/compactWidth_expandedHeight_showsNavigationBar.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/compactWidth_mediumHeight_showsNavigationBar.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/expandedWidth_compactHeight_showsNavigationRail.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/mediumWidth_compactHeight_showsNavigationRail.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png (100%) rename app/src/{testDemoDebug => testDemo}/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png (100%) diff --git a/app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt similarity index 99% rename from app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt rename to app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index 63a7c7f91..dcbc1e5c0 100644 --- a/app/src/testDemoDebug/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -154,7 +154,7 @@ class NiaAppScreenSizesScreenshotTests { composeTestRule.onRoot() .captureRoboImage( - "src/testDemoDebug/screenshots/$screenshotName.png", + "src/testDemo/screenshots/$screenshotName.png", roborazziOptions = DefaultRoborazziOptions, ) } diff --git a/app/src/testDemoDebug/screenshots/compactWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemoDebug/screenshots/compactWidth_compactHeight_showsNavigationBar.png rename to app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png diff --git a/app/src/testDemoDebug/screenshots/compactWidth_expandedHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemoDebug/screenshots/compactWidth_expandedHeight_showsNavigationBar.png rename to app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png diff --git a/app/src/testDemoDebug/screenshots/compactWidth_mediumHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png similarity index 100% rename from app/src/testDemoDebug/screenshots/compactWidth_mediumHeight_showsNavigationBar.png rename to app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png diff --git a/app/src/testDemoDebug/screenshots/expandedWidth_compactHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/expandedWidth_compactHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationRail.png diff --git a/app/src/testDemoDebug/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png diff --git a/app/src/testDemoDebug/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png diff --git a/app/src/testDemoDebug/screenshots/mediumWidth_compactHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/mediumWidth_compactHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationRail.png diff --git a/app/src/testDemoDebug/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png diff --git a/app/src/testDemoDebug/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png similarity index 100% rename from app/src/testDemoDebug/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png rename to app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png From d711a1a4dd08a5a36a2731c7617a995aa4559a36 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 19 Dec 2023 15:50:56 +0000 Subject: [PATCH 48/70] Update README.md --- README.md | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b71427dfe..6f13f5de2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The app is currently in development. The `prodRelease` variant is [available on **Now in Android** displays content from the [Now in Android](https://developer.android.com/series/now-in-android) series. Users can browse for links to recent videos, articles and other content. Users can also follow topics they are interested -in. +in, and be notified when new content is published which matches interests they are following. ## Screenshots @@ -109,12 +109,42 @@ Examples: manipulate the state of the `Test` repository and verify the resulting behavior, instead of checking that specific repository methods were called. -## Screenshot tests +To run the tests execute the following gradle tasks: + +- `testDemoDebug` run all local tests against the `demoDebug` variant. +- `connectedDemoDebugAndroidTest` run all instrumented tests against the `demoDebug` variant. -**Now In Android** uses [Roborazzi](https://github.com/takahirom/roborazzi) to do screenshot tests -of certain screens and components. To run these tests, run the `verifyRoborazziDemoDebug` or -`recordRoborazziDemoDebug` tasks. Note that screenshots are recorded on CI, using Linux, and other -platforms might generate slightly different images, making the tests fail. +**Note:** You should not run `./gradlew test` or `./gradlew connectedAndroidTest` as this will execute +tests against _all_ build variants which is both unecessary and will result in failures as only the +`demoDebug` variant is supported. No other variants have any tests (although this might change in future). + +## Screenshot tests +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) +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)). + +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: + +- `verifyRoborazziDemoDebug` run all screenshot tests, verifying the screenshots against the known +correct screenshots. +- `recordRoborazziDemoDebug` record new "known correct" screenshots. Use this command when you have +made changes to the UI and manually verified that they are rendered correctly. Screenshots will be +stored in `modulename/src/test/screenshots`. +- `compareRoborazziDemoDebug` create comparison images between failed tests and the known correct +images. These can also be found in `modulename/src/test/screenshots`. + +**Note:** The known correct screenshots stored in this repository are recorded on CI using Linux. Other +platforms may (and probably will) generate slightly different images, making the screenshot tests fail. +When working on a non-Linux platform, a workaround to this is to run `recordRoborazziDemoDebug` on the +`main` branch before starting work. After making changes, `verifyRoborazziDemoDebug` will identify only +legitimate changes. + +For more information about screenshot testing +[check out this talk](https://www.droidcon.com/2023/11/15/easy-screenshot-testing-with-compose/). # UI The app was designed using [Material 3 guidelines](https://m3.material.io/). Learn more about the design process and From a49e901fb419d179387382e72f927ef3363dfe8e Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 19 Dec 2023 16:02:35 +0000 Subject: [PATCH 49/70] Don't run unit tests on prodDebug variant Change-Id: I0adf6c70b9634de876aed64400a7d3b4033983b3 --- .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 d47342c6a..d1d631117 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -75,7 +75,7 @@ jobs: # Run local tests after screenshot tests to avoid wrong UP-TO-DATE. TODO: Ignore screenshots. - name: Run local tests if: always() - run: ./gradlew testDemoDebug testProdDebug :lint:test + run: ./gradlew testDemoDebug :lint:test # Replace task exclusions with `-Pandroidx.baselineprofile.skipgeneration` when # https://android-review.googlesource.com/c/platform/frameworks/support/+/2602790 landed in a # release build From d018e57f526d38d8de8a6a7798eda25742cc93a6 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 19 Dec 2023 16:05:36 +0000 Subject: [PATCH 50/70] Fix spotless Change-Id: I5dea4fec5b391e8cb7794af13e99978fcb622fbb --- app/src/benchmark/res/values-night/colors.xml | 2 -- app/src/benchmark/res/values/colors.xml | 2 -- app/src/debug/res/values-night/colors.xml | 2 -- app/src/debug/res/values/colors.xml | 2 -- 4 files changed, 8 deletions(-) diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml index 7e62bc52c..677eb4e03 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/benchmark/res/values-night/colors.xml @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - #FFFFFF #FF006780 diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index 8524b17d2..d33b7ba72 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - #000000 #FF006780 diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml index 2d36bde01..d6a4c98e0 100644 --- a/app/src/debug/res/values-night/colors.xml +++ b/app/src/debug/res/values-night/colors.xml @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - #FFFFFF #FFA23F16 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index 56a856913..6365ddb3f 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - #000000 #FFA23F16 From 2a2d8ed88df563e22e82b0b53efa351850f3c629 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 19 Dec 2023 10:17:42 -0800 Subject: [PATCH 51/70] Change the name of FOR_YOU_NAVIGATION_ROUTE to FOR_YOU_ROUTE --- .../samples/apps/nowinandroid/navigation/NiaNavHost.kt | 4 ++-- .../com/google/samples/apps/nowinandroid/ui/NiaAppState.kt | 4 ++-- .../feature/foryou/navigation/ForYouNavigation.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) 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 70651ed12..6167b0b59 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 @@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier 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_NAVIGATION_ROUTE +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.search.navigation.searchScreen @@ -41,7 +41,7 @@ fun NiaNavHost( appState: NiaAppState, onShowSnackbar: suspend (String, String?) -> Boolean, modifier: Modifier = Modifier, - startDestination: String = FOR_YOU_NAVIGATION_ROUTE, + startDestination: String = FOR_YOU_ROUTE, ) { val navController = appState.navController NavHost( 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 9c587cc5c..b99eab245 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 @@ -35,7 +35,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor 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 -import com.google.samples.apps.nowinandroid.feature.foryou.navigation.FOR_YOU_NAVIGATION_ROUTE +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 @@ -91,7 +91,7 @@ class NiaAppState( val currentTopLevelDestination: TopLevelDestination? @Composable get() = when (currentDestination?.route) { - FOR_YOU_NAVIGATION_ROUTE -> FOR_YOU + FOR_YOU_ROUTE -> FOR_YOU BOOKMARKS_ROUTE -> BOOKMARKS INTERESTS_ROUTE -> INTERESTS else -> null diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt index 264345be4..154b0f83b 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt @@ -26,17 +26,17 @@ import androidx.navigation.navDeepLink import com.google.samples.apps.nowinandroid.feature.foryou.ForYouRoute const val LINKED_NEWS_RESOURCE_ID = "linkedNewsResourceId" -const val FOR_YOU_NAVIGATION_ROUTE = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" +const val FOR_YOU_ROUTE = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" private const val DEEP_LINK_URI_PATTERN = "https://www.nowinandroid.apps.samples.google.com/foryou/{$LINKED_NEWS_RESOURCE_ID}" fun NavController.navigateToForYou(navOptions: NavOptions? = null) { - this.navigate(FOR_YOU_NAVIGATION_ROUTE, navOptions) + this.navigate(FOR_YOU_ROUTE, navOptions) } fun NavGraphBuilder.forYouScreen(onTopicClick: (String) -> Unit) { composable( - route = FOR_YOU_NAVIGATION_ROUTE, + route = FOR_YOU_ROUTE, deepLinks = listOf( navDeepLink { uriPattern = DEEP_LINK_URI_PATTERN }, ), From d671126f9eb5532206db2c7260e3cc06a978dd77 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 19 Dec 2023 11:15:18 -0800 Subject: [PATCH 52/70] Move @Suppress("ktlint:standard:max-line-length") to function scope --- .../fake/FakeNiaNetworkDataSourceTest.kt | 78 +++++++++---------- 1 file changed, 38 insertions(+), 40 deletions(-) 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 147252cf1..a0c60fdcb 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 @@ -44,47 +44,45 @@ class FakeNiaNetworkDataSourceTest { ) } + @Suppress("ktlint:standard:max-line-length") @Test - fun testDeserializationOfTopics() = - @Suppress("ktlint:standard:max-line-length") - runTest(testDispatcher) { - assertEquals( - NetworkTopic( - id = "1", - name = "Headlines", - shortDescription = "News you'll definitely be interested in", - longDescription = "The latest events and announcements from the world of Android development.", - url = "", - 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", - ), - subject.getTopics().first(), - ) - } + fun testDeserializationOfTopics() = runTest(testDispatcher) { + assertEquals( + NetworkTopic( + id = "1", + name = "Headlines", + shortDescription = "News you'll definitely be interested in", + longDescription = "The latest events and announcements from the world of Android development.", + url = "", + 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", + ), + subject.getTopics().first(), + ) + } + @Suppress("ktlint:standard:max-line-length") @Test - fun testDeserializationOfNewsResources() = - @Suppress("ktlint:standard:max-line-length") - runTest(testDispatcher) { - assertEquals( - NetworkNewsResource( - 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 = LocalDateTime( - year = 2022, - monthNumber = 5, - dayOfMonth = 4, - hour = 23, - minute = 0, - second = 0, - nanosecond = 0, - ).toInstant(TimeZone.UTC), - type = "Codelab", - topics = listOf("2", "3", "10"), - ), - subject.getNewsResources().find { it.id == "125" }, - ) - } + fun testDeserializationOfNewsResources() = runTest(testDispatcher) { + assertEquals( + NetworkNewsResource( + 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 = LocalDateTime( + year = 2022, + monthNumber = 5, + dayOfMonth = 4, + hour = 23, + minute = 0, + second = 0, + nanosecond = 0, + ).toInstant(TimeZone.UTC), + type = "Codelab", + topics = listOf("2", "3", "10"), + ), + subject.getNewsResources().find { it.id == "125" }, + ) + } } From 485440b550c0a241e96fbe78e52632ed0ec94619 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 19 Dec 2023 12:42:49 -0800 Subject: [PATCH 53/70] Suppress ktlint:standard:function-naming in ResultKtTest --- .../samples/apps/nowinandroid/core/result/ResultKtTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt index 512a1a2f2..08fed508c 100644 --- a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt +++ b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt @@ -24,8 +24,9 @@ import kotlin.test.assertEquals class ResultKtTest { + @Suppress("ktlint:standard:function-naming") @Test - fun result_catches_errors() = runTest { + fun Result_catches_errors() = runTest { flow { emit(1) throw Exception("Test Done") From 56fbe13b5abe497df8b32a262001a922f3f9921c Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 19 Dec 2023 14:16:13 -0800 Subject: [PATCH 54/70] Ignore function naming check when annotated with @Test --- .editorconfig | 2 +- .../samples/apps/nowinandroid/core/result/ResultKtTest.kt | 1 - .../core/datastore/IntToStringIdsMigrationTest.kt | 4 ++-- .../nowinandroid/core/datastore/ListToMapMigrationTest.kt | 6 +++--- .../nowinandroid/core/designsystem/TagScreenshotTests.kt | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2d5a46b3c..7be3f8784 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,4 +4,4 @@ [*.{kt,kts}] ij_kotlin_allow_trailing_comma=true ij_kotlin_allow_trailing_comma_on_call_site=true -ktlint_function_naming_ignore_when_annotated_with=Composable +ktlint_function_naming_ignore_when_annotated_with=Composable, Test diff --git a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt index 08fed508c..4f1229e9d 100644 --- a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt +++ b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt @@ -24,7 +24,6 @@ import kotlin.test.assertEquals class ResultKtTest { - @Suppress("ktlint:standard:function-naming") @Test fun Result_catches_errors() = runTest { flow { diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt index 392ca2550..8b97cff34 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt @@ -27,7 +27,7 @@ import kotlin.test.assertTrue class IntToStringIdsMigrationTest { @Test - fun intToStringIdsMigration_should_migrate_topic_ids() = runTest { + fun IntToStringIdsMigration_should_migrate_topic_ids() = runTest { // Set up existing preferences with topic int ids val preMigrationUserPreferences = userPreferences { deprecatedIntFollowedTopicIds.addAll(listOf(1, 2, 3)) @@ -56,7 +56,7 @@ class IntToStringIdsMigrationTest { } @Test - fun intToStringIdsMigration_should_migrate_author_ids() = runTest { + fun IntToStringIdsMigration_should_migrate_author_ids() = runTest { // Set up existing preferences with author int ids val preMigrationUserPreferences = userPreferences { deprecatedIntFollowedAuthorIds.addAll(listOf(4, 5, 6)) diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt index 41404d6a9..f7e083b45 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt @@ -24,7 +24,7 @@ import kotlin.test.assertTrue class ListToMapMigrationTest { @Test - fun listToMapMigration_should_migrate_topic_ids() = runTest { + fun ListToMapMigration_should_migrate_topic_ids() = runTest { // Set up existing preferences with topic ids val preMigrationUserPreferences = userPreferences { deprecatedFollowedTopicIds.addAll(listOf("1", "2", "3")) @@ -50,7 +50,7 @@ class ListToMapMigrationTest { } @Test - fun listToMapMigration_should_migrate_author_ids() = runTest { + fun ListToMapMigration_should_migrate_author_ids() = runTest { // Set up existing preferences with author ids val preMigrationUserPreferences = userPreferences { deprecatedFollowedAuthorIds.addAll(listOf("4", "5", "6")) @@ -76,7 +76,7 @@ class ListToMapMigrationTest { } @Test - fun listToMapMigration_should_migrate_bookmarks() = runTest { + fun ListToMapMigration_should_migrate_bookmarks() = runTest { // Set up existing preferences with bookmarks val preMigrationUserPreferences = userPreferences { deprecatedBookmarkedNewsResourceIds.addAll(listOf("7", "8", "9")) diff --git a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt index 16ed71aeb..d9edfd6c6 100644 --- a/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt +++ b/core/designsystem/src/test/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt @@ -47,7 +47,7 @@ class TagScreenshotTests() { val composeTestRule = createAndroidComposeRule() @Test - fun tag_multipleThemes() { + fun Tag_multipleThemes() { composeTestRule.captureMultiTheme("Tag") { NiaTopicTag(followed = true, onClick = {}) { Text("TOPIC") From 115e66b6e34950354bf3f28617d2c67674509b29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:18:00 +0000 Subject: [PATCH 55/70] Bump lint from 31.1.3 to 31.2.0 Bumps `lint` from 31.1.3 to 31.2.0. Updates `com.android.tools.lint:lint-api` from 31.1.3 to 31.2.0 Updates `com.android.tools.lint:lint-checks` from 31.1.3 to 31.2.0 Updates `com.android.tools.lint:lint-tests` from 31.1.3 to 31.2.0 --- updated-dependencies: - dependency-name: com.android.tools.lint:lint-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.android.tools.lint:lint-checks dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.android.tools.lint:lint-tests 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 95fb23428..7333db3dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,7 +47,7 @@ kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" ksp = "1.9.10-1.0.13" -lint = "31.1.3" +lint = "31.2.0" okhttp = "4.12.0" protobuf = "3.24.4" protobufPlugin = "0.9.4" From f36af7c2102a15f73e467d931456e97532834c8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:18:46 +0000 Subject: [PATCH 56/70] Bump hilt from 2.49 to 2.50 Bumps `hilt` from 2.49 to 2.50. Updates `com.google.dagger:hilt-android` from 2.49 to 2.50 - [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.49...dagger-2.50) Updates `com.google.dagger:hilt-android-testing` from 2.49 to 2.50 - [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.49...dagger-2.50) Updates `com.google.dagger:hilt-android-compiler` from 2.49 to 2.50 - [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.49...dagger-2.50) Updates `com.google.dagger.hilt.android` from 2.49 to 2.50 - [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.49...dagger-2.50) --- 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 95fb23428..df5db8cd0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" googleOss = "17.0.1" googleOssPlugin = "0.10.6" -hilt = "2.49" +hilt = "2.50" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" From 60c5f5ee264c9dc08b521bccbbef4bcad09f2f34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:19:42 +0000 Subject: [PATCH 57/70] Bump coil from 2.4.0 to 2.5.0 Bumps `coil` from 2.4.0 to 2.5.0. Updates `io.coil-kt:coil` from 2.4.0 to 2.5.0 - [Release notes](https://github.com/coil-kt/coil/releases) - [Changelog](https://github.com/coil-kt/coil/blob/main/CHANGELOG.md) - [Commits](https://github.com/coil-kt/coil/compare/2.4.0...2.5.0) Updates `io.coil-kt:coil-compose` from 2.4.0 to 2.5.0 - [Release notes](https://github.com/coil-kt/coil/releases) - [Changelog](https://github.com/coil-kt/coil/blob/main/CHANGELOG.md) - [Commits](https://github.com/coil-kt/coil/compare/2.4.0...2.5.0) Updates `io.coil-kt:coil-svg` from 2.4.0 to 2.5.0 - [Release notes](https://github.com/coil-kt/coil/releases) - [Changelog](https://github.com/coil-kt/coil/blob/main/CHANGELOG.md) - [Commits](https://github.com/coil-kt/coil/compare/2.4.0...2.5.0) --- updated-dependencies: - dependency-name: io.coil-kt:coil dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.coil-kt:coil-compose dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.coil-kt:coil-svg 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 95fb23428..6268707b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.1.0" androidxWork = "2.9.0-rc01" -coil = "2.4.0" +coil = "2.5.0" dependencyGuard = "0.4.3" firebaseBom = "32.4.0" firebaseCrashlyticsPlugin = "2.9.9" From e9f7cb820457c20f0f79f6e6e4c35434c16d4a45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:19:58 +0000 Subject: [PATCH 58/70] Bump room from 2.6.0 to 2.6.1 Bumps `room` from 2.6.0 to 2.6.1. Updates `androidx.room:room-compiler` from 2.6.0 to 2.6.1 Updates `androidx.room:room-ktx` from 2.6.0 to 2.6.1 Updates `androidx.room:room-runtime` from 2.6.0 to 2.6.1 --- updated-dependencies: - dependency-name: androidx.room:room-compiler dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: androidx.room:room-ktx dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: androidx.room:room-runtime dependency-type: direct:production update-type: version-update:semver-patch ... 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 95fb23428..90e8bd2bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,7 +55,7 @@ retrofit = "2.9.0" retrofitKotlinxSerializationJson = "1.0.0" robolectric = "4.11.1" roborazzi = "1.6.0" -room = "2.6.0" +room = "2.6.1" secrets = "2.0.1" truth = "1.1.5" turbine = "1.0.0" From a878e70f418a2aa88fe07a6e921a06dac42f8800 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:20:43 +0000 Subject: [PATCH 59/70] Bump androidGradlePlugin from 8.1.3 to 8.2.0 Bumps `androidGradlePlugin` from 8.1.3 to 8.2.0. Updates `com.android.tools.build:gradle` from 8.1.3 to 8.2.0 Updates `com.android.application` from 8.1.3 to 8.2.0 Updates `com.android.library` from 8.1.3 to 8.2.0 Updates `com.android.test` from 8.1.3 to 8.2.0 --- updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.android.application dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.android.library dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.android.test 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 95fb23428..15ea9b0c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanist = "0.32.0" androidDesugarJdkLibs = "2.0.3" # AGP and tools should be updated together -androidGradlePlugin = "8.1.3" +androidGradlePlugin = "8.2.0" androidTools = "31.1.3" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" From 334377e303e9708240729a575bf607697a23ad65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:20:59 +0000 Subject: [PATCH 60/70] Bump androidx.window:window from 1.1.0 to 1.2.0 Bumps androidx.window:window from 1.1.0 to 1.2.0. --- updated-dependencies: - dependency-name: androidx.window:window 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 95fb23428..2f529984d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ androidxTestRules = "1.5.0" androidxTestRunner = "1.5.2" androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" -androidxWindowManager = "1.1.0" +androidxWindowManager = "1.2.0" androidxWork = "2.9.0-rc01" coil = "2.4.0" dependencyGuard = "0.4.3" From 5ccf4f5ec61e1a4304d7ff86af95370989cee32a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:21:06 +0000 Subject: [PATCH 61/70] Bump com.android.tools:desugar_jdk_libs from 2.0.3 to 2.0.4 Bumps [com.android.tools:desugar_jdk_libs](https://github.com/google/desugar_jdk_libs) from 2.0.3 to 2.0.4. - [Changelog](https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/desugar_jdk_libs/commits) --- updated-dependencies: - dependency-name: com.android.tools:desugar_jdk_libs dependency-type: direct:production update-type: version-update:semver-patch ... 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 95fb23428..94823336d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] accompanist = "0.32.0" -androidDesugarJdkLibs = "2.0.3" +androidDesugarJdkLibs = "2.0.4" # AGP and tools should be updated together androidGradlePlugin = "8.1.3" androidTools = "31.1.3" From 2dd917fd7de59803745cc10c7ddcf06b5a083a07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:22:19 +0000 Subject: [PATCH 62/70] Bump the kotlin-ksp-compose group with 7 updates Bumps the kotlin-ksp-compose group with 7 updates: | Package | From | To | | --- | --- | --- | | androidx.compose.compiler:compiler | `1.5.3` | `1.5.7` | | [org.jetbrains.kotlin:kotlin-stdlib-jdk8](https://github.com/JetBrains/kotlin) | `1.9.10` | `1.9.21` | | [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin) | `1.9.10` | `1.9.21` | | [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) | `1.9.10` | `1.9.21` | | [org.jetbrains.kotlin.plugin.serialization](https://github.com/JetBrains/kotlin) | `1.9.10` | `1.9.21` | | [com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin](https://github.com/google/ksp) | `1.9.10-1.0.13` | `1.9.21-1.0.16` | | [com.google.devtools.ksp](https://github.com/google/ksp) | `1.9.10-1.0.13` | `1.9.21-1.0.16` | Updates `androidx.compose.compiler:compiler` from 1.5.3 to 1.5.7 Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 1.9.10 to 1.9.21 - [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.10...v1.9.21) Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.9.10 to 1.9.21 - [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.10...v1.9.21) Updates `org.jetbrains.kotlin.jvm` from 1.9.10 to 1.9.21 - [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.10...v1.9.21) Updates `org.jetbrains.kotlin.plugin.serialization` from 1.9.10 to 1.9.21 - [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.10...v1.9.21) Updates `com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin` from 1.9.10-1.0.13 to 1.9.21-1.0.16 - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/compare/1.9.10-1.0.13...1.9.21-1.0.16) Updates `com.google.devtools.ksp` from 1.9.10-1.0.13 to 1.9.21-1.0.16 - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/compare/1.9.10-1.0.13...1.9.21-1.0.16) --- updated-dependencies: - dependency-name: androidx.compose.compiler:compiler dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kotlin-ksp-compose - 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: 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 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 65ddba6bc..95f3c7827 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.3" +androidxComposeCompiler = "1.5.7" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" @@ -42,11 +42,11 @@ hilt = "2.49" hiltExt = "1.1.0" jacoco = "0.8.7" junit4 = "4.13.2" -kotlin = "1.9.10" +kotlin = "1.9.21" kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" -ksp = "1.9.10-1.0.13" +ksp = "1.9.21-1.0.16" lint = "31.2.0" okhttp = "4.12.0" protobuf = "3.24.4" From b801a7deeae4a474bedfa12e95b817e7bc83c090 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Wed, 20 Dec 2023 18:55:34 +0000 Subject: [PATCH 63/70] Moving comments to _after_ first XML element Change-Id: Id4271a67ffcfbd712a2dd1f363970fcd31ee5ac0 --- app/src/benchmark/res/values-night/colors.xml | 2 ++ app/src/benchmark/res/values/colors.xml | 2 ++ app/src/debug/res/values-night/colors.xml | 2 ++ app/src/debug/res/values/colors.xml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml index 677eb4e03..cbf22c766 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/benchmark/res/values-night/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> + #FFFFFF #FF006780 diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index d33b7ba72..a98c6d8f6 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> + #000000 #FF006780 diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml index d6a4c98e0..daa017e4a 100644 --- a/app/src/debug/res/values-night/colors.xml +++ b/app/src/debug/res/values-night/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> + #FFFFFF #FFA23F16 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index 6365ddb3f..487a7820b 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> + #000000 #FFA23F16 From 57bf575514f2bb0e1c140e57b7c3e92a96b0457b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:03:28 +0000 Subject: [PATCH 64/70] Bump androidxWork from 2.9.0-rc01 to 2.9.0 Bumps `androidxWork` from 2.9.0-rc01 to 2.9.0. Updates `androidx.work:work-runtime-ktx` from 2.9.0-rc01 to 2.9.0 Updates `androidx.work:work-testing` from 2.8.1 to 2.9.0 --- updated-dependencies: - dependency-name: androidx.work:work-runtime-ktx dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: androidx.work:work-testing dependency-type: direct:production update-type: version-update:semver-minor ... 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 17a65460b..8f3ac5dd8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ androidxTestRunner = "1.5.2" androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.2.0" -androidxWork = "2.9.0-rc01" +androidxWork = "2.9.0" coil = "2.5.0" dependencyGuard = "0.4.3" firebaseBom = "32.4.0" @@ -152,7 +152,7 @@ firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "fir firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } -work-testing = { group = "androidx.work", name = "work-testing", version = "2.8.1" } +work-testing = { group = "androidx.work", name = "work-testing", version = "2.9.0" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } From be9dc533e36815ddbdeb448a69e641e9ca1687ab Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 22 Dec 2023 09:48:37 +0100 Subject: [PATCH 65/70] Replace `Enum.values()` with `Enum.entries` 'Enum.values()' is recommended to be replaced by 'Enum.entries' since 1.9 --- .../com/google/samples/apps/nowinandroid/ui/NiaAppState.kt | 2 +- .../interests/ScrollTopicListPowerMetricsBenchmark.kt | 2 +- .../apps/nowinandroid/core/testing/util/ScreenshotHelper.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 b99eab245..7a38b6649 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 @@ -115,7 +115,7 @@ class NiaAppState( * Map of top level destinations to be used in the TopBar, BottomBar and NavRail. The key is the * route. */ - val topLevelDestinations: List = TopLevelDestination.values().asList() + val topLevelDestinations: List = TopLevelDestination.entries /** * The top level destinations that have unread news resources. diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/ScrollTopicListPowerMetricsBenchmark.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/ScrollTopicListPowerMetricsBenchmark.kt index 13c6f55e3..f938fad62 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/ScrollTopicListPowerMetricsBenchmark.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/ScrollTopicListPowerMetricsBenchmark.kt @@ -45,7 +45,7 @@ class ScrollTopicListPowerMetricsBenchmark { @get:Rule val benchmarkRule = MacrobenchmarkRule() - private val categories = PowerCategory.values() + private val categories = PowerCategory.entries .associateWith { PowerCategoryDisplayLevel.TOTAL } @Test diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt index 0f00ff16d..468fff8df 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt @@ -53,7 +53,7 @@ fun AndroidComposeTestRule, A>.c screenshotName: String, body: @Composable () -> Unit, ) { - DefaultTestDevices.values().forEach { + DefaultTestDevices.entries.forEach { this.captureForDevice(it.description, it.spec, screenshotName, body = body) } } From a8450176ede6f51143563f56a94b210c27ef081e Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 22 Dec 2023 11:04:43 +0100 Subject: [PATCH 66/70] Add tests for `DesignSystemDetector` lint --- .../designsystem/DesignSystemDetectorTest.kt | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt new file mode 100644 index 000000000..6ea55c7e9 --- /dev/null +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt @@ -0,0 +1,162 @@ +/* + * 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.lint.designsystem + +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin +import com.android.tools.lint.checks.infrastructure.TestLintTask.lint +import com.google.samples.apps.nowinandroid.lint.designsystem.DesignSystemDetector.Companion.ISSUE +import com.google.samples.apps.nowinandroid.lint.designsystem.DesignSystemDetector.Companion.METHOD_NAMES +import com.google.samples.apps.nowinandroid.lint.designsystem.DesignSystemDetector.Companion.RECEIVER_NAMES +import org.junit.Test + +class DesignSystemDetectorTest { + + @Test + fun `detect replacements of Composable`() { + lint() + .issues(ISSUE) + .allowMissingSdk() + .files( + COMPOSABLE_STUB, + STUBS, + kotlin( + """ + |import androidx.compose.runtime.Composable + | + |@Composable + |fun App() { + ${METHOD_NAMES.keys.joinToString("\n") { "| $it()" }} + |} + """.trimMargin(), + ).indented(), + ) + .run() + .expect( + """ + src/test.kt:5: Error: Using MaterialTheme instead of NiaTheme [DesignSystem] + MaterialTheme() + ~~~~~~~~~~~~~~~ + src/test.kt:6: Error: Using Button instead of NiaButton [DesignSystem] + Button() + ~~~~~~~~ + src/test.kt:7: Error: Using OutlinedButton instead of NiaOutlinedButton [DesignSystem] + OutlinedButton() + ~~~~~~~~~~~~~~~~ + src/test.kt:8: Error: Using TextButton instead of NiaTextButton [DesignSystem] + TextButton() + ~~~~~~~~~~~~ + src/test.kt:9: Error: Using FilterChip instead of NiaFilterChip [DesignSystem] + FilterChip() + ~~~~~~~~~~~~ + src/test.kt:10: Error: Using ElevatedFilterChip instead of NiaFilterChip [DesignSystem] + ElevatedFilterChip() + ~~~~~~~~~~~~~~~~~~~~ + src/test.kt:11: Error: Using NavigationBar instead of NiaNavigationBar [DesignSystem] + NavigationBar() + ~~~~~~~~~~~~~~~ + src/test.kt:12: Error: Using NavigationBarItem instead of NiaNavigationBarItem [DesignSystem] + NavigationBarItem() + ~~~~~~~~~~~~~~~~~~~ + src/test.kt:13: Error: Using NavigationRail instead of NiaNavigationRail [DesignSystem] + NavigationRail() + ~~~~~~~~~~~~~~~~ + src/test.kt:14: Error: Using NavigationRailItem instead of NiaNavigationRailItem [DesignSystem] + NavigationRailItem() + ~~~~~~~~~~~~~~~~~~~~ + src/test.kt:15: Error: Using TabRow instead of NiaTabRow [DesignSystem] + TabRow() + ~~~~~~~~ + src/test.kt:16: Error: Using Tab instead of NiaTab [DesignSystem] + Tab() + ~~~~~ + src/test.kt:17: Error: Using IconToggleButton instead of NiaIconToggleButton [DesignSystem] + IconToggleButton() + ~~~~~~~~~~~~~~~~~~ + src/test.kt:18: Error: Using FilledIconToggleButton instead of NiaIconToggleButton [DesignSystem] + FilledIconToggleButton() + ~~~~~~~~~~~~~~~~~~~~~~~~ + src/test.kt:19: Error: Using FilledTonalIconToggleButton instead of NiaIconToggleButton [DesignSystem] + FilledTonalIconToggleButton() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + src/test.kt:20: Error: Using OutlinedIconToggleButton instead of NiaIconToggleButton [DesignSystem] + OutlinedIconToggleButton() + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + src/test.kt:21: Error: Using CenterAlignedTopAppBar instead of NiaTopAppBar [DesignSystem] + CenterAlignedTopAppBar() + ~~~~~~~~~~~~~~~~~~~~~~~~ + src/test.kt:22: Error: Using SmallTopAppBar instead of NiaTopAppBar [DesignSystem] + SmallTopAppBar() + ~~~~~~~~~~~~~~~~ + src/test.kt:23: Error: Using MediumTopAppBar instead of NiaTopAppBar [DesignSystem] + MediumTopAppBar() + ~~~~~~~~~~~~~~~~~ + src/test.kt:24: Error: Using LargeTopAppBar instead of NiaTopAppBar [DesignSystem] + LargeTopAppBar() + ~~~~~~~~~~~~~~~~ + 20 errors, 0 warnings + """.trimIndent(), + ) + } + + @Test + fun `detect replacements of Receiver`() { + lint() + .issues(ISSUE) + .allowMissingSdk() + .files( + COMPOSABLE_STUB, + STUBS, + kotlin( + """ + |fun main() { + ${RECEIVER_NAMES.keys.joinToString("\n") { "| $it.toString()" }} + |} + """.trimMargin(), + ).indented(), + ) + .run() + .expect( + """ + src/test.kt:2: Error: Using Icons instead of NiaIcons [DesignSystem] + Icons.toString() + ~~~~~~~~~~~~~~~~ + 1 errors, 0 warnings + """.trimIndent(), + ) + } + + private companion object { + + private val COMPOSABLE_STUB: TestFile = kotlin( + """ + package androidx.compose.runtime + annotation class Composable + """.trimIndent(), + ).indented() + + private val STUBS: TestFile = kotlin( + """ + |import androidx.compose.runtime.Composable + | + ${METHOD_NAMES.keys.joinToString("\n") { "|@Composable fun $it() = {}" }} + ${RECEIVER_NAMES.keys.joinToString("\n") { "|object $it" }} + | + """.trimMargin(), + ).indented() + } +} From b043d8db8691e34392d1e3e4a8dcd32bed7f68e7 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 22 Dec 2023 11:32:57 +0100 Subject: [PATCH 67/70] Replace workaround on SettingsViewModel now that a fix has been deployed The issue has been addressed in `androidx.compose.ui:ui:1.5.0`. --- .../feature/settings/SettingsViewModel.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt index a49c0d512..123c84d1c 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt @@ -24,12 +24,13 @@ import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds @HiltViewModel class SettingsViewModel @Inject constructor( @@ -48,13 +49,7 @@ class SettingsViewModel @Inject constructor( } .stateIn( scope = viewModelScope, - // Starting eagerly means the user data is ready when the SettingsDialog is laid out - // for the first time. Without this, due to b/221643630 the layout is done using the - // "Loading" text, then replaced with the user editable fields once loaded, however, - // the layout height doesn't change meaning all the fields are squashed into a small - // scrollable column. - // TODO: Change to SharingStarted.WhileSubscribed(5_000) when b/221643630 is fixed - started = SharingStarted.Eagerly, + started = WhileSubscribed(5.seconds.inWholeMilliseconds), initialValue = Loading, ) From 59143b0b79608dc316d6cc130caddc7f7b631346 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 22 Dec 2023 12:08:40 +0100 Subject: [PATCH 68/70] Suppress `LintImplTrimIndent` for `trimMargin()` --- .../nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt index 6ea55c7e9..188a52ee0 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemDetectorTest.kt @@ -34,6 +34,7 @@ class DesignSystemDetectorTest { .files( COMPOSABLE_STUB, STUBS, + @Suppress("LintImplTrimIndent") kotlin( """ |import androidx.compose.runtime.Composable @@ -121,6 +122,7 @@ class DesignSystemDetectorTest { .files( COMPOSABLE_STUB, STUBS, + @Suppress("LintImplTrimIndent") kotlin( """ |fun main() { From 046831df36fb59559dbf159368a604d0324c5c0e Mon Sep 17 00:00:00 2001 From: Don Turner Date: Fri, 22 Dec 2023 11:33:22 +0000 Subject: [PATCH 69/70] Update dependency guard classpaths Change-Id: I5f729bce92ec1cc9f71e34e42cbe38765966fc31 --- .../dependencies/releaseRuntimeClasspath.txt | 73 ++++++++++--------- .../prodReleaseRuntimeClasspath.txt | 45 ++++++------ 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 7db29f30c..10f2dfa32 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -1,15 +1,16 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 +androidx.annotation:annotation-experimental:1.3.1 +androidx.annotation:annotation-jvm:1.7.0 +androidx.annotation:annotation:1.7.0 androidx.appcompat:appcompat-resources:1.6.1 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 androidx.browser:browser:1.6.0 -androidx.collection:collection:1.2.0 +androidx.collection:collection-jvm:1.3.0 +androidx.collection:collection:1.3.0 androidx.compose.animation:animation-android:1.5.4 androidx.compose.animation:animation-core-android:1.5.4 androidx.compose.animation:animation-core:1.5.4 @@ -45,24 +46,24 @@ androidx.compose.ui:ui-util:1.5.4 androidx.compose.ui:ui:1.5.4 androidx.compose:compose-bom:2023.10.01 androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.10.0 -androidx.core:core:1.10.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core:1.12.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.0.0 androidx.emoji2:emoji2:1.4.0 androidx.exifinterface:exifinterface:1.3.6 androidx.fragment:fragment:1.5.1 androidx.interpolator:interpolator:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.1 -androidx.lifecycle:lifecycle-common:2.6.1 -androidx.lifecycle:lifecycle-livedata-core:2.6.1 -androidx.lifecycle:lifecycle-livedata:2.6.1 -androidx.lifecycle:lifecycle-process:2.6.1 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 -androidx.lifecycle:lifecycle-runtime:2.6.1 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 -androidx.lifecycle:lifecycle-viewmodel:2.6.1 +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.loader:loader:1.0.0 androidx.metrics:metrics-performance:1.0.0-alpha04 androidx.profileinstaller:profileinstaller:1.3.1 @@ -74,29 +75,29 @@ 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 -com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.code.findbugs:jsr305:3.0.2 -com.google.dagger:dagger-lint-aar:2.48.1 -com.google.dagger:dagger:2.48.1 -com.google.dagger:hilt-android:2.48.1 -com.google.dagger:hilt-core:2.48.1 +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.guava:listenablefuture:1.0 -com.squareup.okhttp3:okhttp:4.11.0 -com.squareup.okio:okio-jvm:3.3.0 -com.squareup.okio:okio:3.3.0 -io.coil-kt:coil-base:2.4.0 -io.coil-kt:coil-compose-base:2.4.0 -io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil:2.4.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 javax.inject:javax.inject:1 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 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.10 -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1 -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.1 -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 -org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlin:kotlin-stdlib:1.9.21 +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 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.5.0 +org.jetbrains.kotlinx:kotlinx-datetime:0.5.0 org.jetbrains:annotations:23.0.0 diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index 878a5c462..3f88c60ec 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -1,17 +1,18 @@ androidx.activity:activity-compose:1.8.0 androidx.activity:activity-ktx:1.8.0 androidx.activity:activity:1.8.0 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 +androidx.annotation:annotation-experimental:1.3.1 +androidx.annotation:annotation-jvm:1.7.0 +androidx.annotation:annotation:1.7.0 androidx.appcompat:appcompat-resources:1.6.1 androidx.appcompat:appcompat:1.6.1 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 androidx.browser:browser:1.6.0 -androidx.collection:collection-ktx:1.1.0 -androidx.collection:collection:1.2.0 +androidx.collection:collection-jvm:1.3.0 +androidx.collection:collection-ktx:1.3.0 +androidx.collection:collection:1.3.0 androidx.compose.animation:animation-android:1.5.4 androidx.compose.animation:animation-core-android:1.5.4 androidx.compose.animation:animation-core:1.5.4 @@ -96,9 +97,9 @@ androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 androidx.profileinstaller:profileinstaller:1.3.1 androidx.resourceinspection:resourceinspection-annotation:1.0.1 -androidx.room:room-common:2.6.0 -androidx.room:room-ktx:2.6.0 -androidx.room:room-runtime:2.6.0 +androidx.room:room-common:2.6.1 +androidx.room:room-ktx:2.6.1 +androidx.room:room-runtime:2.6.1 androidx.savedstate:savedstate-ktx:1.2.1 androidx.savedstate:savedstate:1.2.1 androidx.sqlite:sqlite-framework:2.4.0 @@ -114,7 +115,7 @@ androidx.window:window:1.0.0 androidx.work:work-runtime-ktx:2.9.0-rc01 androidx.work:work-runtime:2.9.0-rc01 com.caverock:androidsvg-aar:1.4 -com.google.accompanist:accompanist-drawablepainter:0.30.1 +com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-permissions:0.32.0 com.google.android.datatransport:transport-api:3.0.0 com.google.android.datatransport:transport-backend-cct:3.1.9 @@ -133,10 +134,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.48.1 -com.google.dagger:dagger:2.48.1 -com.google.dagger:hilt-android:2.48.1 -com.google.dagger:hilt-core:2.48.1 +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.errorprone:error_prone_annotations:2.11.0 com.google.firebase:firebase-abt:21.1.1 com.google.firebase:firebase-analytics-ktx:21.4.0 @@ -175,27 +176,27 @@ com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.6.0 com.squareup.okio:okio:3.6.0 com.squareup.retrofit2:retrofit:2.9.0 -io.coil-kt:coil-base:2.4.0 -io.coil-kt:coil-compose-base:2.4.0 -io.coil-kt:coil-compose:2.4.0 -io.coil-kt:coil-svg:2.4.0 -io.coil-kt:coil:2.4.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.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.10 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 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.10 +org.jetbrains.kotlin:kotlin-stdlib:1.9.21 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 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.1 -org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.5.0 +org.jetbrains.kotlinx:kotlinx-datetime:0.5.0 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.0 org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0 From 61f80739ef131429d4de19d0cf55e8306cea194b Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 22 Dec 2023 12:47:24 +0100 Subject: [PATCH 70/70] Merge `lint` dependency version with `androidTools` --- gradle/libs.versions.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 02aa9280f..34c19dd47 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ accompanist = "0.32.0" androidDesugarJdkLibs = "2.0.4" # AGP and tools should be updated together androidGradlePlugin = "8.2.0" -androidTools = "31.1.3" +androidTools = "31.2.0" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" @@ -47,7 +47,6 @@ kotlinxCoroutines = "1.7.3" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" ksp = "1.9.21-1.0.16" -lint = "31.2.0" okhttp = "4.12.0" protobuf = "3.24.4" protobufPlugin = "0.9.4" @@ -127,9 +126,9 @@ kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-co kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } -lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } -lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "lint" } -lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "lint" } +lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "androidTools" } +lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "androidTools" } +lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "androidTools" } okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }