Merge remote-tracking branch 'origin/main' into fix/1161-2

pull/1315/head
Simon Marquis 6 months ago
commit 1c5513283a

@ -12,12 +12,11 @@ updates:
registries: "*"
labels: [ "version update" ]
groups:
kotlin-ksp-compose:
kotlin-ksp:
patterns:
- "org.jetbrains.kotlin:*"
- "org.jetbrains.kotlin.jvm"
- "com.google.devtools.ksp"
- "androidx.compose.compiler:compiler"
open-pull-requests-limit: 10
registries:
maven-google:

3
.gitignore vendored

@ -43,3 +43,6 @@ _sandbox
# Android Studio captures folder
captures/
# Kotlin
.kotlin

@ -2,8 +2,8 @@ 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.7.1
androidx.annotation:annotation:1.7.1
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
@ -12,61 +12,72 @@ 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.6.3
androidx.compose.animation:animation-core-android:1.6.3
androidx.compose.animation:animation-core:1.6.3
androidx.compose.animation:animation:1.6.3
androidx.compose.foundation:foundation-android:1.6.3
androidx.compose.foundation:foundation-layout-android:1.6.3
androidx.compose.foundation:foundation-layout:1.6.3
androidx.compose.foundation:foundation:1.6.3
androidx.compose.material3:material3-android:1.2.1
androidx.compose.material3:material3:1.2.1
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.6.3
androidx.compose.material:material-ripple:1.6.3
androidx.compose.runtime:runtime-android:1.6.3
androidx.compose.runtime:runtime-saveable-android:1.6.3
androidx.compose.runtime:runtime-saveable:1.6.3
androidx.compose.runtime:runtime:1.6.3
androidx.compose.ui:ui-android:1.6.3
androidx.compose.ui:ui-geometry-android:1.6.3
androidx.compose.ui:ui-geometry:1.6.3
androidx.compose.ui:ui-graphics-android:1.6.3
androidx.compose.ui:ui-graphics:1.6.3
androidx.compose.ui:ui-text-android:1.6.3
androidx.compose.ui:ui-text:1.6.3
androidx.compose.ui:ui-tooling-preview-android:1.6.3
androidx.compose.ui:ui-tooling-preview:1.6.3
androidx.compose.ui:ui-unit-android:1.6.3
androidx.compose.ui:ui-unit:1.6.3
androidx.compose.ui:ui-util-android:1.6.3
androidx.compose.ui:ui-util:1.6.3
androidx.compose.ui:ui: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.12.0
androidx.core:core:1.12.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.7.0
androidx.lifecycle:lifecycle-common:2.7.0
androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0
androidx.lifecycle:lifecycle-livedata-core:2.7.0
androidx.lifecycle:lifecycle-livedata:2.7.0
androidx.lifecycle:lifecycle-process:2.7.0
androidx.lifecycle:lifecycle-runtime-ktx:2.7.0
androidx.lifecycle:lifecycle-runtime:2.7.0
androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0
androidx.lifecycle:lifecycle-viewmodel:2.7.0
androidx.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
@ -79,12 +90,16 @@ 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
com.google.dagger:dagger:2.51
com.google.dagger:hilt-android:2.51
com.google.dagger:hilt-core:2.51
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
@ -94,10 +109,10 @@ io.coil-kt:coil-compose-base:2.6.0
io.coil-kt:coil-compose:2.6.0
io.coil-kt:coil:2.6.0
javax.inject:javax.inject:1
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22
org.jetbrains.kotlin:kotlin-stdlib-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:1.9.22
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

@ -2,10 +2,10 @@ 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-beta01
androidx.annotation:annotation:1.8.0-beta01
androidx.appcompat:appcompat-resources:1.6.1
androidx.appcompat:appcompat:1.6.1
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
@ -13,54 +13,56 @@ 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-alpha06
androidx.compose.animation:animation-core-android:1.7.0-alpha06
androidx.compose.animation:animation-core:1.7.0-alpha06
androidx.compose.animation:animation:1.7.0-alpha06
androidx.compose.foundation:foundation-android:1.7.0-alpha06
androidx.compose.foundation:foundation-layout-android:1.7.0-alpha06
androidx.compose.foundation:foundation-layout:1.7.0-alpha06
androidx.compose.foundation:foundation:1.7.0-alpha06
androidx.compose.material3.adaptive:adaptive-android:1.0.0-alpha10
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0-alpha10
androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha10
androidx.compose.material3.adaptive:adaptive-navigation-android:1.0.0-alpha10
androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha10
androidx.compose.material3.adaptive:adaptive:1.0.0-alpha10
androidx.compose.material3:material3-android:1.2.1
androidx.compose.material3:material3-window-size-class-android:1.2.1
androidx.compose.material3:material3-window-size-class:1.2.1
androidx.compose.material3:material3:1.2.1
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.6.3
androidx.compose.material:material-ripple:1.6.3
androidx.compose.runtime:runtime-android:1.7.0-alpha06
androidx.compose.runtime:runtime-saveable-android:1.7.0-alpha06
androidx.compose.runtime:runtime-saveable:1.7.0-alpha06
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-alpha06
androidx.compose.ui:ui-android:1.7.0-alpha06
androidx.compose.ui:ui-geometry-android:1.7.0-alpha06
androidx.compose.ui:ui-geometry:1.7.0-alpha06
androidx.compose.ui:ui-graphics-android:1.7.0-alpha06
androidx.compose.ui:ui-graphics:1.7.0-alpha06
androidx.compose.ui:ui-text-android:1.7.0-alpha06
androidx.compose.ui:ui-text:1.7.0-alpha06
androidx.compose.ui:ui-tooling-preview-android:1.7.0-alpha06
androidx.compose.ui:ui-tooling-preview:1.7.0-alpha06
androidx.compose.ui:ui-unit-android:1.7.0-alpha06
androidx.compose.ui:ui-unit:1.7.0-alpha06
androidx.compose.ui:ui-util-android:1.7.0-alpha06
androidx.compose.ui:ui-util:1.7.0-alpha06
androidx.compose.ui:ui:1.7.0-alpha06
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.12.0
androidx.core:core-ktx:1.13.1
androidx.core:core-splashscreen:1.0.1
androidx.core:core:1.12.0
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
@ -73,33 +75,34 @@ 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.graphics:graphics-path:1.0.0-beta02
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.0-alpha04
androidx.lifecycle:lifecycle-common-jvm:2.8.0-alpha04
androidx.lifecycle:lifecycle-common:2.8.0-alpha04
androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.0-alpha04
androidx.lifecycle:lifecycle-livedata-core:2.8.0-alpha04
androidx.lifecycle:lifecycle-livedata:2.8.0-alpha04
androidx.lifecycle:lifecycle-process:2.8.0-alpha04
androidx.lifecycle:lifecycle-runtime-android:2.8.0-alpha04
androidx.lifecycle:lifecycle-runtime-compose:2.8.0-alpha04
androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.0-alpha04
androidx.lifecycle:lifecycle-runtime-ktx:2.8.0-alpha04
androidx.lifecycle:lifecycle-runtime:2.8.0-alpha04
androidx.lifecycle:lifecycle-service:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel-android:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.0-alpha04
androidx.lifecycle:lifecycle-viewmodel:2.8.0-alpha04
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
@ -129,9 +132,9 @@ 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-alpha03
androidx.window:window-core:1.3.0-alpha03
androidx.window:window:1.3.0-alpha03
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
@ -154,10 +157,10 @@ com.google.android.gms:play-services-oss-licenses:17.0.1
com.google.android.gms:play-services-stats:17.0.2
com.google.android.gms:play-services-tasks:18.0.2
com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.51
com.google.dagger:dagger:2.51
com.google.dagger:hilt-android:2.51
com.google.dagger:hilt-core:2.51
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
@ -188,8 +191,8 @@ 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
@ -203,10 +206,10 @@ 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:1.9.22
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:1.9.22
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

@ -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

@ -20,7 +20,6 @@ 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.hasAnyAncestor
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
@ -225,12 +224,7 @@ class NavigationTest {
onNodeWithText(ok).performClick()
// Check that the saved screen is still visible and selected.
onNode(
hasText(saved) and
hasAnyAncestor(
hasTestTag("NiaBottomBar") or hasTestTag("NiaNavRail"),
),
).assertIsSelected()
onNode(hasText(saved) and hasTestTag("NiaNavItem")).assertIsSelected()
}
}

@ -1,247 +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.runtime.Composable
import androidx.compose.ui.test.DeviceConfigurationOverride
import androidx.compose.ui.test.ForcedSize
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
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
@Inject
lateinit var timeZoneMonitor: TimeZoneMonitor
@Before
fun setup() {
hiltRule.inject()
}
@Test
fun compactWidth_compactHeight_showsNavigationBar() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(400.dp, 400.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
}
@Test
fun mediumWidth_compactHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(610.dp, 400.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Test
fun expandedWidth_compactHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 400.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Test
fun compactWidth_mediumHeight_showsNavigationBar() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(400.dp, 500.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
}
@Test
fun mediumWidth_mediumHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(610.dp, 500.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Test
fun expandedWidth_mediumHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 500.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Test
fun compactWidth_expandedHeight_showsNavigationBar() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(400.dp, 1000.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaBottomBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaNavRail").assertDoesNotExist()
}
@Test
fun mediumWidth_expandedHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(610.dp, 1000.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Test
fun expandedWidth_expandedHeight_showsNavigationRail() {
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1000.dp)),
) {
BoxWithConstraints {
NiaApp(fakeAppState(maxWidth, maxHeight))
}
}
}
composeTestRule.onNodeWithTag("NiaNavRail").assertIsDisplayed()
composeTestRule.onNodeWithTag("NiaBottomBar").assertDoesNotExist()
}
@Composable
private fun fakeAppState(maxWidth: Dp, maxHeight: Dp) = rememberNiaAppState(
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
}

@ -16,15 +16,11 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.ComposeNavigator
import androidx.navigation.compose.composable
@ -43,7 +39,6 @@ import kotlinx.datetime.TimeZone
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
@ -52,7 +47,6 @@ import kotlin.test.assertTrue
* Note: This could become an unit test if Robolectric is added to the project and the Context
* is faked.
*/
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
class NiaAppStateTest {
@get:Rule
@ -79,7 +73,6 @@ class NiaAppStateTest {
NiaAppState(
navController = navController,
coroutineScope = backgroundScope,
windowSizeClass = getCompactWindowClass(),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -102,7 +95,6 @@ class NiaAppStateTest {
fun niaAppState_destinations() = runTest {
composeTestRule.setContent {
state = rememberNiaAppState(
windowSizeClass = getCompactWindowClass(),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -115,64 +107,12 @@ class NiaAppStateTest {
assertTrue(state.topLevelDestinations[2].name.contains("interests", true))
}
@Test
fun niaAppState_showBottomBar_compact() = runTest {
composeTestRule.setContent {
state = NiaAppState(
navController = NavHostController(LocalContext.current),
coroutineScope = backgroundScope,
windowSizeClass = getCompactWindowClass(),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
}
assertTrue(state.shouldShowBottomBar)
assertFalse(state.shouldShowNavRail)
}
@Test
fun niaAppState_showNavRail_medium() = runTest {
composeTestRule.setContent {
state = NiaAppState(
navController = NavHostController(LocalContext.current),
coroutineScope = backgroundScope,
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(800.dp, 800.dp)),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
}
assertTrue(state.shouldShowNavRail)
assertFalse(state.shouldShowBottomBar)
}
@Test
fun niaAppState_showNavRail_large() = runTest {
composeTestRule.setContent {
state = NiaAppState(
navController = NavHostController(LocalContext.current),
coroutineScope = backgroundScope,
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
}
assertTrue(state.shouldShowNavRail)
assertFalse(state.shouldShowBottomBar)
}
@Test
fun niaAppState_whenNetworkMonitorIsOffline_StateIsOffline() = runTest(UnconfinedTestDispatcher()) {
composeTestRule.setContent {
state = NiaAppState(
navController = NavHostController(LocalContext.current),
coroutineScope = backgroundScope,
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -193,7 +133,6 @@ class NiaAppStateTest {
state = NiaAppState(
navController = NavHostController(LocalContext.current),
coroutineScope = backgroundScope,
windowSizeClass = getCompactWindowClass(),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -207,8 +146,6 @@ class NiaAppStateTest {
state.currentTimeZone.value,
)
}
private fun getCompactWindowClass() = WindowSizeClass.calculateFromSize(DpSize(500.dp, 300.dp))
}
@Composable

@ -23,8 +23,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
@ -58,7 +57,6 @@ import javax.inject.Inject
private const val TAG = "MainActivity"
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@ -134,7 +132,6 @@ class MainActivity : ComponentActivity() {
}
val appState = rememberNiaAppState(
windowSizeClass = calculateWindowSizeClass(this),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -151,6 +148,7 @@ class MainActivity : ComponentActivity() {
androidTheme = shouldUseAndroidTheme(uiState),
disableDynamicTheming = shouldDisableDynamicTheming(uiState),
) {
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
NiaApp(appState)
}
}

@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
@ -26,7 +25,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@ -39,6 +37,9 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult.ActionPerformed
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -63,10 +64,7 @@ import androidx.navigation.NavDestination.Companion.hierarchy
import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBarItem
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationRail
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationRailItem
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationSuiteScaffold
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAppBar
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
@ -76,8 +74,13 @@ import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun NiaApp(appState: NiaAppState, modifier: Modifier = Modifier) {
fun NiaApp(
appState: NiaAppState,
modifier: Modifier = Modifier,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
) {
val shouldShowGradientBackground =
appState.currentTopLevelDestination == TopLevelDestination.FOR_YOU
var showSettingsDialog by rememberSaveable { mutableStateOf(false) }
@ -111,13 +114,18 @@ fun NiaApp(appState: NiaAppState, modifier: Modifier = Modifier) {
showSettingsDialog = showSettingsDialog,
onSettingsDismissed = { showSettingsDialog = false },
onTopAppBarActionClick = { showSettingsDialog = true },
windowAdaptiveInfo = windowAdaptiveInfo,
)
}
}
}
@Composable
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@OptIn(
ExperimentalMaterial3Api::class,
ExperimentalComposeUiApi::class,
ExperimentalMaterial3AdaptiveApi::class,
)
internal fun NiaApp(
appState: NiaAppState,
snackbarHostState: SnackbarHostState,
@ -125,15 +133,49 @@ internal fun NiaApp(
onSettingsDismissed: () -> Unit,
onTopAppBarActionClick: () -> Unit,
modifier: Modifier = Modifier,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
) {
val unreadDestinations by appState.topLevelDestinationsWithUnreadResources
.collectAsStateWithLifecycle()
val currentDestination = appState.currentDestination
if (showSettingsDialog) {
SettingsDialog(
onDismiss = { onSettingsDismissed() },
)
}
NiaNavigationSuiteScaffold(
navigationSuiteItems = {
appState.topLevelDestinations.forEach { destination ->
val hasUnread = unreadDestinations.contains(destination)
val selected = currentDestination
.isTopLevelDestinationInHierarchy(destination)
item(
selected = selected,
onClick = { appState.navigateToTopLevelDestination(destination) },
icon = {
Icon(
imageVector = destination.unselectedIcon,
contentDescription = null,
)
},
selectedIcon = {
Icon(
imageVector = destination.selectedIcon,
contentDescription = null,
)
},
label = { Text(stringResource(destination.iconTextId)) },
modifier =
Modifier
.testTag("NiaNavItem")
.then(if (hasUnread) Modifier.notificationDot() else Modifier),
)
}
},
windowAdaptiveInfo = windowAdaptiveInfo,
) {
Scaffold(
modifier = modifier.semantics {
testTagsAsResourceId = true
@ -142,19 +184,8 @@ internal fun NiaApp(
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
snackbarHost = { SnackbarHost(snackbarHostState) },
bottomBar = {
if (appState.shouldShowBottomBar) {
NiaBottomBar(
destinations = appState.topLevelDestinations,
destinationsWithUnreadResources = unreadDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier.testTag("NiaBottomBar"),
)
}
},
) { padding ->
Row(
Column(
Modifier
.fillMaxSize()
.padding(padding)
@ -165,19 +196,6 @@ internal fun NiaApp(
),
),
) {
if (appState.shouldShowNavRail) {
NiaNavRail(
destinations = appState.topLevelDestinations,
destinationsWithUnreadResources = unreadDestinations,
onNavigateToDestination = appState::navigateToTopLevelDestination,
currentDestination = appState.currentDestination,
modifier = Modifier
.testTag("NiaNavRail")
.safeDrawingPadding(),
)
}
Column(Modifier.fillMaxSize()) {
// Show the top app bar on top level destinations.
val destination = appState.currentTopLevelDestination
val shouldShowTopAppBar = destination != null
@ -201,13 +219,14 @@ internal fun NiaApp(
}
Box(
modifier = if (shouldShowTopAppBar) {
Modifier.consumeWindowInsets(
WindowInsets.safeDrawing.only(WindowInsetsSides.Top),
)
// Workaround for https://issuetracker.google.com/338478720
modifier = Modifier.consumeWindowInsets(
if (shouldShowTopAppBar) {
WindowInsets.safeDrawing.only(WindowInsetsSides.Top)
} else {
Modifier
WindowInsets(0, 0, 0, 0)
},
),
) {
NiaNavHost(
appState = appState,
@ -220,82 +239,12 @@ internal fun NiaApp(
},
)
}
}
// TODO: We may want to add padding or spacer when the snackbar is shown so that
// content doesn't display behind it.
}
}
}
@Composable
private fun NiaNavRail(
destinations: List<TopLevelDestination>,
destinationsWithUnreadResources: Set<TopLevelDestination>,
onNavigateToDestination: (TopLevelDestination) -> Unit,
currentDestination: NavDestination?,
modifier: Modifier = Modifier,
) {
NiaNavigationRail(modifier = modifier) {
destinations.forEach { destination ->
val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
val hasUnread = destinationsWithUnreadResources.contains(destination)
NiaNavigationRailItem(
selected = selected,
onClick = { onNavigateToDestination(destination) },
icon = {
Icon(
imageVector = destination.unselectedIcon,
contentDescription = null,
)
},
selectedIcon = {
Icon(
imageVector = destination.selectedIcon,
contentDescription = null,
)
},
label = { Text(stringResource(destination.iconTextId)) },
modifier = if (hasUnread) Modifier.notificationDot() else Modifier,
)
}
}
}
@Composable
private fun NiaBottomBar(
destinations: List<TopLevelDestination>,
destinationsWithUnreadResources: Set<TopLevelDestination>,
onNavigateToDestination: (TopLevelDestination) -> Unit,
currentDestination: NavDestination?,
modifier: Modifier = Modifier,
) {
NiaNavigationBar(
modifier = modifier,
) {
destinations.forEach { destination ->
val hasUnread = destinationsWithUnreadResources.contains(destination)
val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
NiaNavigationBarItem(
selected = selected,
onClick = { onNavigateToDestination(destination) },
icon = {
Icon(
imageVector = destination.unselectedIcon,
contentDescription = null,
)
},
selectedIcon = {
Icon(
imageVector = destination.selectedIcon,
contentDescription = null,
)
},
label = { Text(stringResource(destination.iconTextId)) },
modifier = if (hasUnread) Modifier.notificationDot() else Modifier,
)
}
}
}
private fun Modifier.notificationDot(): Modifier =

@ -16,8 +16,6 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
@ -55,7 +53,6 @@ import kotlinx.datetime.TimeZone
@Composable
fun rememberNiaAppState(
windowSizeClass: WindowSizeClass,
networkMonitor: NetworkMonitor,
userNewsResourceRepository: UserNewsResourceRepository,
timeZoneMonitor: TimeZoneMonitor,
@ -66,7 +63,6 @@ fun rememberNiaAppState(
return remember(
navController,
coroutineScope,
windowSizeClass,
networkMonitor,
userNewsResourceRepository,
timeZoneMonitor,
@ -74,7 +70,6 @@ fun rememberNiaAppState(
NiaAppState(
navController = navController,
coroutineScope = coroutineScope,
windowSizeClass = windowSizeClass,
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
@ -86,7 +81,6 @@ fun rememberNiaAppState(
class NiaAppState(
val navController: NavHostController,
coroutineScope: CoroutineScope,
val windowSizeClass: WindowSizeClass,
networkMonitor: NetworkMonitor,
userNewsResourceRepository: UserNewsResourceRepository,
timeZoneMonitor: TimeZoneMonitor,
@ -103,12 +97,6 @@ class NiaAppState(
else -> null
}
val shouldShowBottomBar: Boolean
get() = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact
val shouldShowNavRail: Boolean
get() = !shouldShowBottomBar
val isOffline = networkMonitor.isOnline
.map(Boolean::not)
.stateIn(

@ -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()
}
},
)
}
},
)
}

@ -16,8 +16,9 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
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.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.DeviceConfigurationOverride
@ -27,6 +28,7 @@ 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.UserDataRepository
@ -57,7 +59,6 @@ import javax.inject.Inject
/**
* Tests that the navigation UI is rendered correctly on different screen sizes.
*/
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
@ -122,6 +123,7 @@ class NiaAppScreenSizesScreenshotTests {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun testNiaAppScreenshotWithSize(width: Dp, height: Dp, screenshotName: String) {
composeTestRule.setContent {
CompositionLocalProvider(
@ -132,14 +134,20 @@ class NiaAppScreenSizesScreenshotTests {
) {
NiaTheme {
val fakeAppState = rememberNiaAppState(
windowSizeClass = WindowSizeClass.calculateFromSize(
DpSize(width, height),
),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
NiaApp(fakeAppState)
NiaApp(
fakeAppState,
windowAdaptiveInfo = WindowAdaptiveInfo(
windowSizeClass = WindowSizeClass.compute(
width.value,
height.value,
),
windowPosture = Posture(),
),
)
}
}
}
@ -162,20 +170,20 @@ class NiaAppScreenSizesScreenshotTests {
}
@Test
fun mediumWidth_compactHeight_showsNavigationRail() {
fun mediumWidth_compactHeight_showsNavigationBar() {
testNiaAppScreenshotWithSize(
610.dp,
400.dp,
"mediumWidth_compactHeight_showsNavigationRail",
"mediumWidth_compactHeight_showsNavigationBar",
)
}
@Test
fun expandedWidth_compactHeight_showsNavigationRail() {
fun expandedWidth_compactHeight_showsNavigationBar() {
testNiaAppScreenshotWithSize(
900.dp,
400.dp,
"expandedWidth_compactHeight_showsNavigationRail",
"expandedWidth_compactHeight_showsNavigationBar",
)
}

@ -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())

@ -19,8 +19,9 @@ 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.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
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
@ -31,6 +32,7 @@ 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
@ -63,7 +65,6 @@ import javax.inject.Inject
/**
* Tests that the Snackbar is correctly displayed on different screen sizes.
*/
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
@ -191,6 +192,7 @@ class SnackbarScreenshotTests {
}
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun testSnackbarScreenshotWithSize(
snackbarHostState: SnackbarHostState,
width: Dp,
@ -210,16 +212,26 @@ class SnackbarScreenshotTests {
DeviceConfigurationOverride.ForcedSize(DpSize(width, height)),
) {
BoxWithConstraints {
NiaTheme {
val appState = rememberNiaAppState(
windowSizeClass = WindowSizeClass.calculateFromSize(
DpSize(maxWidth, maxHeight),
),
networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
NiaTheme {
NiaApp(appState, snackbarHostState, false, {}, {})
NiaApp(
appState = appState,
snackbarHostState = snackbarHostState,
showSettingsDialog = false,
onSettingsDismissed = {},
onTopAppBarActionClick = {},
windowAdaptiveInfo = WindowAdaptiveInfo(
windowSizeClass = WindowSizeClass.compute(
maxWidth.value,
maxHeight.value,
),
windowPosture = Posture(),
),
)
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

@ -14,6 +14,7 @@
* limitations under the License.
*/
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@ -28,15 +29,17 @@ java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.android.tools.common)
compileOnly(libs.compose.gradlePlugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin)
compileOnly(libs.firebase.performance.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)

@ -18,12 +18,14 @@ import com.android.build.api.dsl.ApplicationExtension
import com.google.samples.apps.nowinandroid.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.application")
apply(plugin = "com.android.application")
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
val extension = extensions.getByType<ApplicationExtension>()
configureAndroidCompose(extension)

@ -18,14 +18,14 @@ import com.android.build.gradle.LibraryExtension
import com.google.samples.apps.nowinandroid.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.kotlin
class AndroidLibraryComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
apply(plugin = "com.android.library")
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
val extension = extensions.getByType<LibraryExtension>()
configureAndroidCompose(extension)

@ -18,9 +18,11 @@ package com.google.samples.apps.nowinandroid
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
/**
* Configure Compose-specific options
@ -33,10 +35,6 @@ internal fun Project.configureAndroidCompose(
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.findVersion("androidxComposeCompiler").get().toString()
}
dependencies {
val bom = libs.findLibrary("androidx-compose-bom").get()
add("implementation", platform(bom))
@ -53,48 +51,22 @@ internal fun Project.configureAndroidCompose(
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs += buildComposeMetricsParameters()
freeCompilerArgs += stabilityConfiguration()
freeCompilerArgs += strongSkippingConfiguration()
}
}
}
extensions.configure<ComposeCompilerGradlePluginExtension> {
fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
fun Provider<*>.relativeToRootProject(dir: String) = flatMap {
rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir))
}.map { it.dir(dir) }
private fun Project.buildComposeMetricsParameters(): List<String> {
val metricParameters = mutableListOf<String>()
val enableMetricsProvider = project.providers.gradleProperty("enableComposeCompilerMetrics")
val relativePath = projectDir.relativeTo(rootDir)
val buildDir = layout.buildDirectory.get().asFile
val enableMetrics = (enableMetricsProvider.orNull == "true")
if (enableMetrics) {
val metricsFolder = buildDir.resolve("compose-metrics").resolve(relativePath)
metricParameters.add("-P")
metricParameters.add(
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath,
)
}
project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue()
.relativeToRootProject("compose-metrics")
.let(metricsDestination::set)
val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports")
val enableReports = (enableReportsProvider.orNull == "true")
if (enableReports) {
val reportsFolder = buildDir.resolve("compose-reports").resolve(relativePath)
metricParameters.add("-P")
metricParameters.add(
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath
)
}
return metricParameters.toList()
}
project.providers.gradleProperty("enableComposeCompilerReports").onlyIfTrue()
.relativeToRootProject("compose-reports")
.let(reportsDestination::set)
private fun Project.stabilityConfiguration() = listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=${project.rootDir.absolutePath}/compose_compiler_config.conf",
)
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("compose_compiler_config.conf")
private fun Project.strongSkippingConfiguration() = listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true",
)
enableStrongSkippingMode = true
}
}

@ -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<GenerateBadgingTask>(generateBadgingTaskName) {
apk.set(
variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE),
)
aapt2Executable.set(
File(
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(
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<CheckBadgingTask>(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"),
)
}
}
}

@ -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<RegularFile> = myObjFactory.listProperty(RegularFile::class.java)
val allDirectories: ListProperty<Directory> = myObjFactory.listProperty(Directory::class.java)
val allDirectories: ListProperty<Directory> =
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,22 +81,27 @@ 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") },
)
}

@ -20,11 +20,14 @@ import com.android.build.api.dsl.CommonExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
/**
* Configure base Kotlin with Android options
@ -48,7 +51,7 @@ internal fun Project.configureKotlinAndroid(
}
}
configureKotlin()
configureKotlin<KotlinAndroidProjectExtension>()
dependencies {
add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get())
@ -66,26 +69,26 @@ internal fun Project.configureKotlinJvm() {
targetCompatibility = JavaVersion.VERSION_11
}
configureKotlin()
configureKotlin<KotlinJvmProjectExtension>()
}
/**
* Configure base Kotlin options
*/
private fun Project.configureKotlin() {
// Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
// Set JVM target to 11
jvmTarget = JavaVersion.VERSION_11.toString()
private inline fun <reified T : KotlinTopLevelExtension> Project.configureKotlin() = configure<T> {
// Treat all Kotlin warnings as errors (disabled by default)
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
val warningsAsErrors: String? by project
when (this) {
is KotlinAndroidProjectExtension -> compilerOptions
is KotlinJvmProjectExtension -> compilerOptions
else -> TODO("Unsupported project extension $this ${T::class}")
}.apply {
jvmTarget = JvmTarget.JVM_11
allWarningsAsErrors = warningsAsErrors.toBoolean()
freeCompilerArgs = freeCompilerArgs + listOf(
freeCompilerArgs.add(
// Enable experimental coroutines APIs, including Flow
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
)
}
}
}

@ -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
}
}
}

@ -36,6 +36,7 @@ plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.android.test) apply false
alias(libs.plugins.baselineprofile) apply false
alias(libs.plugins.compose) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.dependencyGuard) apply false
@ -49,13 +50,3 @@ plugins {
alias(libs.plugins.room) apply false
alias(libs.plugins.module.graph) apply true // Plugin applied to allow module graph generation
}
// Task to print all the module paths in the project e.g. :core:data
// Used by module graph generator script
tasks.register("printModulePaths") {
subprojects {
if (subprojects.size == 0) {
println(this.path)
}
}
}

@ -34,6 +34,8 @@ dependencies {
api(libs.androidx.compose.foundation.layout)
api(libs.androidx.compose.material.iconsExtended)
api(libs.androidx.compose.material3)
api(libs.androidx.compose.material3.adaptive)
api(libs.androidx.compose.material3.navigationSuite)
api(libs.androidx.compose.runtime)
api(libs.androidx.compose.ui.util)

@ -23,10 +23,20 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.NavigationRailItemDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@ -165,6 +175,101 @@ fun NiaNavigationRail(
)
}
/**
* Now in Android navigation suite scaffold with item and content slots.
* Wraps Material 3 [NavigationSuiteScaffold].
*
* @param modifier Modifier to be applied to the navigation suite scaffold.
* @param navigationSuiteItems A slot to display multiple items via [NiaNavigationSuiteScope].
* @param windowAdaptiveInfo The window adaptive info.
* @param content The app content inside the scaffold.
*/
@OptIn(
ExperimentalMaterial3AdaptiveNavigationSuiteApi::class,
ExperimentalMaterial3AdaptiveApi::class,
)
@Composable
fun NiaNavigationSuiteScaffold(
navigationSuiteItems: NiaNavigationSuiteScope.() -> Unit,
modifier: Modifier = Modifier,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
content: @Composable () -> Unit,
) {
val layoutType = NavigationSuiteScaffoldDefaults
.calculateFromAdaptiveInfo(windowAdaptiveInfo)
val navigationSuiteItemColors = NavigationSuiteItemColors(
navigationBarItemColors = NavigationBarItemDefaults.colors(
selectedIconColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedIconColor = NiaNavigationDefaults.navigationContentColor(),
selectedTextColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedTextColor = NiaNavigationDefaults.navigationContentColor(),
indicatorColor = NiaNavigationDefaults.navigationIndicatorColor(),
),
navigationRailItemColors = NavigationRailItemDefaults.colors(
selectedIconColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedIconColor = NiaNavigationDefaults.navigationContentColor(),
selectedTextColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedTextColor = NiaNavigationDefaults.navigationContentColor(),
indicatorColor = NiaNavigationDefaults.navigationIndicatorColor(),
),
navigationDrawerItemColors = NavigationDrawerItemDefaults.colors(
selectedIconColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedIconColor = NiaNavigationDefaults.navigationContentColor(),
selectedTextColor = NiaNavigationDefaults.navigationSelectedItemColor(),
unselectedTextColor = NiaNavigationDefaults.navigationContentColor(),
),
)
NavigationSuiteScaffold(
navigationSuiteItems = {
NiaNavigationSuiteScope(
navigationSuiteScope = this,
navigationSuiteItemColors = navigationSuiteItemColors,
).run(navigationSuiteItems)
},
layoutType = layoutType,
containerColor = Color.Transparent,
navigationSuiteColors = NavigationSuiteDefaults.colors(
navigationBarContentColor = NiaNavigationDefaults.navigationContentColor(),
navigationRailContainerColor = Color.Transparent,
),
modifier = modifier,
) {
content()
}
}
/**
* A wrapper around [NavigationSuiteScope] to declare navigation items.
*/
@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
class NiaNavigationSuiteScope internal constructor(
private val navigationSuiteScope: NavigationSuiteScope,
private val navigationSuiteItemColors: NavigationSuiteItemColors,
) {
fun item(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
icon: @Composable () -> Unit,
selectedIcon: @Composable () -> Unit = icon,
label: @Composable (() -> Unit)? = null,
) = navigationSuiteScope.item(
selected = selected,
onClick = onClick,
icon = {
if (selected) {
selectedIcon()
} else {
icon()
}
},
label = label,
colors = navigationSuiteItemColors,
modifier = modifier,
)
}
@ThemePreviews
@Composable
fun NiaNavigationBarPreview() {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 220 KiB

@ -31,6 +31,27 @@ 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.
if grep -P "" /dev/null > /dev/null 2>&1; then
GREP_COMMAND=grep
elif command -v ggrep &> /dev/null; then
GREP_COMMAND=ggrep
else
echo "You don't have a version of 'grep' installed which supports Perl regular expressions."
echo "On MacOS you can install one using Homebrew with the command: 'brew install grep'"
exit 1
fi
# Initialize an array to store excluded modules
excluded_modules=()
@ -50,7 +71,7 @@ while [[ $# -gt 0 ]]; do
done
# Get the module paths
module_paths=$(./gradlew -q printModulePaths --no-configuration-cache)
module_paths=$(${GREP_COMMAND} -oP 'include\("\K[^"]+' settings.gradle.kts)
# Ensure the output directory exists
mkdir -p docs/images/graphs/
@ -100,11 +121,9 @@ echo "$module_paths" | while read -r module_path; do
-Pmodules.graph.output.gv="/tmp/${file_name}.gv" \
-Pmodules.graph.of.module="${module_path}" </dev/null
# Convert to SVG using dot, remove unnecessary comments, and reformat
# Convert to SVG using dot, and cleanup/compress using svgo
dot -Tsvg "/tmp/${file_name}.gv" |
sed 's/<!--/\x0<!--/g;s/-->/-->\x0/g' | grep -zv '^<!--' | tr -d '\0' |
xmllint --format - \
> "docs/images/graphs/${file_name}.svg"
svgo --multipass --pretty --output="docs/images/graphs/${file_name}.svg" -
# Remove the temporary .gv file
rm "/tmp/${file_name}.gv"
fi

@ -3,21 +3,21 @@ accompanist = "0.34.0"
androidDesugarJdkLibs = "2.0.4"
# AGP and tools should be updated together
androidGradlePlugin = "8.4.0"
androidTools = "31.4.0"
androidTools = "31.4.1"
androidxActivity = "1.8.2"
androidxAppCompat = "1.6.1"
androidxAppCompat = "1.7.0"
androidxBrowser = "1.8.0"
androidxComposeAlpha = "1.7.0-beta01"
androidxComposeBom = "2024.02.02"
androidxComposeCompiler = "1.5.8"
androidxComposeUiTest = "1.7.0-alpha06"
androidxComposeMaterial3Adaptive = "1.0.0-alpha10"
androidxComposeMaterial3Adaptive = "1.0.0-beta01"
androidxComposeMaterial3AdaptiveNavigationSuite = "1.3.0-beta01"
androidxComposeRuntimeTracing = "1.0.0-beta01"
androidxCore = "1.12.0"
androidxCoreSplashscreen = "1.0.1"
androidxDataStore = "1.0.0"
androidxEspresso = "3.5.1"
androidxHiltNavigationCompose = "1.2.0"
androidxLifecycle = "2.7.0"
androidxLifecycle = "2.8.1"
androidxMacroBenchmark = "1.2.4"
androidxMetrics = "1.0.0-alpha04"
androidxNavigation = "2.8.0-alpha06"
@ -27,7 +27,7 @@ androidxTestExt = "1.1.5"
androidxTestRules = "1.5.0"
androidxTestRunner = "1.5.2"
androidxTracing = "1.3.0-alpha02"
androidxUiAutomator = "2.2.0"
androidxUiAutomator = "2.3.0"
androidxWindowManager = "1.3.0-alpha03"
androidxWork = "2.9.0"
coil = "2.6.0"
@ -38,22 +38,22 @@ firebasePerfPlugin = "1.4.2"
gmsPlugin = "4.4.1"
googleOss = "17.0.1"
googleOssPlugin = "0.10.6"
hilt = "2.51"
hilt = "2.51.1"
hiltExt = "1.1.0"
jacoco = "0.8.7"
junit4 = "4.13.2"
kotlin = "1.9.22"
kotlin = "2.0.0"
kotlinxCoroutines = "1.8.0"
kotlinxDatetime = "0.5.0"
kotlinxSerializationJson = "1.6.3"
ksp = "1.9.22-1.0.18"
ksp = "2.0.0-1.0.21"
moduleGraph = "2.5.0"
okhttp = "4.12.0"
protobuf = "4.26.0"
protobuf = "4.26.1"
protobufPlugin = "0.9.4"
retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "1.0.0"
robolectric = "4.11.1"
robolectric = "4.12.2"
roborazzi = "1.7.0"
room = "2.6.1"
secrets = "2.0.1"
@ -71,17 +71,18 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" }
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidxComposeAlpha" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-material3-navigationSuite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite", version.ref = "androidxComposeMaterial3AdaptiveNavigationSuite" }
androidx-compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" }
androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "androidxComposeUiTest" }
androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "androidxComposeAlpha" }
androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
@ -147,6 +148,7 @@ turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine
# Dependencies of the included build-logic
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" }
compose-gradlePlugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "firebaseCrashlyticsPlugin" }
firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
@ -158,6 +160,7 @@ android-application = { id = "com.android.application", version.ref = "androidGr
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" }
baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidxMacroBenchmark"}
compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" }
firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerfPlugin" }

@ -14,6 +14,7 @@
* limitations under the License.
*/
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@ -29,9 +30,9 @@ java {
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}

@ -24,7 +24,7 @@ pluginManagement {
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
google()
mavenCentral()
@ -63,4 +63,3 @@ include(":lint")
include(":sync:work")
include(":sync:sync-test")
include(":ui-test-hilt-manifest")

Loading…
Cancel
Save