@ -0,0 +1,25 @@
|
|||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "gradle"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
registries: "*"
|
||||||
|
labels: [ "version update" ]
|
||||||
|
groups:
|
||||||
|
kotlin-ksp:
|
||||||
|
patterns:
|
||||||
|
- "org.jetbrains.kotlin:*"
|
||||||
|
- "org.jetbrains.kotlin.jvm"
|
||||||
|
- "com.google.devtools.ksp"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
registries:
|
||||||
|
maven-google:
|
||||||
|
type: "maven-repository"
|
||||||
|
url: "https://maven.google.com"
|
||||||
|
replaces-base: true
|
@ -1,17 +1,25 @@
|
|||||||
Thanks for submitting a pull request. Please include the following information.
|
**DO NOT CREATE A PULL REQUEST WITHOUT READING THESE INSTRUCTIONS**
|
||||||
|
|
||||||
**What I have done and why**
|
## Instructions
|
||||||
Include a summary of what your pull request contains, and why you have made these changes.
|
Thanks for submitting a pull request. To accept your pull request we need you do a few things:
|
||||||
|
|
||||||
|
**If this is your first pull request**
|
||||||
|
|
||||||
|
- [Sign the contributors license agreement](https://cla.developers.google.com/)
|
||||||
|
|
||||||
|
**Ensure tests pass and code is formatted correctly**
|
||||||
|
|
||||||
Fixes #<issue_number_goes_here>
|
- Run local tests on the `DemoDebug` variant by running `./gradlew testDemoDebug`
|
||||||
|
- Fix code formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
|
||||||
|
|
||||||
**Do tests pass?**
|
**Add a description**
|
||||||
- [ ] Run local tests on `DemoDebug` variant: `./gradlew testDemoDebug`
|
|
||||||
- [ ] Check formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply`
|
|
||||||
|
|
||||||
**Is this your first pull request?**
|
We need to know what you've done and why you've done it. Include a summary of what your pull request contains, and why you have made these changes. Include links to any relevant issues which it fixes.
|
||||||
- [ ] [Sign the CLA](https://cla.developers.google.com/)
|
|
||||||
- [ ] Run `./tools/setup.sh`
|
|
||||||
- [ ] Import the code formatting style as explained in [the setup script](/tools/setup.sh#L40).
|
|
||||||
|
|
||||||
|
[Here's an example](https://github.com/android/nowinandroid/pull/1257).
|
||||||
|
|
||||||
|
**NOW DELETE THIS LINE AND EVERYTHING ABOVE IT**
|
||||||
|
|
||||||
|
**What I have done and why**
|
||||||
|
|
||||||
|
\<add your PR description here\>
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:base", "group:all", ":dependencyDashboard", "schedule:daily"
|
|
||||||
],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchPackageNames": ["org.objenesis:objenesis"],
|
|
||||||
"allowedVersions": "<=2.6"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matchPackageNames": ["com.google.protobuf"],
|
|
||||||
"allowedVersions": "<=0.8.19"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,3 +1,3 @@
|
|||||||
# :app-nia-catalog module
|
# :app-nia-catalog module
|
||||||
|
## Dependency graph
|
||||||
![Dependency graph](../docs/images/graphs/dep_graph_app_nia_catalog.png)
|
![Dependency graph](../docs/images/graphs/dep_graph_app_nia_catalog.svg)
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
androidx.activity:activity-compose:1.8.2
|
||||||
|
androidx.activity:activity-ktx:1.8.2
|
||||||
|
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.arch.core:core-common:2.2.0
|
||||||
|
androidx.arch.core:core-runtime:2.2.0
|
||||||
|
androidx.autofill:autofill:1.0.0
|
||||||
|
androidx.browser:browser:1.8.0
|
||||||
|
androidx.collection:collection-jvm:1.4.0
|
||||||
|
androidx.collection:collection-ktx:1.4.0
|
||||||
|
androidx.collection:collection:1.4.0
|
||||||
|
androidx.compose.animation:animation-android:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation-core-android:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation-core:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-android:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-layout-android:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-layout:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation:1.7.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-android:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive:1.0.0-beta01
|
||||||
|
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-adaptive-navigation-suite:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-android:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3:1.3.0-beta01
|
||||||
|
androidx.compose.material:material-icons-core-android:1.6.3
|
||||||
|
androidx.compose.material:material-icons-core:1.6.3
|
||||||
|
androidx.compose.material:material-icons-extended-android:1.6.3
|
||||||
|
androidx.compose.material:material-icons-extended:1.6.3
|
||||||
|
androidx.compose.material:material-ripple-android:1.7.0-beta01
|
||||||
|
androidx.compose.material:material-ripple:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-android:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-saveable-android:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-saveable:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-geometry-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-geometry:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-graphics-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-graphics:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-text-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-text:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-tooling-preview-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-tooling-preview:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-unit-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-unit:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-util-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-util:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui:1.7.0-beta01
|
||||||
|
androidx.compose:compose-bom:2024.02.02
|
||||||
|
androidx.concurrent:concurrent-futures:1.1.0
|
||||||
|
androidx.core:core-ktx:1.13.1
|
||||||
|
androidx.core:core:1.13.1
|
||||||
|
androidx.customview:customview-poolingcontainer:1.0.0
|
||||||
|
androidx.customview:customview:1.0.0
|
||||||
|
androidx.emoji2:emoji2:1.3.0
|
||||||
|
androidx.exifinterface:exifinterface:1.3.7
|
||||||
|
androidx.fragment:fragment:1.5.1
|
||||||
|
androidx.graphics:graphics-path:1.0.1
|
||||||
|
androidx.interpolator:interpolator: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-viewmodel-android: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.loader:loader:1.0.0
|
||||||
|
androidx.metrics:metrics-performance:1.0.0-alpha04
|
||||||
|
androidx.profileinstaller:profileinstaller:1.3.1
|
||||||
|
androidx.savedstate:savedstate-ktx:1.2.1
|
||||||
|
androidx.savedstate:savedstate:1.2.1
|
||||||
|
androidx.startup:startup-runtime:1.1.1
|
||||||
|
androidx.tracing:tracing-ktx:1.3.0-alpha02
|
||||||
|
androidx.tracing:tracing:1.3.0-alpha02
|
||||||
|
androidx.vectordrawable:vectordrawable-animated:1.1.0
|
||||||
|
androidx.vectordrawable:vectordrawable:1.1.0
|
||||||
|
androidx.versionedparcelable:versionedparcelable:1.1.1
|
||||||
|
androidx.viewpager:viewpager:1.0.0
|
||||||
|
androidx.window.extensions.core:core:1.0.0
|
||||||
|
androidx.window:window-core-android:1.3.0-beta02
|
||||||
|
androidx.window:window-core:1.3.0-beta02
|
||||||
|
androidx.window:window:1.3.0-beta02
|
||||||
|
com.google.accompanist:accompanist-drawablepainter:0.32.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.guava:listenablefuture:1.0
|
||||||
|
com.squareup.okhttp3:okhttp:4.12.0
|
||||||
|
com.squareup.okio:okio-jvm:3.8.0
|
||||||
|
com.squareup.okio:okio:3.8.0
|
||||||
|
io.coil-kt:coil-base:2.6.0
|
||||||
|
io.coil-kt:coil-compose-base:2.6.0
|
||||||
|
io.coil-kt:coil-compose:2.6.0
|
||||||
|
io.coil-kt:coil:2.6.0
|
||||||
|
javax.inject:javax.inject:1
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib:2.0.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.5.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-datetime:0.5.0
|
||||||
|
org.jetbrains:annotations:23.0.0
|
@ -1,3 +1,3 @@
|
|||||||
# :app module
|
# :app module
|
||||||
|
## Dependency graph
|
||||||
![Dependency graph](../docs/images/graphs/dep_graph_app.png)
|
![Dependency graph](../docs/images/graphs/dep_graph_app.svg)
|
||||||
|
@ -0,0 +1,226 @@
|
|||||||
|
androidx.activity:activity-compose:1.8.2
|
||||||
|
androidx.activity:activity-ktx:1.8.2
|
||||||
|
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.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
|
||||||
|
androidx.browser:browser:1.8.0
|
||||||
|
androidx.collection:collection-jvm:1.4.0
|
||||||
|
androidx.collection:collection-ktx:1.4.0
|
||||||
|
androidx.collection:collection:1.4.0
|
||||||
|
androidx.compose.animation:animation-android:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation-core-android:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation-core:1.7.0-beta01
|
||||||
|
androidx.compose.animation:animation:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-android:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-layout-android:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation-layout:1.7.0-beta01
|
||||||
|
androidx.compose.foundation:foundation:1.7.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-android:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-layout:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-navigation-android:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-beta01
|
||||||
|
androidx.compose.material3.adaptive:adaptive:1.0.0-beta01
|
||||||
|
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-adaptive-navigation-suite:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-android:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-window-size-class-android:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3-window-size-class:1.3.0-beta01
|
||||||
|
androidx.compose.material3:material3:1.3.0-beta01
|
||||||
|
androidx.compose.material:material-icons-core-android:1.6.3
|
||||||
|
androidx.compose.material:material-icons-core:1.6.3
|
||||||
|
androidx.compose.material:material-icons-extended-android:1.6.3
|
||||||
|
androidx.compose.material:material-icons-extended:1.6.3
|
||||||
|
androidx.compose.material:material-ripple-android:1.7.0-beta01
|
||||||
|
androidx.compose.material:material-ripple:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-android:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-saveable-android:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-saveable:1.7.0-beta01
|
||||||
|
androidx.compose.runtime:runtime-tracing:1.0.0-beta01
|
||||||
|
androidx.compose.runtime:runtime:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-geometry-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-geometry:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-graphics-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-graphics:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-text-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-text:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-tooling-preview-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-tooling-preview:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-unit-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-unit:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-util-android:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui-util:1.7.0-beta01
|
||||||
|
androidx.compose.ui:ui:1.7.0-beta01
|
||||||
|
androidx.compose:compose-bom:2024.02.02
|
||||||
|
androidx.concurrent:concurrent-futures:1.1.0
|
||||||
|
androidx.core:core-ktx:1.13.1
|
||||||
|
androidx.core:core-splashscreen:1.0.1
|
||||||
|
androidx.core:core:1.13.1
|
||||||
|
androidx.cursoradapter:cursoradapter:1.0.0
|
||||||
|
androidx.customview:customview-poolingcontainer:1.0.0
|
||||||
|
androidx.customview:customview:1.0.0
|
||||||
|
androidx.datastore:datastore-core:1.0.0
|
||||||
|
androidx.datastore:datastore-preferences-core:1.0.0
|
||||||
|
androidx.datastore:datastore-preferences:1.0.0
|
||||||
|
androidx.datastore:datastore:1.0.0
|
||||||
|
androidx.documentfile:documentfile:1.0.0
|
||||||
|
androidx.drawerlayout:drawerlayout:1.0.0
|
||||||
|
androidx.emoji2:emoji2-views-helper:1.3.0
|
||||||
|
androidx.emoji2:emoji2:1.3.0
|
||||||
|
androidx.exifinterface:exifinterface:1.3.7
|
||||||
|
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
|
||||||
|
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.1
|
||||||
|
androidx.lifecycle:lifecycle-common-jvm:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-common:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-livedata-core:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-livedata:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-process:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime-android:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime-compose-android:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime-compose:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime-ktx:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-runtime:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-service:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel-android:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel-compose:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.1
|
||||||
|
androidx.lifecycle:lifecycle-viewmodel:2.8.1
|
||||||
|
androidx.loader:loader:1.0.0
|
||||||
|
androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
|
||||||
|
androidx.metrics:metrics-performance:1.0.0-alpha04
|
||||||
|
androidx.navigation:navigation-common-ktx:2.8.0-alpha06
|
||||||
|
androidx.navigation:navigation-common:2.8.0-alpha06
|
||||||
|
androidx.navigation:navigation-compose:2.8.0-alpha06
|
||||||
|
androidx.navigation:navigation-runtime-ktx:2.8.0-alpha06
|
||||||
|
androidx.navigation:navigation-runtime:2.8.0-alpha06
|
||||||
|
androidx.print:print:1.0.0
|
||||||
|
androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
|
||||||
|
androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
|
||||||
|
androidx.profileinstaller:profileinstaller:1.3.1
|
||||||
|
androidx.resourceinspection:resourceinspection-annotation:1.0.1
|
||||||
|
androidx.room:room-common:2.6.1
|
||||||
|
androidx.room:room-ktx:2.6.1
|
||||||
|
androidx.room:room-runtime:2.6.1
|
||||||
|
androidx.savedstate:savedstate-ktx:1.2.1
|
||||||
|
androidx.savedstate:savedstate:1.2.1
|
||||||
|
androidx.sqlite:sqlite-framework:2.4.0
|
||||||
|
androidx.sqlite:sqlite:2.4.0
|
||||||
|
androidx.startup:startup-runtime:1.1.1
|
||||||
|
androidx.tracing:tracing-ktx:1.3.0-alpha02
|
||||||
|
androidx.tracing:tracing-perfetto:1.0.0
|
||||||
|
androidx.tracing:tracing:1.3.0-alpha02
|
||||||
|
androidx.vectordrawable:vectordrawable-animated:1.1.0
|
||||||
|
androidx.vectordrawable:vectordrawable:1.1.0
|
||||||
|
androidx.versionedparcelable:versionedparcelable:1.1.1
|
||||||
|
androidx.viewpager:viewpager:1.0.0
|
||||||
|
androidx.window.extensions.core:core:1.0.0
|
||||||
|
androidx.window:window-core-android:1.3.0-beta02
|
||||||
|
androidx.window:window-core:1.3.0-beta02
|
||||||
|
androidx.window:window:1.3.0-beta02
|
||||||
|
androidx.work:work-runtime-ktx:2.9.0
|
||||||
|
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-backend-cct:3.1.9
|
||||||
|
com.google.android.datatransport:transport-runtime:3.1.9
|
||||||
|
com.google.android.gms:play-services-ads-identifier:18.0.0
|
||||||
|
com.google.android.gms:play-services-base:18.0.1
|
||||||
|
com.google.android.gms:play-services-basement:18.1.0
|
||||||
|
com.google.android.gms:play-services-cloud-messaging:17.0.1
|
||||||
|
com.google.android.gms:play-services-measurement-api:21.4.0
|
||||||
|
com.google.android.gms:play-services-measurement-base:21.4.0
|
||||||
|
com.google.android.gms:play-services-measurement-impl:21.4.0
|
||||||
|
com.google.android.gms:play-services-measurement-sdk-api:21.4.0
|
||||||
|
com.google.android.gms:play-services-measurement-sdk:21.4.0
|
||||||
|
com.google.android.gms:play-services-measurement:21.4.0
|
||||||
|
com.google.android.gms:play-services-oss-licenses:17.0.1
|
||||||
|
com.google.android.gms:play-services-stats:17.0.2
|
||||||
|
com.google.android.gms:play-services-tasks:18.0.2
|
||||||
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
|
com.google.dagger:dagger-lint-aar:2.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.firebase:firebase-abt:21.1.1
|
||||||
|
com.google.firebase:firebase-analytics-ktx:21.4.0
|
||||||
|
com.google.firebase:firebase-analytics:21.4.0
|
||||||
|
com.google.firebase:firebase-annotations:16.2.0
|
||||||
|
com.google.firebase:firebase-bom:32.4.0
|
||||||
|
com.google.firebase:firebase-common-ktx:20.4.2
|
||||||
|
com.google.firebase:firebase-common:20.4.2
|
||||||
|
com.google.firebase:firebase-components:17.1.5
|
||||||
|
com.google.firebase:firebase-config:21.5.0
|
||||||
|
com.google.firebase:firebase-crashlytics-ktx:18.5.0
|
||||||
|
com.google.firebase:firebase-crashlytics:18.5.0
|
||||||
|
com.google.firebase:firebase-datatransport:18.1.8
|
||||||
|
com.google.firebase:firebase-encoders-json:18.0.1
|
||||||
|
com.google.firebase:firebase-encoders-proto:16.0.0
|
||||||
|
com.google.firebase:firebase-encoders:17.0.0
|
||||||
|
com.google.firebase:firebase-iid-interop:17.1.0
|
||||||
|
com.google.firebase:firebase-installations-interop:17.1.1
|
||||||
|
com.google.firebase:firebase-installations:17.2.0
|
||||||
|
com.google.firebase:firebase-measurement-connector:19.0.0
|
||||||
|
com.google.firebase:firebase-messaging-ktx:23.3.0
|
||||||
|
com.google.firebase:firebase-messaging:23.3.0
|
||||||
|
com.google.firebase:firebase-perf-ktx:20.5.0
|
||||||
|
com.google.firebase:firebase-perf:20.5.0
|
||||||
|
com.google.firebase:firebase-sessions:1.1.0
|
||||||
|
com.google.firebase:protolite-well-known-types:18.0.0
|
||||||
|
com.google.guava:failureaccess:1.0.1
|
||||||
|
com.google.guava:guava:31.1-android
|
||||||
|
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||||
|
com.google.j2objc:j2objc-annotations:1.3
|
||||||
|
com.google.protobuf:protobuf-javalite: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
|
||||||
|
com.squareup.okio:okio-jvm:3.8.0
|
||||||
|
com.squareup.okio:okio:3.8.0
|
||||||
|
com.squareup.retrofit2:retrofit:2.9.0
|
||||||
|
io.coil-kt:coil-base:2.6.0
|
||||||
|
io.coil-kt:coil-compose-base:2.6.0
|
||||||
|
io.coil-kt:coil-compose:2.6.0
|
||||||
|
io.coil-kt:coil-svg:2.6.0
|
||||||
|
io.coil-kt:coil:2.6.0
|
||||||
|
javax.inject:javax.inject:1
|
||||||
|
org.checkerframework:checker-qual:3.12.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
|
||||||
|
org.jetbrains.kotlin:kotlin-stdlib:2.0.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.5.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-datetime:0.5.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.3
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3
|
||||||
|
org.jetbrains:annotations:23.0.0
|
@ -1,268 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.samples.apps.nowinandroid.ui
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
|
||||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
|
||||||
import androidx.compose.ui.unit.DpSize
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.google.accompanist.testharness.TestHarness
|
|
||||||
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
|
|
||||||
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
|
|
||||||
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
|
|
||||||
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
|
|
||||||
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
|
|
||||||
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
|
|
||||||
import dagger.hilt.android.testing.BindValue
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidRule
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.rules.TemporaryFolder
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that the navigation UI is rendered correctly on different screen sizes.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
|
||||||
@HiltAndroidTest
|
|
||||||
class NavigationUiTest {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grant [android.Manifest.permission.POST_NOTIFICATIONS] permission.
|
|
||||||
*/
|
|
||||||
@get:Rule(order = 2)
|
|
||||||
val postNotificationsPermission = GrantPostNotificationsPermissionRule()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a test activity to set the content on.
|
|
||||||
*/
|
|
||||||
@get:Rule(order = 3)
|
|
||||||
val composeTestRule = createAndroidComposeRule<HiltComponentActivity>()
|
|
||||||
|
|
||||||
val userNewsResourceRepository = CompositeUserNewsResourceRepository(
|
|
||||||
newsRepository = TestNewsRepository(),
|
|
||||||
userDataRepository = TestUserDataRepository(),
|
|
||||||
)
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var networkMonitor: NetworkMonitor
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
hiltRule.inject()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun compactWidth_compactHeight_showsNavigationBar() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(400.dp, 400.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun mediumWidth_compactHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(610.dp, 400.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun expandedWidth_compactHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(900.dp, 400.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun compactWidth_mediumHeight_showsNavigationBar() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(400.dp, 500.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun mediumWidth_mediumHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(610.dp, 500.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun expandedWidth_mediumHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(900.dp, 500.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun compactWidth_expandedHeight_showsNavigationBar() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(400.dp, 1000.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun mediumWidth_expandedHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(610.dp, 1000.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun expandedWidth_expandedHeight_showsNavigationRail() {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
TestHarness(size = DpSize(900.dp, 1000.dp)) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
NiaApp(
|
|
||||||
windowSizeClass = WindowSizeClass.calculateFromSize(
|
|
||||||
DpSize(maxWidth, maxHeight),
|
|
||||||
),
|
|
||||||
networkMonitor = networkMonitor,
|
|
||||||
userNewsResourceRepository = userNewsResourceRepository,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.ui.interests2pane
|
||||||
|
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class Interests2PaneViewModel @Inject constructor(
|
||||||
|
private val savedStateHandle: SavedStateHandle,
|
||||||
|
) : ViewModel() {
|
||||||
|
val selectedTopicId: StateFlow<String?> =
|
||||||
|
savedStateHandle.getStateFlow(TOPIC_ID_ARG, savedStateHandle[TOPIC_ID_ARG])
|
||||||
|
|
||||||
|
fun onTopicClick(topicId: String?) {
|
||||||
|
savedStateHandle[TOPIC_ID_ARG] = topicId
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* 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.ExperimentalMaterial3AdaptiveApi
|
||||||
|
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.navigation.ThreePaneScaffoldNavigator
|
||||||
|
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.key
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.Saver
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import androidx.navigation.navArgument
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.interests.navigation.INTERESTS_ROUTE
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ROUTE
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.createTopicRoute
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
|
||||||
|
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
private const val DETAIL_PANE_NAVHOST_ROUTE = "detail_pane_route"
|
||||||
|
|
||||||
|
fun NavGraphBuilder.interestsListDetailScreen() {
|
||||||
|
composable(
|
||||||
|
route = INTERESTS_ROUTE,
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(TOPIC_ID_ARG) {
|
||||||
|
type = NavType.StringType
|
||||||
|
defaultValue = null
|
||||||
|
nullable = true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
InterestsListDetailScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun InterestsListDetailScreen(
|
||||||
|
viewModel: Interests2PaneViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle()
|
||||||
|
InterestsListDetailScreen(
|
||||||
|
selectedTopicId = selectedTopicId,
|
||||||
|
onTopicClick = viewModel::onTopicClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
|
@Composable
|
||||||
|
internal fun InterestsListDetailScreen(
|
||||||
|
selectedTopicId: String?,
|
||||||
|
onTopicClick: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val listDetailNavigator = rememberListDetailPaneScaffoldNavigator(
|
||||||
|
initialDestinationHistory = listOfNotNull(
|
||||||
|
ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.List),
|
||||||
|
ThreePaneScaffoldDestinationItem<Nothing>(ListDetailPaneScaffoldRole.Detail).takeIf {
|
||||||
|
selectedTopicId != null
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
BackHandler(listDetailNavigator.canNavigateBack()) {
|
||||||
|
listDetailNavigator.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
var nestedNavHostStartDestination by remember {
|
||||||
|
mutableStateOf(selectedTopicId?.let(::createTopicRoute) ?: TOPIC_ROUTE)
|
||||||
|
}
|
||||||
|
var nestedNavKey by rememberSaveable(
|
||||||
|
stateSaver = Saver({ it.toString() }, UUID::fromString),
|
||||||
|
) {
|
||||||
|
mutableStateOf(UUID.randomUUID())
|
||||||
|
}
|
||||||
|
val nestedNavController = key(nestedNavKey) {
|
||||||
|
rememberNavController()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onTopicClickShowDetailPane(topicId: String) {
|
||||||
|
onTopicClick(topicId)
|
||||||
|
if (listDetailNavigator.isDetailPaneVisible()) {
|
||||||
|
// If the detail pane was visible, then use the nestedNavController navigate call
|
||||||
|
// directly
|
||||||
|
nestedNavController.navigateToTopic(topicId) {
|
||||||
|
popUpTo(DETAIL_PANE_NAVHOST_ROUTE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, recreate the NavHost entirely, and start at the new destination
|
||||||
|
nestedNavHostStartDestination = createTopicRoute(topicId)
|
||||||
|
nestedNavKey = UUID.randomUUID()
|
||||||
|
}
|
||||||
|
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDetailPaneScaffold(
|
||||||
|
value = listDetailNavigator.scaffoldValue,
|
||||||
|
directive = listDetailNavigator.scaffoldDirective,
|
||||||
|
listPane = {
|
||||||
|
AnimatedPane {
|
||||||
|
InterestsRoute(
|
||||||
|
onTopicClick = ::onTopicClickShowDetailPane,
|
||||||
|
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailPane = {
|
||||||
|
AnimatedPane {
|
||||||
|
key(nestedNavKey) {
|
||||||
|
NavHost(
|
||||||
|
navController = nestedNavController,
|
||||||
|
startDestination = nestedNavHostStartDestination,
|
||||||
|
route = DETAIL_PANE_NAVHOST_ROUTE,
|
||||||
|
) {
|
||||||
|
topicScreen(
|
||||||
|
showBackButton = !listDetailNavigator.isListPaneVisible(),
|
||||||
|
onBackClick = listDetailNavigator::navigateBack,
|
||||||
|
onTopicClick = ::onTopicClickShowDetailPane,
|
||||||
|
)
|
||||||
|
composable(route = TOPIC_ROUTE) {
|
||||||
|
TopicDetailPlaceholder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
|
private fun <T> ThreePaneScaffoldNavigator<T>.isListPaneVisible(): Boolean =
|
||||||
|
scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
|
private fun <T> ThreePaneScaffoldNavigator<T>.isDetailPaneVisible(): Boolean =
|
||||||
|
scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.util
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.profileinstaller.ProfileVerifier
|
||||||
|
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.guava.await
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the app's Baseline Profile Compilation Status using [ProfileVerifier].
|
||||||
|
*
|
||||||
|
* When delivering through Google Play, the baseline profile is compiled during installation.
|
||||||
|
* In this case you will see the correct state logged without any further action necessary.
|
||||||
|
* To verify baseline profile installation locally, you need to manually trigger baseline
|
||||||
|
* profile installation.
|
||||||
|
*
|
||||||
|
* For immediate compilation, call:
|
||||||
|
* ```bash
|
||||||
|
* adb shell cmd package compile -f -m speed-profile com.example.macrobenchmark.target
|
||||||
|
* ```
|
||||||
|
* You can also trigger background optimizations:
|
||||||
|
* ```bash
|
||||||
|
* adb shell pm bg-dexopt-job
|
||||||
|
* ```
|
||||||
|
* Both jobs run asynchronously and might take some time complete.
|
||||||
|
*
|
||||||
|
* To see quick turnaround of the ProfileVerifier, we recommend using `speed-profile`.
|
||||||
|
* If you don't do either of these steps, you might only see the profile status reported as
|
||||||
|
* "enqueued for compilation" when running the sample locally.
|
||||||
|
*
|
||||||
|
* @see androidx.profileinstaller.ProfileVerifier.CompilationStatus.ResultCode
|
||||||
|
*/
|
||||||
|
class ProfileVerifierLogger @Inject constructor(
|
||||||
|
@ApplicationScope private val scope: CoroutineScope,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "ProfileInstaller"
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun invoke() = scope.launch {
|
||||||
|
val status = ProfileVerifier.getCompilationStatusAsync().await()
|
||||||
|
Log.d(TAG, "Status code: ${status.profileInstallResultCode}")
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
when {
|
||||||
|
status.isCompiledWithProfile -> "App compiled with profile"
|
||||||
|
status.hasProfileEnqueuedForCompilation() -> "Profile enqueued for compilation"
|
||||||
|
else -> "Profile not compiled nor enqueued"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -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<HiltComponentActivity>()
|
||||||
|
|
||||||
|
@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())
|
@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* 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.layout.BoxWithConstraints
|
||||||
|
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.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
|
import androidx.compose.ui.test.DeviceConfigurationOverride
|
||||||
|
import androidx.compose.ui.test.ForcedSize
|
||||||
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.onRoot
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.DpSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
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 SnackbarScreenshotTests {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<HiltComponentActivity>()
|
||||||
|
|
||||||
|
@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,
|
||||||
|
"snackbar_compact_medium_noSnackbar",
|
||||||
|
action = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun snackbarShown_phone() {
|
||||||
|
val snackbarHostState = SnackbarHostState()
|
||||||
|
testSnackbarScreenshotWithSize(
|
||||||
|
snackbarHostState,
|
||||||
|
400.dp,
|
||||||
|
500.dp,
|
||||||
|
"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,
|
||||||
|
"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,
|
||||||
|
"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)),
|
||||||
|
) {
|
||||||
|
BoxWithConstraints {
|
||||||
|
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(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onRoot()
|
||||||
|
.captureRoboImage(
|
||||||
|
"src/testDemo/screenshots/$screenshotName.png",
|
||||||
|
roborazziOptions = DefaultRoborazziOptions,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 260 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 211 KiB |
After Width: | Height: | Size: 96 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.baselineprofile
|
||||||
|
|
||||||
|
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||||
|
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
|
||||||
|
import com.google.samples.apps.nowinandroid.bookmarks.goToBookmarksScreen
|
||||||
|
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline Profile of the "Bookmarks" screen
|
||||||
|
*/
|
||||||
|
class BookmarksBaselineProfile {
|
||||||
|
@get:Rule val baselineProfileRule = BaselineProfileRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generate() =
|
||||||
|
baselineProfileRule.collect(PACKAGE_NAME) {
|
||||||
|
startActivityAndAllowNotifications()
|
||||||
|
|
||||||
|
// Navigate to saved screen
|
||||||
|
goToBookmarksScreen()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.baselineprofile
|
||||||
|
|
||||||
|
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||||
|
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
|
||||||
|
import com.google.samples.apps.nowinandroid.interests.goToInterestsScreen
|
||||||
|
import com.google.samples.apps.nowinandroid.interests.interestsScrollTopicsDownUp
|
||||||
|
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline Profile of the "Interests" screen
|
||||||
|
*/
|
||||||
|
class InterestsBaselineProfile {
|
||||||
|
@get:Rule val baselineProfileRule = BaselineProfileRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generate() =
|
||||||
|
baselineProfileRule.collect(PACKAGE_NAME) {
|
||||||
|
startActivityAndAllowNotifications()
|
||||||
|
|
||||||
|
// Navigate to interests screen
|
||||||
|
goToInterestsScreen()
|
||||||
|
interestsScrollTopicsDownUp()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.samples.apps.nowinandroid.baselineprofile
|
||||||
|
|
||||||
|
import androidx.benchmark.macro.MacrobenchmarkScope
|
||||||
|
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||||
|
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
|
||||||
|
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline Profile for app startup. This profile also enables using [Dex Layout Optimizations](https://developer.android.com/topic/performance/baselineprofiles/dex-layout-optimizations)
|
||||||
|
* via the `includeInStartupProfile` parameter.
|
||||||
|
*/
|
||||||
|
class StartupBaselineProfile {
|
||||||
|
@get:Rule val baselineProfileRule = BaselineProfileRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generate() = baselineProfileRule.collect(
|
||||||
|
PACKAGE_NAME,
|
||||||
|
includeInStartupProfile = true,
|
||||||
|
profileBlock = MacrobenchmarkScope::startActivityAndAllowNotifications,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
// This file contains classes (with possible wildcards) that the Compose Compiler will treat as stable.
|
||||||
|
// It allows us to define classes that our not part of our codebase without wrapping them in a stable class.
|
||||||
|
// For more information, check https://developer.android.com/jetpack/compose/performance/stability/fix#configuration-file
|
||||||
|
|
||||||
|
// We always use immutable classes for our data model, to avoid running the Compose compiler
|
||||||
|
// in the module we declare it to be stable here.
|
||||||
|
com.google.samples.apps.nowinandroid.core.model.data.*
|
||||||
|
|
||||||
|
// Java standard library classes
|
||||||
|
java.time.ZoneId
|
||||||
|
java.time.ZoneOffset
|
@ -0,0 +1,3 @@
|
|||||||
|
# :core:analytics module
|
||||||
|
## Dependency graph
|
||||||
|
![Dependency graph](../../docs/images/graphs/dep_graph_core_analytics.svg)
|