diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 6bee0ddfb..5bf03b47a 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -25,9 +25,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v3 - - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -38,7 +35,10 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v3 + uses: gradle/actions/setup-gradle@v3 + with: + validate-wrappers: true + gradle-home-cache-cleanup: true - name: Check build-logic run: ./gradlew check -p build-logic @@ -187,7 +187,10 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/gradle-build-action@v3 + uses: gradle/actions/setup-gradle@v3 + with: + validate-wrappers: true + gradle-home-cache-cleanup: true - name: Build projects and run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index 2184a4c50..b18b41faa 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -19,10 +19,8 @@ jobs: ls /dev/kvm - name: Checkout - uses: actions/checkout@v4 - - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v3 + uses: actions/checkout@v4 - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -33,6 +31,12 @@ jobs: distribution: 'zulu' java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + with: + validate-wrappers: true + gradle-home-cache-cleanup: true + - name: Install GMD image for baseline profile generation run: yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager "system-images;android-33;aosp_atd;x86_64" diff --git a/README.md b/README.md index be1270b16..1f5270323 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,8 @@ Examples: To run the tests execute the following gradle tasks: -- `testDemoDebug` run all local tests against the `demoDebug` variant. +- `testDemoDebug` run all local tests against the `demoDebug` variant. Screenshot tests will fail +(see below for explanation). To avoid this, run `recordRoborazziDemoDebug` prior to running unit tests. - `connectedDemoDebugAndroidTest` run all instrumented tests against the `demoDebug` variant. **Note:** You should not run `./gradlew test` or `./gradlew connectedAndroidTest` as this will execute @@ -137,7 +138,7 @@ 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 +**Note on failing screenshot tests:** 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 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 355ec42c0..9c6989f67 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,8 +20,8 @@ plugins { alias(libs.plugins.nowinandroid.android.application.compose) alias(libs.plugins.nowinandroid.android.application.flavors) alias(libs.plugins.nowinandroid.android.application.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) alias(libs.plugins.nowinandroid.android.application.firebase) + alias(libs.plugins.nowinandroid.hilt) id("com.google.android.gms.oss-licenses-plugin") alias(libs.plugins.baselineprofile) alias(libs.plugins.roborazzi) @@ -112,16 +112,14 @@ dependencies { kspTest(libs.hilt.compiler) testImplementation(projects.core.dataTest) - testImplementation(projects.core.testing) - testImplementation(projects.sync.syncTest) - testImplementation(libs.androidx.compose.ui.test) - testImplementation(libs.androidx.work.testing) testImplementation(libs.hilt.android.testing) + testImplementation(projects.sync.syncTest) testDemoImplementation(libs.robolectric) testDemoImplementation(libs.roborazzi) testDemoImplementation(projects.core.screenshotTesting) + androidTestImplementation(kotlin("test")) androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.dataTest) androidTestImplementation(projects.core.datastoreTest) diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index 214ae5ca8..96e5940e8 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -4,8 +4,8 @@ androidx.activity:activity:1.8.2 androidx.annotation:annotation-experimental:1.4.0 androidx.annotation:annotation-jvm:1.8.0 androidx.annotation:annotation:1.8.0 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 +androidx.appcompat:appcompat-resources:1.7.0 +androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 @@ -75,7 +75,7 @@ androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 androidx.exifinterface:exifinterface:1.3.7 -androidx.fragment:fragment:1.5.1 +androidx.fragment:fragment:1.5.4 androidx.graphics:graphics-path:1.0.1 androidx.hilt:hilt-common:1.1.0 androidx.hilt:hilt-navigation-compose:1.2.0 @@ -83,26 +83,26 @@ androidx.hilt:hilt-navigation:1.2.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.8.0 -androidx.lifecycle:lifecycle-common-jvm:2.8.0 -androidx.lifecycle:lifecycle-common:2.8.0 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.0 -androidx.lifecycle:lifecycle-livedata-core:2.8.0 -androidx.lifecycle:lifecycle-livedata:2.8.0 -androidx.lifecycle:lifecycle-process:2.8.0 -androidx.lifecycle:lifecycle-runtime-android:2.8.0 -androidx.lifecycle:lifecycle-runtime-compose-android:2.8.0 -androidx.lifecycle:lifecycle-runtime-compose:2.8.0 -androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.8.0 -androidx.lifecycle:lifecycle-runtime:2.8.0 -androidx.lifecycle:lifecycle-service:2.8.0 -androidx.lifecycle:lifecycle-viewmodel-android:2.8.0 -androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.0 -androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.0 -androidx.lifecycle:lifecycle-viewmodel:2.8.0 +androidx.lifecycle:lifecycle-common-java8:2.8.3 +androidx.lifecycle:lifecycle-common-jvm:2.8.3 +androidx.lifecycle:lifecycle-common:2.8.3 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.3 +androidx.lifecycle:lifecycle-livedata-core:2.8.3 +androidx.lifecycle:lifecycle-livedata:2.8.3 +androidx.lifecycle:lifecycle-process:2.8.3 +androidx.lifecycle:lifecycle-runtime-android:2.8.3 +androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3 +androidx.lifecycle:lifecycle-runtime-compose:2.8.3 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3 +androidx.lifecycle:lifecycle-runtime-ktx:2.8.3 +androidx.lifecycle:lifecycle-runtime:2.8.3 +androidx.lifecycle:lifecycle-service:2.8.3 +androidx.lifecycle:lifecycle-viewmodel-android:2.8.3 +androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.3 +androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 +androidx.lifecycle:lifecycle-viewmodel:2.8.3 androidx.loader:loader:1.0.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.metrics:metrics-performance:1.0.0-alpha04 @@ -140,59 +140,56 @@ androidx.work:work-runtime:2.9.0 com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-permissions:0.34.0 -com.google.android.datatransport:transport-api:3.0.0 +com.google.android.datatransport:transport-api:3.1.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-basement:18.4.0 +com.google.android.gms:play-services-cloud-messaging:17.2.0 +com.google.android.gms:play-services-measurement-api:22.0.2 +com.google.android.gms:play-services-measurement-base:22.0.2 +com.google.android.gms:play-services-measurement-impl:22.0.2 +com.google.android.gms:play-services-measurement-sdk-api:22.0.2 +com.google.android.gms:play-services-measurement-sdk:22.0.2 +com.google.android.gms:play-services-measurement:22.0.2 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.android.gms:play-services-tasks:18.2.0 com.google.code.findbugs:jsr305:3.0.2 com.google.dagger:dagger-lint-aar:2.51.1 com.google.dagger:dagger:2.51.1 com.google.dagger:hilt-android:2.51.1 com.google.dagger:hilt-core:2.51.1 -com.google.errorprone:error_prone_annotations:2.11.0 +com.google.errorprone:error_prone_annotations:2.26.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-analytics:22.0.2 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-bom:33.1.1 +com.google.firebase:firebase-common-ktx:21.0.0 +com.google.firebase:firebase-common:21.0.0 +com.google.firebase:firebase-components:18.0.0 +com.google.firebase:firebase-config-interop:16.0.1 +com.google.firebase:firebase-config:22.0.0 +com.google.firebase:firebase-crashlytics:19.0.2 +com.google.firebase:firebase-datatransport:18.2.0 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:firebase-installations:18.0.0 +com.google.firebase:firebase-measurement-connector:20.0.1 +com.google.firebase:firebase-messaging:24.0.0 +com.google.firebase:firebase-perf:21.0.1 +com.google.firebase:firebase-sessions:2.0.2 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:4.26.0 -com.google.protobuf:protobuf-kotlin-lite:4.26.0 +com.google.protobuf:protobuf-javalite:4.26.1 +com.google.protobuf:protobuf-kotlin-lite:4.26.1 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 diff --git a/app/prodRelease-badging.txt b/app/prodRelease-badging.txt index 769e0a6e4..21b36a7fb 100644 --- a/app/prodRelease-badging.txt +++ b/app/prodRelease-badging.txt @@ -6,9 +6,9 @@ uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.POST_NOTIFICATIONS' uses-permission: name='android.permission.WAKE_LOCK' uses-permission: name='com.google.android.c2dm.permission.RECEIVE' +uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE' uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' uses-permission: name='android.permission.FOREGROUND_SERVICE' -uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE' uses-permission: name='com.google.samples.apps.nowinandroid.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION' application-label:'Now in Android' application-label-af:'Now in Android' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 9c7f3b935..5f4922bce 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,13 +1,3 @@ --dontwarn org.bouncycastle.jsse.BCSSLParameters --dontwarn org.bouncycastle.jsse.BCSSLSocket --dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider --dontwarn org.conscrypt.Conscrypt$Version --dontwarn org.conscrypt.Conscrypt --dontwarn org.conscrypt.ConscryptHostnameVerifier --dontwarn org.openjsse.javax.net.ssl.SSLParameters --dontwarn org.openjsse.javax.net.ssl.SSLSocket --dontwarn org.openjsse.net.ssl.OpenJSSE - # Fix for Retrofit issue https://github.com/square/retrofit/issues/3751 # Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items). -keep,allowobfuscation,allowshrinking interface retrofit2.Call @@ -16,4 +6,4 @@ # With R8 full mode generic signatures are stripped for classes that are not # kept. Suspend functions are wrapped in continuations where the type argument # is used. --keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation \ No newline at end of file +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation 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 93c674bcc..b15024cc7 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 @@ -16,13 +16,11 @@ package com.google.samples.apps.nowinandroid.ui -import androidx.annotation.StringRes import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithContentDescription @@ -47,7 +45,6 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder 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.search.R as FeatureSearchR @@ -88,9 +85,6 @@ class NavigationTest { @Inject lateinit var topicsRepository: TopicsRepository - private fun AndroidComposeTestRule<*, *>.stringResource(@StringRes resId: Int) = - ReadOnlyProperty { _, _ -> activity.getString(resId) } - // The strings used for matching in these tests private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_navigate_up) private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt new file mode 100644 index 000000000..bdc09885d --- /dev/null +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.ui + +import androidx.annotation.StringRes +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import kotlin.properties.ReadOnlyProperty + +fun AndroidComposeTestRule<*, *>.stringResource( + @StringRes resId: Int, +): ReadOnlyProperty = + ReadOnlyProperty { _, _ -> activity.getString(resId) } diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt new file mode 100644 index 000000000..21ac3e920 --- /dev/null +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt @@ -0,0 +1,228 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.ui.interests2pane + +import androidx.activity.compose.BackHandler +import androidx.compose.material3.adaptive.Posture +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.ui.test.DeviceConfigurationOverride +import androidx.compose.ui.test.ForcedSize +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.test.espresso.Espresso +import androidx.window.core.layout.WindowSizeClass +import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.ui.stringResource +import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import javax.inject.Inject +import kotlin.test.assertTrue +import com.google.samples.apps.nowinandroid.feature.topic.R as FeatureTopicR + +@HiltAndroidTest +class InterestsListDetailScreenTest { + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @BindValue + @get:Rule(order = 1) + val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() + + @get:Rule(order = 2) + val composeTestRule = createAndroidComposeRule() + + @Inject + lateinit var topicsRepository: TopicsRepository + + // The strings used for matching in these tests. + private val placeholderText by composeTestRule.stringResource(FeatureTopicR.string.feature_topic_select_an_interest) + private val listPaneTag = "interests:topics" + + private val Topic.testTag + get() = "topic:${this.id}" + + // Overrides for device sizes. + private enum class TestDeviceConfig(widthDp: Float, heightDp: Float) { + Compact(412f, 915f), + Expanded(1200f, 840f), + ; + + val sizeOverride = DeviceConfigurationOverride.ForcedSize(DpSize(widthDp.dp, heightDp.dp)) + val adaptiveInfo = WindowAdaptiveInfo( + windowSizeClass = WindowSizeClass.compute(widthDp, heightDp), + windowPosture = Posture(), + ) + } + + @Before + fun setup() { + hiltRule.inject() + } + + /** Convenience function for getting all topics during tests, */ + private fun getTopics(): List = runBlocking { + topicsRepository.getTopics().first() + } + + @Test + fun expandedWidth_initialState_showsTwoPanesWithPlaceholder() { + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Expanded) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsDisplayed() + } + } + + @Test + fun compactWidth_initialState_showsListPane() { + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Compact) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + } + } + + @Test + fun expandedWidth_topicSelected_updatesDetailPane() { + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Expanded) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + val firstTopic = getTopics().first() + onNodeWithText(firstTopic.name).performClick() + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsDisplayed() + } + } + + @Test + fun compactWidth_topicSelected_showsTopicDetailPane() { + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Compact) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + val firstTopic = getTopics().first() + onNodeWithText(firstTopic.name).performClick() + + onNodeWithTag(listPaneTag).assertIsNotDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsDisplayed() + } + } + + @Test + fun expandedWidth_backPressFromTopicDetail_leavesInterests() { + var unhandledBackPress = false + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Expanded) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + // Back press should not be handled by the two pane layout, and thus + // "fall through" to this BackHandler. + BackHandler { + unhandledBackPress = true + } + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + val firstTopic = getTopics().first() + onNodeWithText(firstTopic.name).performClick() + + Espresso.pressBack() + + assertTrue(unhandledBackPress) + } + } + + @Test + fun compactWidth_backPressFromTopicDetail_showsListPane() { + composeTestRule.apply { + setContent { + with(TestDeviceConfig.Compact) { + DeviceConfigurationOverride(override = sizeOverride) { + NiaTheme { + InterestsListDetailScreen(windowAdaptiveInfo = adaptiveInfo) + } + } + } + } + + val firstTopic = getTopics().first() + onNodeWithText(firstTopic.name).performClick() + + Espresso.pressBack() + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsNotDisplayed() + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0b0482c13..716305ab6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,11 +20,13 @@ + + + + + diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt index ada4e49d1..919cb44f2 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt @@ -18,11 +18,14 @@ package com.google.samples.apps.nowinandroid.ui.interests2pane import androidx.activity.compose.BackHandler import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.AnimatedPane import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.material3.adaptive.layout.PaneAdaptedValue import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem +import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator import androidx.compose.runtime.Composable @@ -71,11 +74,13 @@ fun NavGraphBuilder.interestsListDetailScreen() { @Composable internal fun InterestsListDetailScreen( viewModel: Interests2PaneViewModel = hiltViewModel(), + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle() InterestsListDetailScreen( selectedTopicId = selectedTopicId, onTopicClick = viewModel::onTopicClick, + windowAdaptiveInfo = windowAdaptiveInfo, ) } @@ -84,8 +89,10 @@ internal fun InterestsListDetailScreen( internal fun InterestsListDetailScreen( selectedTopicId: String?, onTopicClick: (String) -> Unit, + windowAdaptiveInfo: WindowAdaptiveInfo, ) { val listDetailNavigator = rememberListDetailPaneScaffoldNavigator( + scaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo), initialDestinationHistory = listOfNotNull( ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.List), ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.Detail).takeIf { diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt new file mode 100644 index 000000000..2fc88e561 --- /dev/null +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.ui + +import android.view.WindowInsets +import android.widget.FrameLayout +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.test.DeviceConfigurationOverride +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.children + +/** + * A [DeviceConfigurationOverride] that allows overriding the [windowInsets] available + * to the content under test. + */ +@Suppress("ktlint:standard:function-naming") +fun DeviceConfigurationOverride.Companion.WindowInsets( + windowInsets: WindowInsetsCompat, +): DeviceConfigurationOverride = DeviceConfigurationOverride { contentUnderTest -> + val currentContentUnderTest by rememberUpdatedState(contentUnderTest) + val currentWindowInsets by rememberUpdatedState(windowInsets) + AndroidView( + factory = { context -> + object : FrameLayout(context) { + override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets { + children.forEach { + it.dispatchApplyWindowInsets(currentWindowInsets.toWindowInsets()) + } + return WindowInsetsCompat.CONSUMED.toWindowInsets()!! + } + + /** + * Deprecated, but intercept the `requestApplyInsets` call via the deprecated + * method. + */ + @Deprecated("Deprecated in Java") + override fun requestFitSystemWindows() { + dispatchApplyWindowInsets(currentWindowInsets.toWindowInsets()!!) + } + }.apply { + addView( + ComposeView(context).apply { + setContent { + currentContentUnderTest() + } + }, + ) + } + }, + ) +} diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt new file mode 100644 index 000000000..b9970effd --- /dev/null +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt @@ -0,0 +1,349 @@ +/* + * 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.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.windowInsetsEndWidth +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.windowInsetsStartWidth +import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.material3.SnackbarDuration.Indefinite +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.Posture +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toAndroidRect +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.DeviceConfigurationOverride +import androidx.compose.ui.test.ForcedSize +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpRect +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.roundToIntRect +import androidx.core.graphics.Insets +import androidx.core.view.WindowInsetsCompat +import androidx.window.core.layout.WindowSizeClass +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode +import java.util.TimeZone +import javax.inject.Inject + +/** + * Tests that the Snackbar is correctly displayed on different screen sizes. + */ +@RunWith(RobolectricTestRunner::class) +@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") +@LooperMode(LooperMode.Mode.PAUSED) +@HiltAndroidTest +class SnackbarInsetsScreenshotTests { + + /** + * Manages the components' state and is used to perform injection on your test + */ + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + /** + * Create a temporary folder used to create a Data Store file. This guarantees that + * the file is removed in between each test, preventing a crash. + */ + @BindValue + @get:Rule(order = 1) + val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() + + /** + * Use a test activity to set the content on. + */ + @get:Rule(order = 2) + val composeTestRule = createAndroidComposeRule() + + @Inject + lateinit var networkMonitor: NetworkMonitor + + @Inject + lateinit var timeZoneMonitor: TimeZoneMonitor + + @Inject + lateinit var userDataRepository: FakeUserDataRepository + + @Inject + lateinit var topicsRepository: TopicsRepository + + @Inject + lateinit var userNewsResourceRepository: UserNewsResourceRepository + + @Before + fun setup() { + hiltRule.inject() + + // Configure user data + runBlocking { + userDataRepository.setShouldHideOnboarding(true) + + userDataRepository.setFollowedTopicIds( + setOf(topicsRepository.getTopics().first().first().id), + ) + } + } + + @Before + fun setTimeZone() { + // Make time zone deterministic in tests + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + @Test + fun phone_noSnackbar() { + val snackbarHostState = SnackbarHostState() + testSnackbarScreenshotWithSize( + snackbarHostState, + 400.dp, + 500.dp, + "insets_snackbar_compact_medium_noSnackbar", + action = { }, + ) + } + + @Test + fun snackbarShown_phone() { + val snackbarHostState = SnackbarHostState() + testSnackbarScreenshotWithSize( + snackbarHostState, + 400.dp, + 500.dp, + "insets_snackbar_compact_medium", + ) { + snackbarHostState.showSnackbar( + "This is a test snackbar message", + actionLabel = "Action Label", + duration = Indefinite, + ) + } + } + + @Test + fun snackbarShown_foldable() { + val snackbarHostState = SnackbarHostState() + testSnackbarScreenshotWithSize( + snackbarHostState, + 600.dp, + 600.dp, + "insets_snackbar_medium_medium", + ) { + snackbarHostState.showSnackbar( + "This is a test snackbar message", + actionLabel = "Action Label", + duration = Indefinite, + ) + } + } + + @Test + fun snackbarShown_tablet() { + val snackbarHostState = SnackbarHostState() + testSnackbarScreenshotWithSize( + snackbarHostState, + 900.dp, + 900.dp, + "insets_snackbar_expanded_expanded", + ) { + snackbarHostState.showSnackbar( + "This is a test snackbar message", + actionLabel = "Action Label", + duration = Indefinite, + ) + } + } + + @OptIn(ExperimentalMaterial3AdaptiveApi::class) + private fun testSnackbarScreenshotWithSize( + snackbarHostState: SnackbarHostState, + width: Dp, + height: Dp, + screenshotName: String, + action: suspend () -> Unit, + ) { + lateinit var scope: CoroutineScope + composeTestRule.setContent { + CompositionLocalProvider( + // Replaces images with placeholders + LocalInspectionMode provides true, + ) { + scope = rememberCoroutineScope() + + DeviceConfigurationOverride( + DeviceConfigurationOverride.ForcedSize(DpSize(width, height)), + ) { + DeviceConfigurationOverride( + DeviceConfigurationOverride.WindowInsets( + WindowInsetsCompat.Builder() + .setInsets( + WindowInsetsCompat.Type.statusBars(), + DpRect( + left = 0.dp, + top = 64.dp, + right = 0.dp, + bottom = 0.dp, + ).toInsets(), + ) + .setInsets( + WindowInsetsCompat.Type.navigationBars(), + DpRect( + left = 64.dp, + top = 0.dp, + right = 64.dp, + bottom = 64.dp, + ).toInsets(), + ) + .build(), + ), + ) { + BoxWithConstraints(Modifier.testTag("root")) { + NiaTheme { + val appState = rememberNiaAppState( + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, + ) + NiaApp( + appState = appState, + snackbarHostState = snackbarHostState, + showSettingsDialog = false, + onSettingsDismissed = {}, + onTopAppBarActionClick = {}, + windowAdaptiveInfo = WindowAdaptiveInfo( + windowSizeClass = WindowSizeClass.compute( + maxWidth.value, + maxHeight.value, + ), + windowPosture = Posture(), + ), + ) + DebugVisibleWindowInsets() + } + } + } + } + } + } + + scope.launch { + action() + } + + composeTestRule.onNodeWithTag("root") + .captureRoboImage( + "src/testDemo/screenshots/$screenshotName.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} + +@Composable +fun DebugVisibleWindowInsets( + modifier: Modifier = Modifier, + debugColor: Color = Color.Magenta.copy(alpha = 0.5f), +) { + Box(modifier = modifier.fillMaxSize()) { + Spacer( + modifier = Modifier + .align(Alignment.CenterStart) + .fillMaxHeight() + .windowInsetsStartWidth(WindowInsets.safeDrawing) + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical)) + .background(debugColor), + ) + Spacer( + modifier = Modifier + .align(Alignment.CenterEnd) + .fillMaxHeight() + .windowInsetsEndWidth(WindowInsets.safeDrawing) + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical)) + .background(debugColor), + ) + Spacer( + modifier = Modifier + .align(Alignment.TopCenter) + .fillMaxWidth() + .windowInsetsTopHeight(WindowInsets.safeDrawing) + .background(debugColor), + ) + Spacer( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .windowInsetsBottomHeight(WindowInsets.safeDrawing) + .background(debugColor), + ) + } +} + +@Composable +private fun DpRect.toInsets() = toInsets(LocalDensity.current) + +private fun DpRect.toInsets(density: Density) = + Insets.of(with(density) { toRect() }.roundToIntRect().toAndroidRect()) diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png new file mode 100644 index 000000000..aae785a47 Binary files /dev/null and b/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png new file mode 100644 index 000000000..d37f02c65 Binary files /dev/null and b/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png b/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png new file mode 100644 index 000000000..3d2c79256 Binary files /dev/null and b/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png b/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png new file mode 100644 index 000000000..3e7171bf4 Binary files /dev/null and b/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png differ diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index dc478a829..c735fa4a0 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -89,9 +89,9 @@ gradlePlugin { id = "nowinandroid.android.test" implementationClass = "AndroidTestConventionPlugin" } - register("androidHilt") { - id = "nowinandroid.android.hilt" - implementationClass = "AndroidHiltConventionPlugin" + register("hilt") { + id = "nowinandroid.hilt" + implementationClass = "HiltConventionPlugin" } register("androidRoom") { id = "nowinandroid.android.room" diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 9583ece7d..9110e7fa3 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -26,14 +26,10 @@ class AndroidFeatureConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply { - apply(libs.findPlugin("nowinandroid.android.library").get().get().pluginId) - apply(libs.findPlugin("nowinandroid.android.hilt").get().get().pluginId) + apply("nowinandroid.android.library") + apply("nowinandroid.hilt") } extensions.configure { - defaultConfig { - testInstrumentationRunner = - "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" - } testOptions.animationsDisabled = true configureGradleManagedDevices(this) } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index d5b48ffa4..185d22c02 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -40,6 +40,7 @@ class AndroidLibraryConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) defaultConfig.targetSdk = 34 + defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testOptions.animationsDisabled = true configureFlavors(this) configureGradleManagedDevices(this) @@ -52,6 +53,7 @@ class AndroidLibraryConventionPlugin : Plugin { disableUnnecessaryAndroidTests(target) } dependencies { + add("androidTestImplementation", kotlin("test")) add("testImplementation", kotlin("test")) add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) diff --git a/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt b/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt similarity index 55% rename from build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt rename to build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt index 617ff9ff2..a8228e5af 100644 --- a/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * 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. @@ -14,25 +14,28 @@ * limitations under the License. */ +import com.android.build.gradle.api.AndroidBasePlugin import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.dependencies -class AndroidHiltConventionPlugin : Plugin { +class HiltConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { - with(pluginManager) { - apply(libs.findPlugin("ksp").get().get().pluginId) - apply(libs.findPlugin("hilt-plugin").get().get().pluginId) - } - + pluginManager.apply("com.google.devtools.ksp") dependencies { - "implementation"(libs.findLibrary("hilt.android").get()) - "ksp"(libs.findLibrary("hilt.compiler").get()) + add("ksp", libs.findLibrary("hilt.compiler").get()) + add("implementation", libs.findLibrary("hilt.core").get()) } + /** Add support for Android modules, based on [AndroidBasePlugin] */ + pluginManager.withPlugin("com.android.base") { + pluginManager.apply("dagger.hilt.android.plugin") + dependencies { + add("implementation", libs.findLibrary("hilt.android").get()) + } + } } } - } 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 c59d3ffb8..4447b8602 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 @@ -16,10 +16,10 @@ package com.google.samples.apps.nowinandroid +import com.android.SdkConstants 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.Project @@ -36,6 +36,7 @@ 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.assign import org.gradle.kotlin.dsl.register import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.process.ExecOperations @@ -117,23 +118,20 @@ fun Project.configureBadgingTasks( val generateBadgingTaskName = "generate${capitalizedVariantName}Badging" val generateBadging = tasks.register(generateBadgingTaskName) { - apk.set( - variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE), - ) - aapt2Executable.set( - File( - baseExtension.sdkDirectory, - "${SdkConstants.FD_BUILD_TOOLS}/" + - "${baseExtension.buildToolsVersion}/" + - SdkConstants.FN_AAPT2, - ), + apk = variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE) + + aapt2Executable = File( + baseExtension.sdkDirectory, + "${SdkConstants.FD_BUILD_TOOLS}/" + + "${baseExtension.buildToolsVersion}/" + + SdkConstants.FN_AAPT2, ) - badging.set( - project.layout.buildDirectory.file( - "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", - ), + + badging = project.layout.buildDirectory.file( + "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", ) + } val updateBadgingTaskName = "update${capitalizedVariantName}Badging" @@ -144,17 +142,14 @@ fun Project.configureBadgingTasks( val checkBadgingTaskName = "check${capitalizedVariantName}Badging" tasks.register(checkBadgingTaskName) { - goldenBadging.set( - project.layout.projectDirectory.file("${variant.name}-badging.txt"), - ) - generatedBadging.set( - generateBadging.get().badging, - ) - this.updateBadgingTaskName.set(updateBadgingTaskName) + goldenBadging = project.layout.projectDirectory.file("${variant.name}-badging.txt") + + generatedBadging = generateBadging.get().badging + + this.updateBadgingTaskName = updateBadgingTaskName + + output = project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName") - output.set( - project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName"), - ) } } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt index 7820a978e..972d539c6 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt @@ -24,6 +24,7 @@ import org.gradle.api.file.Directory import org.gradle.api.file.RegularFile import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.assign import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.withType @@ -66,9 +67,13 @@ internal fun Project.configureJacoco( val myObjFactory = project.objects val buildDir = layout.buildDirectory.get().asFile val allJars: ListProperty = myObjFactory.listProperty(RegularFile::class.java) - val allDirectories: ListProperty = myObjFactory.listProperty(Directory::class.java) + val allDirectories: ListProperty = + myObjFactory.listProperty(Directory::class.java) val reportTask = - tasks.register("create${variant.name.capitalize()}CombinedCoverageReport", JacocoReport::class) { + tasks.register( + "create${variant.name.capitalize()}CombinedCoverageReport", + JacocoReport::class, + ) { classDirectories.setFrom( allJars, @@ -76,23 +81,28 @@ internal fun Project.configureJacoco( dirs.map { dir -> myObjFactory.fileTree().setDir(dir).exclude(coverageExclusions) } - } + }, ) reports { - xml.required.set(true) - html.required.set(true) + xml.required = true + html.required = true } // TODO: This is missing files in src/debug/, src/prod, src/demo, src/demoDebug... - sourceDirectories.setFrom(files("$projectDir/src/main/java", "$projectDir/src/main/kotlin")) + sourceDirectories.setFrom( + files( + "$projectDir/src/main/java", + "$projectDir/src/main/kotlin", + ), + ) executionData.setFrom( project.fileTree("$buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest") .matching { include("**/*.exec") }, project.fileTree("$buildDir/outputs/code_coverage/${variant.name}AndroidTest") - .matching { include("**/*.ec") } - ) + .matching { include("**/*.ec") }, + ) } 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 8e88f5a53..271fc51b7 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 @@ -33,6 +33,7 @@ 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.kotlin.dsl.assign import org.gradle.work.DisableCachingByDefault import java.io.File @@ -53,12 +54,12 @@ internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtensio if (artifact != null && testSources != null) { tasks.register( "${variant.name}PrintTestApk", - PrintApkLocationTask::class.java + PrintApkLocationTask::class.java, ) { - apkFolder.set(artifact) - builtArtifactsLoader.set(loader) - variantName.set(variant.name) - sources.set(testSources) + apkFolder = artifact + builtArtifactsLoader = loader + variantName = variant.name + sources = testSources } } } @@ -100,4 +101,4 @@ internal abstract class PrintApkLocationTask : DefaultTask() { val apk = File(builtArtifacts.elements.single().outputFile).toPath() println(apk) } -} \ No newline at end of file +} diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts index 023574e6f..72f7620b0 100644 --- a/core/analytics/build.gradle.kts +++ b/core/analytics/build.gradle.kts @@ -16,7 +16,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.compose) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { 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 111130a79..41b035875 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 @@ -16,9 +16,9 @@ package com.google.samples.apps.nowinandroid.core.analytics +import com.google.firebase.Firebase import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.ktx.Firebase +import com.google.firebase.analytics.analytics import dagger.Binds import dagger.Module import dagger.Provides @@ -35,8 +35,6 @@ internal abstract class AnalyticsModule { companion object { @Provides @Singleton - fun provideFirebaseAnalytics(): FirebaseAnalytics { - return Firebase.analytics - } + fun provideFirebaseAnalytics(): FirebaseAnalytics = Firebase.analytics } } diff --git a/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt b/core/analytics/src/prod/kotlin/com/google/samples/apps/nowinandroid/core/analytics/FirebaseAnalyticsHelper.kt index 5a4b7f362..cedab6732 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 @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.core.analytics import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.logEvent +import com.google.firebase.analytics.logEvent import javax.inject.Inject /** diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 51ae627dc..f1aa9771c 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -14,16 +14,12 @@ * limitations under the License. */ plugins { - alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) -} - -android { - namespace = "com.google.samples.apps.nowinandroid.core.common" + alias(libs.plugins.nowinandroid.jvm.library) + alias(libs.plugins.nowinandroid.hilt) } dependencies { + implementation(libs.kotlinx.coroutines.core) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.turbine) } \ No newline at end of file diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml deleted file mode 100644 index 51d0cfc2e..000000000 --- a/core/common/src/main/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - \ No newline at end of file diff --git a/core/data-test/build.gradle.kts b/core/data-test/build.gradle.kts index b166df288..420c34a57 100644 --- a/core/data-test/build.gradle.kts +++ b/core/data-test/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 142637ff9..83c268aab 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -16,7 +16,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) id("kotlinx-serialization") } diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 4a6bcb66a..8bab355b4 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -17,15 +17,11 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) alias(libs.plugins.nowinandroid.android.room) + alias(libs.plugins.nowinandroid.hilt) } android { - defaultConfig { - testInstrumentationRunner = - "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" - } namespace = "com.google.samples.apps.nowinandroid.core.database" } @@ -34,5 +30,7 @@ dependencies { implementation(libs.kotlinx.datetime) - androidTestImplementation(projects.core.testing) + androidTestImplementation(libs.androidx.test.core) + androidTestImplementation(libs.androidx.test.runner) + androidTestImplementation(libs.kotlinx.coroutines.test) } diff --git a/core/datastore-test/build.gradle.kts b/core/datastore-test/build.gradle.kts index 53e5e2c0c..375b1d3d8 100644 --- a/core/datastore-test/build.gradle.kts +++ b/core/datastore-test/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index 34ea5ee78..b17bf6abd 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -17,7 +17,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { @@ -33,7 +33,7 @@ android { } dependencies { - api(libs.androidx.dataStore.core) + api(libs.androidx.dataStore) api(projects.core.datastoreProto) api(projects.core.model) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index bbb4ab97b..31635865c 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -46,10 +46,7 @@ dependencies { testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) - testImplementation(libs.roborazzi) testImplementation(projects.core.screenshotTesting) - testImplementation(projects.core.testing) - androidTestImplementation(libs.androidx.compose.ui.test) - androidTestImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 689a99e73..d12482a56 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -17,7 +17,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) id("kotlinx-serialization") id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") } diff --git a/core/notifications/build.gradle.kts b/core/notifications/build.gradle.kts index 92871b72b..34393049b 100644 --- a/core/notifications/build.gradle.kts +++ b/core/notifications/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { @@ -28,5 +28,4 @@ dependencies { implementation(projects.core.common) compileOnly(platform(libs.androidx.compose.bom)) - compileOnly(libs.androidx.compose.runtime) } 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 1c9e7ab63..5da88102a 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 @@ -65,9 +65,7 @@ internal class SystemTrayNotifier @Inject constructor( val newsNotifications = truncatedNewsResources.map { newsResource -> createNewsNotification { - setSmallIcon( - com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, - ) + setSmallIcon(R.drawable.core_notifications_ic_nia_notification) .setContentTitle(newsResource.title) .setContentText(newsResource.content) .setContentIntent(newsPendingIntent(newsResource)) @@ -82,9 +80,7 @@ internal class SystemTrayNotifier @Inject constructor( ) setContentTitle(title) .setContentText(title) - .setSmallIcon( - com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, - ) + .setSmallIcon(R.drawable.core_notifications_ic_nia_notification) // Build summary info into InboxStyle template. .setStyle(newsNotificationStyle(truncatedNewsResources, title)) .setGroup(NEWS_NOTIFICATION_GROUP) diff --git a/core/common/src/main/res/drawable-anydpi-v24/core_common_ic_nia_notification.xml b/core/notifications/src/main/res/drawable-anydpi-v24/core_notifications_ic_nia_notification.xml similarity index 100% rename from core/common/src/main/res/drawable-anydpi-v24/core_common_ic_nia_notification.xml rename to core/notifications/src/main/res/drawable-anydpi-v24/core_notifications_ic_nia_notification.xml diff --git a/core/common/src/main/res/drawable-hdpi/core_common_ic_nia_notification.png b/core/notifications/src/main/res/drawable-hdpi/core_notifications_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-hdpi/core_common_ic_nia_notification.png rename to core/notifications/src/main/res/drawable-hdpi/core_notifications_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-mdpi/core_common_ic_nia_notification.png b/core/notifications/src/main/res/drawable-mdpi/core_notifications_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-mdpi/core_common_ic_nia_notification.png rename to core/notifications/src/main/res/drawable-mdpi/core_notifications_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-xhdpi/core_common_ic_nia_notification.png b/core/notifications/src/main/res/drawable-xhdpi/core_notifications_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/core_common_ic_nia_notification.png rename to core/notifications/src/main/res/drawable-xhdpi/core_notifications_ic_nia_notification.png diff --git a/core/common/src/main/res/drawable-xxhdpi/core_common_ic_nia_notification.png b/core/notifications/src/main/res/drawable-xxhdpi/core_notifications_ic_nia_notification.png similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/core_common_ic_nia_notification.png rename to core/notifications/src/main/res/drawable-xxhdpi/core_notifications_ic_nia_notification.png diff --git a/core/screenshot-testing/build.gradle.kts b/core/screenshot-testing/build.gradle.kts index fb23cf057..794416ba9 100644 --- a/core/screenshot-testing/build.gradle.kts +++ b/core/screenshot-testing/build.gradle.kts @@ -16,7 +16,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.compose) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { @@ -24,11 +24,10 @@ android { } dependencies { + api(libs.bundles.androidx.compose.ui.test) api(libs.roborazzi) implementation(libs.androidx.compose.ui.test) implementation(libs.androidx.activity.compose) - implementation(libs.androidx.compose.ui.test) implementation(libs.robolectric) - implementation(projects.core.common) implementation(projects.core.designsystem) } diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts index 02729ceff..01696d5e8 100644 --- a/core/testing/build.gradle.kts +++ b/core/testing/build.gradle.kts @@ -15,8 +15,7 @@ */ plugins { alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.compose) - alias(libs.plugins.nowinandroid.android.hilt) + alias(libs.plugins.nowinandroid.hilt) } android { @@ -24,19 +23,15 @@ android { } dependencies { - api(kotlin("test")) - api(libs.androidx.compose.ui.test) + api(libs.kotlinx.coroutines.test) api(projects.core.analytics) + api(projects.core.common) api(projects.core.data) api(projects.core.model) api(projects.core.notifications) - debugApi(libs.androidx.compose.ui.testManifest) implementation(libs.androidx.test.rules) implementation(libs.hilt.android.testing) - implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.datetime) - implementation(projects.core.common) - implementation(projects.core.designsystem) } diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 5d8a65d44..5606cb5d1 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -20,9 +20,6 @@ plugins { } android { - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } namespace = "com.google.samples.apps.nowinandroid.core.ui" } @@ -36,5 +33,6 @@ dependencies { implementation(libs.coil.kt) implementation(libs.coil.kt.compose) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/docs/images/graphs/dep_graph_app.svg b/docs/images/graphs/dep_graph_app.svg index 8a94310b2..8e5d9d429 100644 --- a/docs/images/graphs/dep_graph_app.svg +++ b/docs/images/graphs/dep_graph_app.svg @@ -1,383 +1,305 @@ - - - - - G - - - :app - - :app + + + + + + :app + + + + :feature:interests + + + + + + + + :feature:foryou + + + + + + + + :feature:bookmarks + + + + + + + + :feature:topic + + + + + + + + :feature:search + + + + + + + + :feature:settings + + + + + + + + :core:common + + + + + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + :core:model + + + + + + + + :core:analytics + + + + + + + + :sync:work + + + + + + + + + + + + + + + + + + + + :core:domain + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :feature:interests - - :feature:interests - - - :app->:feature:interests - - - - - :feature:foryou - - :feature:foryou - - - :app->:feature:foryou - - - - - :feature:bookmarks - - :feature:bookmarks - - - :app->:feature:bookmarks - - - - - :feature:topic - - :feature:topic - - - :app->:feature:topic - - - - - :feature:search - - :feature:search - - - :app->:feature:search - - - - - :feature:settings - - :feature:settings - - - :app->:feature:settings - - - - - :core:common - - :core:common - - - :app->:core:common - - - - - :core:ui - - :core:ui - - - :app->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :app->:core:designsystem - - - - - :core:data - - :core:data - - - :app->:core:data - - - - - :core:model - - :core:model - - - :app->:core:model - - - - - :core:analytics - - :core:analytics - - - :app->:core:analytics - - - - - :sync:work - - :sync:work - - - :app->:sync:work - - - - - :feature:interests->:core:ui - - - - - :feature:interests->:core:designsystem - - - - - :feature:interests->:core:data - - - - - :core:domain - - :core:domain - - - :feature:interests->:core:domain - - - - - :feature:foryou->:core:ui - - - - - :feature:foryou->:core:designsystem - - - - - :feature:foryou->:core:data - - - - - :feature:foryou->:core:domain - - - - - :feature:bookmarks->:core:ui - - - - - :feature:bookmarks->:core:designsystem - - - - - :feature:bookmarks->:core:data - - - - - :feature:topic->:core:ui - - - - - :feature:topic->:core:designsystem - - - - - :feature:topic->:core:data - - - - - :feature:search->:core:ui - - - - - :feature:search->:core:designsystem - - - - - :feature:search->:core:data - - - - - :feature:search->:core:domain - - - - - :feature:settings->:core:ui - - - - - :feature:settings->:core:designsystem - - - - - :feature:settings->:core:data - - - - - :core:ui->:core:designsystem - - - - - :core:ui->:core:model - - - - - :core:ui->:core:analytics - - - - - :core:data->:core:common - - - - - :core:data->:core:analytics - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :sync:work->:core:data - - - - - :sync:work->:core:analytics - - - - - :core:domain->:core:data - - - - - :core:domain->:core:model - - - - - :core:database->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore->:core:model - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:common - - - - - :core:network->:core:model - - - - - :core:notifications->:core:common - - - - - :core:notifications->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_app_nia_catalog.svg b/docs/images/graphs/dep_graph_app_nia_catalog.svg index fa7cfca5a..151ee63ad 100644 --- a/docs/images/graphs/dep_graph_app_nia_catalog.svg +++ b/docs/images/graphs/dep_graph_app_nia_catalog.svg @@ -1,58 +1,45 @@ - - - - - G - - - :app-nia-catalog - - :app-nia-catalog + + + + + + :app-nia-catalog + + + + :core:designsystem + + + + + + + + :core:ui + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + - - :core:designsystem - - :core:designsystem - - - :app-nia-catalog->:core:designsystem - - - - - :core:ui - - :core:ui - - - :app-nia-catalog->:core:ui - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_analytics.svg b/docs/images/graphs/dep_graph_core_analytics.svg index 9cb11e4eb..45f1c1eb0 100644 --- a/docs/images/graphs/dep_graph_core_analytics.svg +++ b/docs/images/graphs/dep_graph_core_analytics.svg @@ -1,13 +1,9 @@ - - - - - G - - - :core:analytics - - :core:analytics + + + + + + :core:analytics + - diff --git a/docs/images/graphs/dep_graph_core_common.svg b/docs/images/graphs/dep_graph_core_common.svg index a1cdcb610..91033eaa0 100644 --- a/docs/images/graphs/dep_graph_core_common.svg +++ b/docs/images/graphs/dep_graph_core_common.svg @@ -1,13 +1,9 @@ - - - - - G - - - :core:common - - :core:common + + + + + + :core:common + - diff --git a/docs/images/graphs/dep_graph_core_data.svg b/docs/images/graphs/dep_graph_core_data.svg index 8637b06ee..ab91bafb2 100644 --- a/docs/images/graphs/dep_graph_core_data.svg +++ b/docs/images/graphs/dep_graph_core_data.svg @@ -1,123 +1,97 @@ - - - - - G - - - :core:data - - :core:data + + + + + + :core:data + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:analytics + + + + + + + + :core:notifications + + + + + + + + :core:model + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:analytics - - :core:analytics - - - :core:data->:core:analytics - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:model - - :core:model - - - :core:database->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore->:core:model - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:common - - - - - :core:network->:core:model - - - - - :core:notifications->:core:common - - - - - :core:notifications->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_data_test.svg b/docs/images/graphs/dep_graph_core_data_test.svg index 798696c11..b9736c859 100644 --- a/docs/images/graphs/dep_graph_core_data_test.svg +++ b/docs/images/graphs/dep_graph_core_data_test.svg @@ -1,133 +1,105 @@ - - - - - G - - - :core:data-test - - :core:data-test + + + + + + :core:data-test + + + + :core:data + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:analytics + + + + + + + + :core:notifications + + + + + + + + :core:model + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:data - - :core:data - - - :core:data-test->:core:data - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:analytics - - :core:analytics - - - :core:data->:core:analytics - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:model - - :core:model - - - :core:database->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore->:core:model - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:common - - - - - :core:network->:core:model - - - - - :core:notifications->:core:common - - - - - :core:notifications->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_database.svg b/docs/images/graphs/dep_graph_core_database.svg index dd3aef7ee..e82d46436 100644 --- a/docs/images/graphs/dep_graph_core_database.svg +++ b/docs/images/graphs/dep_graph_core_database.svg @@ -1,23 +1,17 @@ - - - - - G - - - :core:database - - :core:database + + + + + + :core:database + + + + :core:model + + + + + - - :core:model - - :core:model - - - :core:database->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_datastore.svg b/docs/images/graphs/dep_graph_core_datastore.svg index 3bf13d087..f7502e55b 100644 --- a/docs/images/graphs/dep_graph_core_datastore.svg +++ b/docs/images/graphs/dep_graph_core_datastore.svg @@ -1,43 +1,33 @@ - - - - - G - - - :core:datastore - - :core:datastore + + + + + + :core:datastore + + + + :core:datastore-proto + + + + + + + + :core:model + + + + + + + + :core:common + + + + + - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:model - - :core:model - - - :core:datastore->:core:model - - - - - :core:common - - :core:common - - - :core:datastore->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_core_datastore_proto.svg b/docs/images/graphs/dep_graph_core_datastore_proto.svg index fd3bba9b6..7fcfb8358 100644 --- a/docs/images/graphs/dep_graph_core_datastore_proto.svg +++ b/docs/images/graphs/dep_graph_core_datastore_proto.svg @@ -1,13 +1,9 @@ - - - - - G - - - :core:datastore-proto - - :core:datastore-proto + + + + + + :core:datastore-proto + - diff --git a/docs/images/graphs/dep_graph_core_datastore_test.svg b/docs/images/graphs/dep_graph_core_datastore_test.svg index e42e5e795..37521a05f 100644 --- a/docs/images/graphs/dep_graph_core_datastore_test.svg +++ b/docs/images/graphs/dep_graph_core_datastore_test.svg @@ -1,58 +1,45 @@ - - - - - G - - - :core:datastore-test - - :core:datastore-test + + + + + + :core:datastore-test + + + + :core:common + + + + + + + + :core:datastore + + + + + + + + + + + + :core:datastore-proto + + + + + + + + :core:model + + + + + - - :core:common - - :core:common - - - :core:datastore-test->:core:common - - - - - :core:datastore - - :core:datastore - - - :core:datastore-test->:core:datastore - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:model - - :core:model - - - :core:datastore->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_designsystem.svg b/docs/images/graphs/dep_graph_core_designsystem.svg index f33d70705..737140876 100644 --- a/docs/images/graphs/dep_graph_core_designsystem.svg +++ b/docs/images/graphs/dep_graph_core_designsystem.svg @@ -1,13 +1,9 @@ - - - - - G - - - :core:designsystem - - :core:designsystem + + + + + + :core:designsystem + - diff --git a/docs/images/graphs/dep_graph_core_domain.svg b/docs/images/graphs/dep_graph_core_domain.svg index 68543466c..fe3740d2f 100644 --- a/docs/images/graphs/dep_graph_core_domain.svg +++ b/docs/images/graphs/dep_graph_core_domain.svg @@ -1,138 +1,109 @@ - - - - - G - - - :core:domain - - :core:domain + + + + + + :core:domain + + + + :core:data + + + + + + + + :core:model + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:analytics + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:data - - :core:data - - - :core:domain->:core:data - - - - - :core:model - - :core:model - - - :core:domain->:core:model - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:analytics - - :core:analytics - - - :core:data->:core:analytics - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_core_model.svg b/docs/images/graphs/dep_graph_core_model.svg index 66959f2f1..125684a08 100644 --- a/docs/images/graphs/dep_graph_core_model.svg +++ b/docs/images/graphs/dep_graph_core_model.svg @@ -1,13 +1,9 @@ - - - - - G - - - :core:model - - :core:model + + + + + + :core:model + - diff --git a/docs/images/graphs/dep_graph_core_network.svg b/docs/images/graphs/dep_graph_core_network.svg index 9902ee8e3..3022a86ee 100644 --- a/docs/images/graphs/dep_graph_core_network.svg +++ b/docs/images/graphs/dep_graph_core_network.svg @@ -1,33 +1,25 @@ - - - - - G - - - :core:network - - :core:network + + + + + + :core:network + + + + :core:common + + + + + + + + :core:model + + + + + - - :core:common - - :core:common - - - :core:network->:core:common - - - - - :core:model - - :core:model - - - :core:network->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_core_notifications.svg b/docs/images/graphs/dep_graph_core_notifications.svg index 3a1126ca8..d96d28769 100644 --- a/docs/images/graphs/dep_graph_core_notifications.svg +++ b/docs/images/graphs/dep_graph_core_notifications.svg @@ -1,33 +1,25 @@ - - - - - G - - - :core:notifications - - :core:notifications + + + + + + :core:notifications + + + + :core:model + + + + + + + + :core:common + + + + + - - :core:model - - :core:model - - - :core:notifications->:core:model - - - - - :core:common - - :core:common - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_core_screenshot_testing.svg b/docs/images/graphs/dep_graph_core_screenshot_testing.svg index 8021dcda6..a7d58b0ea 100644 --- a/docs/images/graphs/dep_graph_core_screenshot_testing.svg +++ b/docs/images/graphs/dep_graph_core_screenshot_testing.svg @@ -1,33 +1,17 @@ - - - - - G - - - :core:screenshot-testing - - :core:screenshot-testing + + + + + + :core:screenshot-testing + + + + :core:designsystem + + + + + - - :core:common - - :core:common - - - :core:screenshot-testing->:core:common - - - - - :core:designsystem - - :core:designsystem - - - :core:screenshot-testing->:core:designsystem - - - - diff --git a/docs/images/graphs/dep_graph_core_testing.svg b/docs/images/graphs/dep_graph_core_testing.svg index ac1b301aa..d441858e5 100644 --- a/docs/images/graphs/dep_graph_core_testing.svg +++ b/docs/images/graphs/dep_graph_core_testing.svg @@ -1,163 +1,121 @@ - - - - - G - - - :core:testing - - :core:testing + + + + + + :core:testing + + + + :core:analytics + + + + + + + + :core:common + + + + + + + + :core:data + + + + + + + + :core:model + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + - - :core:analytics - - :core:analytics - - - :core:testing->:core:analytics - - - - - :core:data - - :core:data - - - :core:testing->:core:data - - - - - :core:model - - :core:model - - - :core:testing->:core:model - - - - - :core:notifications - - :core:notifications - - - :core:testing->:core:notifications - - - - - :core:common - - :core:common - - - :core:testing->:core:common - - - - - :core:designsystem - - :core:designsystem - - - :core:testing->:core:designsystem - - - - - :core:data->:core:analytics - - - - - :core:data->:core:notifications - - - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_core_ui.svg b/docs/images/graphs/dep_graph_core_ui.svg index a36dd7031..2cd972357 100644 --- a/docs/images/graphs/dep_graph_core_ui.svg +++ b/docs/images/graphs/dep_graph_core_ui.svg @@ -1,43 +1,33 @@ - - - - - G - - - :core:ui - - :core:ui + + + + + + :core:ui + + + + :core:analytics + + + + + + + + :core:designsystem + + + + + + + + :core:model + + + + + - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:designsystem - - :core:designsystem - - - :core:ui->:core:designsystem - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_feature_bookmarks.svg b/docs/images/graphs/dep_graph_feature_bookmarks.svg index 8fbe423c2..0391eb39c 100644 --- a/docs/images/graphs/dep_graph_feature_bookmarks.svg +++ b/docs/images/graphs/dep_graph_feature_bookmarks.svg @@ -1,168 +1,133 @@ - - - - - G - - - :feature:bookmarks - - :feature:bookmarks + + + + + + :feature:bookmarks + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:bookmarks->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:bookmarks->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:bookmarks->:core:data - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_feature_foryou.svg b/docs/images/graphs/dep_graph_feature_foryou.svg index 6b8af3764..63a154a87 100644 --- a/docs/images/graphs/dep_graph_feature_foryou.svg +++ b/docs/images/graphs/dep_graph_feature_foryou.svg @@ -1,188 +1,149 @@ - - - - - G - - - :feature:foryou - - :feature:foryou + + + + + + :feature:foryou + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + :core:domain + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:foryou->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:foryou->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:foryou->:core:data - - - - - :core:domain - - :core:domain - - - :feature:foryou->:core:domain - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:domain->:core:data - - - - - :core:domain->:core:model - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_feature_interests.svg b/docs/images/graphs/dep_graph_feature_interests.svg index b13783a27..2de1fc61f 100644 --- a/docs/images/graphs/dep_graph_feature_interests.svg +++ b/docs/images/graphs/dep_graph_feature_interests.svg @@ -1,188 +1,149 @@ - - - - - G - - - :feature:interests - - :feature:interests + + + + + + :feature:interests + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + :core:domain + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:interests->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:interests->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:interests->:core:data - - - - - :core:domain - - :core:domain - - - :feature:interests->:core:domain - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:domain->:core:data - - - - - :core:domain->:core:model - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_feature_search.svg b/docs/images/graphs/dep_graph_feature_search.svg index ff299f33f..7f8f29777 100644 --- a/docs/images/graphs/dep_graph_feature_search.svg +++ b/docs/images/graphs/dep_graph_feature_search.svg @@ -1,188 +1,149 @@ - - - - - G - - - :feature:search - - :feature:search + + + + + + :feature:search + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + :core:domain + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:search->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:search->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:search->:core:data - - - - - :core:domain - - :core:domain - - - :feature:search->:core:domain - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:domain->:core:data - - - - - :core:domain->:core:model - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_feature_settings.svg b/docs/images/graphs/dep_graph_feature_settings.svg index 1b9648d8b..3f0d35df2 100644 --- a/docs/images/graphs/dep_graph_feature_settings.svg +++ b/docs/images/graphs/dep_graph_feature_settings.svg @@ -1,168 +1,133 @@ - - - - - G - - - :feature:settings - - :feature:settings + + + + + + :feature:settings + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:settings->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:settings->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:settings->:core:data - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_feature_topic.svg b/docs/images/graphs/dep_graph_feature_topic.svg index 49fe361a8..b7c7dd26c 100644 --- a/docs/images/graphs/dep_graph_feature_topic.svg +++ b/docs/images/graphs/dep_graph_feature_topic.svg @@ -1,168 +1,133 @@ - - - - - G - - - :feature:topic - - :feature:topic + + + + + + :feature:topic + + + + :core:ui + + + + + + + + :core:designsystem + + + + + + + + :core:data + + + + + + + + + + + + :core:analytics + + + + + + + + :core:model + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:ui - - :core:ui - - - :feature:topic->:core:ui - - - - - :core:designsystem - - :core:designsystem - - - :feature:topic->:core:designsystem - - - - - :core:data - - :core:data - - - :feature:topic->:core:data - - - - - :core:ui->:core:designsystem - - - - - :core:analytics - - :core:analytics - - - :core:ui->:core:analytics - - - - - :core:model - - :core:model - - - :core:ui->:core:model - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:database->:core:model - - - - - :core:datastore->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:model - - - - - :core:network->:core:common - - - - - :core:notifications->:core:model - - - - - :core:notifications->:core:common - - - - diff --git a/docs/images/graphs/dep_graph_sync_sync_test.svg b/docs/images/graphs/dep_graph_sync_sync_test.svg index 58a31af19..7a083ba54 100644 --- a/docs/images/graphs/dep_graph_sync_sync_test.svg +++ b/docs/images/graphs/dep_graph_sync_sync_test.svg @@ -1,153 +1,121 @@ - - - - - G - - - :sync:sync-test - - :sync:sync-test + + + + + + :sync:sync-test + + + + :core:data + + + + + + + + :sync:work + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:analytics + + + + + + + + :core:notifications + + + + + + + + + + + + + + + + :core:model + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:data - - :core:data - - - :sync:sync-test->:core:data - - - - - :sync:work - - :sync:work - - - :sync:sync-test->:sync:work - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:analytics - - :core:analytics - - - :core:data->:core:analytics - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :sync:work->:core:data - - - - - :sync:work->:core:analytics - - - - - :core:model - - :core:model - - - :core:database->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore->:core:model - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:common - - - - - :core:network->:core:model - - - - - :core:notifications->:core:common - - - - - :core:notifications->:core:model - - - - diff --git a/docs/images/graphs/dep_graph_sync_work.svg b/docs/images/graphs/dep_graph_sync_work.svg index 2581a10ae..c649f2397 100644 --- a/docs/images/graphs/dep_graph_sync_work.svg +++ b/docs/images/graphs/dep_graph_sync_work.svg @@ -1,138 +1,109 @@ - - - - - G - - - :sync:work - - :sync:work + + + + + + :sync:work + + + + :core:analytics + + + + + + + + :core:data + + + + + + + + + + + + :core:common + + + + + + + + :core:database + + + + + + + + :core:datastore + + + + + + + + :core:network + + + + + + + + :core:notifications + + + + + + + + :core:model + + + + + + + + + + + + + + + + :core:datastore-proto + + + + + + + + + + + + + + + + + + + + + - - :core:analytics - - :core:analytics - - - :sync:work->:core:analytics - - - - - :core:data - - :core:data - - - :sync:work->:core:data - - - - - :core:data->:core:analytics - - - - - :core:common - - :core:common - - - :core:data->:core:common - - - - - :core:database - - :core:database - - - :core:data->:core:database - - - - - :core:datastore - - :core:datastore - - - :core:data->:core:datastore - - - - - :core:network - - :core:network - - - :core:data->:core:network - - - - - :core:notifications - - :core:notifications - - - :core:data->:core:notifications - - - - - :core:model - - :core:model - - - :core:database->:core:model - - - - - :core:datastore->:core:common - - - - - :core:datastore->:core:model - - - - - :core:datastore-proto - - :core:datastore-proto - - - :core:datastore->:core:datastore-proto - - - - - :core:network->:core:common - - - - - :core:network->:core:model - - - - - :core:notifications->:core:common - - - - - :core:notifications->:core:model - - - - diff --git a/feature/bookmarks/build.gradle.kts b/feature/bookmarks/build.gradle.kts index 4e97176a2..51a15ce7a 100644 --- a/feature/bookmarks/build.gradle.kts +++ b/feature/bookmarks/build.gradle.kts @@ -29,5 +29,6 @@ dependencies { testImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index fd41d9a13..004fe8ad6 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -33,8 +33,8 @@ dependencies { testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) testImplementation(projects.core.testing) - testImplementation(projects.core.screenshotTesting) - testDemoImplementation(libs.roborazzi) + testDemoImplementation(projects.core.screenshotTesting) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index ee6aaf122..ca91ba2c4 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -29,5 +29,6 @@ dependencies { testImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index 98052e9ab..c5f1f6ad0 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { testImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 4b9a72bdd..15d65204d 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -31,5 +31,5 @@ dependencies { testImplementation(projects.core.testing) - androidTestImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) } diff --git a/feature/topic/build.gradle.kts b/feature/topic/build.gradle.kts index d457b2f73..726920af1 100644 --- a/feature/topic/build.gradle.kts +++ b/feature/topic/build.gradle.kts @@ -29,5 +29,6 @@ dependencies { testImplementation(projects.core.testing) + androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) } \ No newline at end of file diff --git a/generateModuleGraphs.sh b/generateModuleGraphs.sh index eacf19eed..3c3583e67 100755 --- a/generateModuleGraphs.sh +++ b/generateModuleGraphs.sh @@ -31,6 +31,14 @@ then exit 1 fi +# Check if the svgo command is available +if ! command -v svgo &> /dev/null +then + echo "The 'svgo' command is not found. This is required to cleanup and compress SVGs." + echo "Installation instructions available at https://github.com/svg/svgo." + exit 1 +fi + # Check for a version of grep which supports Perl regex. # On MacOS the OS installed grep doesn't support Perl regex so check for the existence of the # GNU version instead which is prefixed with 'g' to distinguish it from the OS installed version. @@ -113,12 +121,10 @@ echo "$module_paths" | while read -r module_path; do -Pmodules.graph.output.gv="/tmp/${file_name}.gv" \ -Pmodules.graph.of.module="${module_path}" /-->\x0/g' | grep -zv '^