Merge branch 'android-remote-main' into api-21

Change-Id: Ib9d2de121c797d97d94e6074cc1da4a26d880a2c
pull/1837/head
Jaehwa Noh 9 months ago
commit 525c6aeb2b

@ -1,25 +0,0 @@
# 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

@ -0,0 +1,9 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>android/.github:renovate-config"
],
"baseBranches": [
"main"
]
}

@ -18,6 +18,7 @@ jobs:
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write
security-events: write
timeout-minutes: 60 timeout-minutes: 60
@ -116,19 +117,8 @@ jobs:
- name: Run local tests - name: Run local tests
run: ./gradlew testDemoDebug :lint:test run: ./gradlew testDemoDebug :lint:test
- name: Setup GMD
run: ./gradlew :benchmarks:pixel6Api33Setup
--info
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
- name: Build all build type and flavor permutations - name: Build all build type and flavor permutations
run: ./gradlew :app:assemble :benchmarks:assemble -Pandroidx.baselineprofile.skipgeneration run: ./gradlew :app:assemble
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
-Pandroid.experimental.androidTest.numManagedDeviceShards=1
-Pandroid.experimental.testOptions.managedDevices.maxConcurrentDevices=1
-Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=5
- name: Upload build outputs (APKs) - name: Upload build outputs (APKs)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@ -161,7 +151,7 @@ jobs:
path: '**/build/reports/lint-results-*.html' path: '**/build/reports/lint-results-*.html'
- name: Upload lint reports (SARIF) - name: Upload lint reports (SARIF)
if: always() if: ${{ !cancelled() && hashFiles('**/*.sarif') != '' }}
uses: github/codeql-action/upload-sarif@v3 uses: github/codeql-action/upload-sarif@v3
with: with:
sarif_file: './' sarif_file: './'
@ -209,9 +199,6 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with:
validate-wrappers: true
gradle-home-cache-cleanup: true
- name: Build projects and run instrumentation tests - name: Build projects and run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2

@ -52,8 +52,9 @@ jobs:
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
- name: Build all build type and flavor permutations including baseline profiles # This generates both baseline and startup profile and adds them into the generated folder
run: ./gradlew :app:assemble - name: Generate Baseline Profile
run: ./gradlew :app:generateReleaseBaselineProfile
-Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true --stacktrace

@ -12,45 +12,45 @@ androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-jvm:1.4.4
androidx.collection:collection-ktx:1.4.4 androidx.collection:collection-ktx:1.4.4
androidx.collection:collection:1.4.4 androidx.collection:collection:1.4.4
androidx.compose.animation:animation-android:1.7.5 androidx.compose.animation:animation-android:1.7.6
androidx.compose.animation:animation-core-android:1.7.5 androidx.compose.animation:animation-core-android:1.7.6
androidx.compose.animation:animation-core:1.7.5 androidx.compose.animation:animation-core:1.7.6
androidx.compose.animation:animation:1.7.5 androidx.compose.animation:animation:1.7.6
androidx.compose.foundation:foundation-android:1.7.5 androidx.compose.foundation:foundation-android:1.7.6
androidx.compose.foundation:foundation-layout-android:1.7.5 androidx.compose.foundation:foundation-layout-android:1.7.6
androidx.compose.foundation:foundation-layout:1.7.5 androidx.compose.foundation:foundation-layout:1.7.6
androidx.compose.foundation:foundation:1.7.5 androidx.compose.foundation:foundation:1.7.6
androidx.compose.material3.adaptive:adaptive-android:1.0.0 androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive:1.0.0 androidx.compose.material3.adaptive:adaptive:1.0.0
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1
androidx.compose.material3:material3-adaptive-navigation-suite:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite:1.3.1
androidx.compose.material3:material3-android:1.3.1 androidx.compose.material3:material3-android:1.3.1
androidx.compose.material3:material3:1.3.1 androidx.compose.material3:material3:1.3.1
androidx.compose.material:material-icons-core-android:1.7.5 androidx.compose.material:material-icons-core-android:1.7.6
androidx.compose.material:material-icons-core:1.7.5 androidx.compose.material:material-icons-core:1.7.6
androidx.compose.material:material-icons-extended-android:1.7.5 androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.5 androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.5 androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.5 androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.7.5 androidx.compose.runtime:runtime-android:1.7.6
androidx.compose.runtime:runtime-saveable-android:1.7.5 androidx.compose.runtime:runtime-saveable-android:1.7.6
androidx.compose.runtime:runtime-saveable:1.7.5 androidx.compose.runtime:runtime-saveable:1.7.6
androidx.compose.runtime:runtime:1.7.5 androidx.compose.runtime:runtime:1.7.6
androidx.compose.ui:ui-android:1.7.5 androidx.compose.ui:ui-android:1.7.6
androidx.compose.ui:ui-geometry-android:1.7.5 androidx.compose.ui:ui-geometry-android:1.7.6
androidx.compose.ui:ui-geometry:1.7.5 androidx.compose.ui:ui-geometry:1.7.6
androidx.compose.ui:ui-graphics-android:1.7.5 androidx.compose.ui:ui-graphics-android:1.7.6
androidx.compose.ui:ui-graphics:1.7.5 androidx.compose.ui:ui-graphics:1.7.6
androidx.compose.ui:ui-text-android:1.7.5 androidx.compose.ui:ui-text-android:1.7.6
androidx.compose.ui:ui-text:1.7.5 androidx.compose.ui:ui-text:1.7.6
androidx.compose.ui:ui-tooling-preview-android:1.7.5 androidx.compose.ui:ui-tooling-preview-android:1.7.6
androidx.compose.ui:ui-tooling-preview:1.7.5 androidx.compose.ui:ui-tooling-preview:1.7.6
androidx.compose.ui:ui-unit-android:1.7.5 androidx.compose.ui:ui-unit-android:1.7.6
androidx.compose.ui:ui-unit:1.7.5 androidx.compose.ui:ui-unit:1.7.6
androidx.compose.ui:ui-util-android:1.7.5 androidx.compose.ui:ui-util-android:1.7.6
androidx.compose.ui:ui-util:1.7.5 androidx.compose.ui:ui-util:1.7.6
androidx.compose.ui:ui:1.7.5 androidx.compose.ui:ui:1.7.6
androidx.compose:compose-bom:2024.11.00 androidx.compose:compose-bom:2024.12.01
androidx.concurrent:concurrent-futures:1.1.0 androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.13.1 androidx.core:core-ktx:1.13.1
androidx.core:core:1.13.1 androidx.core:core:1.13.1
@ -96,10 +96,10 @@ androidx.window:window-core:1.3.0
androidx.window:window:1.3.0 androidx.window:window:1.3.0
com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-drawablepainter:0.32.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.52 com.google.dagger:dagger-lint-aar:2.53.1
com.google.dagger:dagger:2.52 com.google.dagger:dagger:2.53.1
com.google.dagger:hilt-android:2.52 com.google.dagger:hilt-android:2.53.1
com.google.dagger:hilt-core:2.52 com.google.dagger:hilt-core:2.53.1
com.google.guava:listenablefuture:1.0 com.google.guava:listenablefuture:1.0
com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.0 com.squareup.okio:okio-jvm:3.9.0
@ -110,10 +110,10 @@ io.coil-kt:coil-compose:2.7.0
io.coil-kt:coil:2.7.0 io.coil-kt:coil:2.7.0
jakarta.inject:jakarta.inject-api:2.0.1 jakarta.inject:jakarta.inject-api:2.0.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
org.jetbrains.kotlin:kotlin-stdlib:2.0.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1
@ -121,3 +121,4 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1
org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime:0.6.1
org.jetbrains:annotations:23.0.0 org.jetbrains:annotations:23.0.0
org.jspecify:jspecify:1.0.0

@ -36,9 +36,6 @@ android {
// Custom test runner to set up Hilt dependency graph // Custom test runner to set up Hilt dependency graph
testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner"
vectorDrawables {
useSupportLibrary = true
}
} }
buildTypes { buildTypes {

@ -13,14 +13,14 @@ androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-jvm:1.4.4
androidx.collection:collection-ktx:1.4.4 androidx.collection:collection-ktx:1.4.4
androidx.collection:collection:1.4.4 androidx.collection:collection:1.4.4
androidx.compose.animation:animation-android:1.7.5 androidx.compose.animation:animation-android:1.7.6
androidx.compose.animation:animation-core-android:1.7.5 androidx.compose.animation:animation-core-android:1.7.6
androidx.compose.animation:animation-core:1.7.5 androidx.compose.animation:animation-core:1.7.6
androidx.compose.animation:animation:1.7.5 androidx.compose.animation:animation:1.7.6
androidx.compose.foundation:foundation-android:1.7.5 androidx.compose.foundation:foundation-android:1.7.6
androidx.compose.foundation:foundation-layout-android:1.7.5 androidx.compose.foundation:foundation-layout-android:1.7.6
androidx.compose.foundation:foundation-layout:1.7.5 androidx.compose.foundation:foundation-layout:1.7.6
androidx.compose.foundation:foundation:1.7.5 androidx.compose.foundation:foundation:1.7.6
androidx.compose.material3.adaptive:adaptive-android:1.0.0 androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0 androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout:1.0.0 androidx.compose.material3.adaptive:adaptive-layout:1.0.0
@ -33,36 +33,37 @@ androidx.compose.material3:material3-android:1.3.1
androidx.compose.material3:material3-window-size-class-android:1.3.1 androidx.compose.material3:material3-window-size-class-android:1.3.1
androidx.compose.material3:material3-window-size-class:1.3.1 androidx.compose.material3:material3-window-size-class:1.3.1
androidx.compose.material3:material3:1.3.1 androidx.compose.material3:material3:1.3.1
androidx.compose.material:material-icons-core-android:1.7.5 androidx.compose.material:material-icons-core-android:1.7.6
androidx.compose.material:material-icons-core:1.7.5 androidx.compose.material:material-icons-core:1.7.6
androidx.compose.material:material-icons-extended-android:1.7.5 androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.5 androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.5 androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.5 androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.7.5 androidx.compose.runtime:runtime-android:1.7.6
androidx.compose.runtime:runtime-saveable-android:1.7.5 androidx.compose.runtime:runtime-saveable-android:1.7.6
androidx.compose.runtime:runtime-saveable:1.7.5 androidx.compose.runtime:runtime-saveable:1.7.6
androidx.compose.runtime:runtime-tracing:1.7.5 androidx.compose.runtime:runtime-tracing:1.7.6
androidx.compose.runtime:runtime:1.7.5 androidx.compose.runtime:runtime:1.7.6
androidx.compose.ui:ui-android:1.7.5 androidx.compose.ui:ui-android:1.7.6
androidx.compose.ui:ui-geometry-android:1.7.5 androidx.compose.ui:ui-geometry-android:1.7.6
androidx.compose.ui:ui-geometry:1.7.5 androidx.compose.ui:ui-geometry:1.7.6
androidx.compose.ui:ui-graphics-android:1.7.5 androidx.compose.ui:ui-graphics-android:1.7.6
androidx.compose.ui:ui-graphics:1.7.5 androidx.compose.ui:ui-graphics:1.7.6
androidx.compose.ui:ui-text-android:1.7.5 androidx.compose.ui:ui-text-android:1.7.6
androidx.compose.ui:ui-text:1.7.5 androidx.compose.ui:ui-text:1.7.6
androidx.compose.ui:ui-tooling-preview-android:1.7.5 androidx.compose.ui:ui-tooling-preview-android:1.7.6
androidx.compose.ui:ui-tooling-preview:1.7.5 androidx.compose.ui:ui-tooling-preview:1.7.6
androidx.compose.ui:ui-unit-android:1.7.5 androidx.compose.ui:ui-unit-android:1.7.6
androidx.compose.ui:ui-unit:1.7.5 androidx.compose.ui:ui-unit:1.7.6
androidx.compose.ui:ui-util-android:1.7.5 androidx.compose.ui:ui-util-android:1.7.6
androidx.compose.ui:ui-util:1.7.5 androidx.compose.ui:ui-util:1.7.6
androidx.compose.ui:ui:1.7.5 androidx.compose.ui:ui:1.7.6
androidx.compose:compose-bom:2024.11.00 androidx.compose:compose-bom:2024.12.01
androidx.concurrent:concurrent-futures-ktx:1.1.0
androidx.concurrent:concurrent-futures:1.1.0 androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.13.1 androidx.core:core-ktx:1.15.0
androidx.core:core-splashscreen:1.0.1 androidx.core:core-splashscreen:1.0.1
androidx.core:core:1.13.1 androidx.core:core:1.15.0
androidx.cursoradapter:cursoradapter:1.0.0 androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.0.0 androidx.customview:customview:1.0.0
@ -112,11 +113,11 @@ androidx.lifecycle:lifecycle-viewmodel:2.8.7
androidx.loader:loader:1.0.0 androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
androidx.metrics:metrics-performance:1.0.0-beta01 androidx.metrics:metrics-performance:1.0.0-beta01
androidx.navigation:navigation-common-ktx:2.8.4 androidx.navigation:navigation-common-ktx:2.8.5
androidx.navigation:navigation-common:2.8.4 androidx.navigation:navigation-common:2.8.5
androidx.navigation:navigation-compose:2.8.4 androidx.navigation:navigation-compose:2.8.5
androidx.navigation:navigation-runtime-ktx:2.8.4 androidx.navigation:navigation-runtime-ktx:2.8.5
androidx.navigation:navigation-runtime:2.8.4 androidx.navigation:navigation-runtime:2.8.5
androidx.print:print:1.0.0 androidx.print:print:1.0.0
androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
@ -141,11 +142,11 @@ androidx.window.extensions.core:core:1.0.0
androidx.window:window-core-android:1.3.0 androidx.window:window-core-android:1.3.0
androidx.window:window-core:1.3.0 androidx.window:window-core:1.3.0
androidx.window:window:1.3.0 androidx.window:window:1.3.0
androidx.work:work-runtime-ktx:2.9.1 androidx.work:work-runtime-ktx:2.10.0
androidx.work:work-runtime:2.9.1 androidx.work:work-runtime:2.10.0
com.caverock:androidsvg-aar:1.4 com.caverock:androidsvg-aar:1.4
com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-drawablepainter:0.32.0
com.google.accompanist:accompanist-permissions:0.36.0 com.google.accompanist:accompanist-permissions:0.37.0
com.google.android.datatransport:transport-api:3.2.0 com.google.android.datatransport:transport-api:3.2.0
com.google.android.datatransport:transport-backend-cct:3.3.0 com.google.android.datatransport:transport-backend-cct:3.3.0
com.google.android.datatransport:transport-runtime:3.3.0 com.google.android.datatransport:transport-runtime:3.3.0
@ -163,10 +164,10 @@ com.google.android.gms:play-services-oss-licenses:17.1.0
com.google.android.gms:play-services-stats:17.0.2 com.google.android.gms:play-services-stats:17.0.2
com.google.android.gms:play-services-tasks:18.2.0 com.google.android.gms:play-services-tasks:18.2.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.52 com.google.dagger:dagger-lint-aar:2.53.1
com.google.dagger:dagger:2.52 com.google.dagger:dagger:2.53.1
com.google.dagger:hilt-android:2.52 com.google.dagger:hilt-android:2.53.1
com.google.dagger:hilt-core:2.52 com.google.dagger:hilt-core:2.53.1
com.google.errorprone:error_prone_annotations:2.26.0 com.google.errorprone:error_prone_annotations:2.26.0
com.google.firebase:firebase-abt:21.1.1 com.google.firebase:firebase-abt:21.1.1
com.google.firebase:firebase-analytics:22.1.2 com.google.firebase:firebase-analytics:22.1.2
@ -193,8 +194,8 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:31.1-android com.google.guava:guava:31.1-android
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.protobuf:protobuf-javalite:4.28.2 com.google.protobuf:protobuf-javalite:4.29.2
com.google.protobuf:protobuf-kotlin-lite:4.28.2 com.google.protobuf:protobuf-kotlin-lite:4.29.2
com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.0 com.squareup.okio:okio-jvm:3.9.0
@ -211,10 +212,10 @@ javax.inject:javax.inject:1
org.checkerframework:checker-qual:3.12.0 org.checkerframework:checker-qual:3.12.0
org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.22 org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.22
org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22 org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
org.jetbrains.kotlin:kotlin-stdlib:2.0.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0
@ -223,9 +224,10 @@ org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.9.0
org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1
org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime:0.6.1
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3
org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.3 org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3
org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3 org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3
org.jetbrains:annotations:23.0.0 org.jetbrains:annotations:23.0.0
org.jspecify:jspecify:1.0.0

@ -1,6 +1,6 @@
package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14' package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
sdkVersion:'21' sdkVersion:'21'
targetSdkVersion:'34' targetSdkVersion:'35'
uses-permission: name='android.permission.INTERNET' uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.POST_NOTIFICATIONS' uses-permission: name='android.permission.POST_NOTIFICATIONS'

@ -41,6 +41,7 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="uiMode"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

@ -22,10 +22,7 @@ import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@ -35,21 +32,22 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.metrics.performance.JankStats import androidx.metrics.performance.JankStats
import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor 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.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.NiaApp
import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState
import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -81,53 +79,60 @@ class MainActivity : ComponentActivity() {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
var uiState: MainActivityUiState by mutableStateOf(Loading) // We keep this as a mutable state, so that we can track changes inside the composition.
// This allows us to react to dark/light mode changes.
var themeSettings by mutableStateOf(
ThemeSettings(
darkTheme = resources.configuration.isSystemInDarkTheme,
androidTheme = Loading.shouldUseAndroidTheme,
disableDynamicTheming = Loading.shouldDisableDynamicTheming,
),
)
// Update the uiState // Update the uiState
lifecycleScope.launch { lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState combine(
.onEach { uiState = it } isSystemInDarkTheme(),
.collect() viewModel.uiState,
} ) { systemDark, uiState ->
} ThemeSettings(
darkTheme = uiState.shouldUseDarkTheme(systemDark),
// Keep the splash screen on-screen until the UI state is loaded. This condition is androidTheme = uiState.shouldUseAndroidTheme,
// evaluated each time the app needs to be redrawn so it should be fast to avoid blocking disableDynamicTheming = uiState.shouldDisableDynamicTheming,
// the UI. )
splashScreen.setKeepOnScreenCondition {
when (uiState) {
Loading -> true
is Success -> false
}
} }
.onEach { themeSettings = it }
.map { it.darkTheme }
.distinctUntilChanged()
.collect { darkTheme ->
trace("niaEdgeToEdge") {
// Turn off the decor fitting system windows, which allows us to handle insets, // Turn off the decor fitting system windows, which allows us to handle insets,
// including IME animations, and go edge-to-edge // including IME animations, and go edge-to-edge.
// This also sets up the initial system bar style based on the platform theme
enableEdgeToEdge()
setContent {
val darkTheme = shouldUseDarkTheme(uiState)
// Update the edge to edge configuration to match the theme
// This is the same parameters as the default enableEdgeToEdge call, but we manually // This is the same parameters as the default enableEdgeToEdge call, but we manually
// resolve whether or not to show dark theme using uiState, since it can be different // resolve whether or not to show dark theme using uiState, since it can be different
// than the configuration's dark theme value based on the user preference. // than the configuration's dark theme value based on the user preference.
DisposableEffect(darkTheme) {
enableEdgeToEdge( enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto( statusBarStyle = SystemBarStyle.auto(
android.graphics.Color.TRANSPARENT, lightScrim = android.graphics.Color.TRANSPARENT,
android.graphics.Color.TRANSPARENT, darkScrim = android.graphics.Color.TRANSPARENT,
) { darkTheme }, ) { darkTheme },
navigationBarStyle = SystemBarStyle.auto( navigationBarStyle = SystemBarStyle.auto(
lightScrim, lightScrim = lightScrim,
darkScrim, darkScrim = darkScrim,
) { darkTheme }, ) { darkTheme },
) )
onDispose {}
} }
}
}
}
// Keep the splash screen on-screen until the UI state is loaded. This condition is
// evaluated each time the app needs to be redrawn so it should be fast to avoid blocking
// the UI.
splashScreen.setKeepOnScreenCondition { viewModel.uiState.value.shouldKeepSplashScreen() }
setContent {
val appState = rememberNiaAppState( val appState = rememberNiaAppState(
networkMonitor = networkMonitor, networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,
@ -141,9 +146,9 @@ class MainActivity : ComponentActivity() {
LocalTimeZone provides currentTimeZone, LocalTimeZone provides currentTimeZone,
) { ) {
NiaTheme( NiaTheme(
darkTheme = darkTheme, darkTheme = themeSettings.darkTheme,
androidTheme = shouldUseAndroidTheme(uiState), androidTheme = themeSettings.androidTheme,
disableDynamicTheming = shouldDisableDynamicTheming(uiState), disableDynamicTheming = themeSettings.disableDynamicTheming,
) { ) {
NiaApp(appState) NiaApp(appState)
} }
@ -162,47 +167,6 @@ class MainActivity : ComponentActivity() {
} }
} }
/**
* Returns `true` if the Android theme should be used, as a function of the [uiState].
*/
@Composable
private fun shouldUseAndroidTheme(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> false
is Success -> when (uiState.userData.themeBrand) {
ThemeBrand.DEFAULT -> false
ThemeBrand.ANDROID -> true
}
}
/**
* Returns `true` if the dynamic color is disabled, as a function of the [uiState].
*/
@Composable
private fun shouldDisableDynamicTheming(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> false
is Success -> !uiState.userData.useDynamicColor
}
/**
* Returns `true` if dark theme should be used, as a function of the [uiState] and the
* current system context.
*/
@Composable
private fun shouldUseDarkTheme(
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> isSystemInDarkTheme()
is Success -> when (uiState.userData.darkThemeConfig) {
DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
DarkThemeConfig.LIGHT -> false
DarkThemeConfig.DARK -> true
}
}
/** /**
* The default light scrim, as defined by androidx and the platform: * The default light scrim, as defined by androidx and the platform:
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598 * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598
@ -214,3 +178,13 @@ private val lightScrim = android.graphics.Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=40-44;drc=27e7d52e8604a080133e8b842db10c89b4482598 * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=40-44;drc=27e7d52e8604a080133e8b842db10c89b4482598
*/ */
private val darkScrim = android.graphics.Color.argb(0x80, 0x1b, 0x1b, 0x1b) private val darkScrim = android.graphics.Color.argb(0x80, 0x1b, 0x1b, 0x1b)
/**
* Class for the system theme settings.
* This wrapping class allows us to combine all the changes and prevent unnecessary recompositions.
*/
data class ThemeSettings(
val darkTheme: Boolean,
val androidTheme: Boolean,
val disableDynamicTheming: Boolean,
)

@ -21,6 +21,8 @@ import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserData
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -44,5 +46,40 @@ class MainActivityViewModel @Inject constructor(
sealed interface MainActivityUiState { sealed interface MainActivityUiState {
data object Loading : MainActivityUiState data object Loading : MainActivityUiState
data class Success(val userData: UserData) : MainActivityUiState
data class Success(val userData: UserData) : MainActivityUiState {
override val shouldDisableDynamicTheming = !userData.useDynamicColor
override val shouldUseAndroidTheme: Boolean = when (userData.themeBrand) {
ThemeBrand.DEFAULT -> false
ThemeBrand.ANDROID -> true
}
override fun shouldUseDarkTheme(isSystemDarkTheme: Boolean) =
when (userData.darkThemeConfig) {
DarkThemeConfig.FOLLOW_SYSTEM -> isSystemDarkTheme
DarkThemeConfig.LIGHT -> false
DarkThemeConfig.DARK -> true
}
}
/**
* Returns `true` if the state wasn't loaded yet and it should keep showing the splash screen.
*/
fun shouldKeepSplashScreen() = this is Loading
/**
* Returns `true` if the dynamic color is disabled.
*/
val shouldDisableDynamicTheming: Boolean get() = true
/**
* Returns `true` if the Android theme should be used.
*/
val shouldUseAndroidTheme: Boolean get() = false
/**
* Returns `true` if dark theme should be used.
*/
fun shouldUseDarkTheme(isSystemDarkTheme: Boolean) = isSystemDarkTheme
} }

@ -0,0 +1,49 @@
/*
* 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.util
import android.content.res.Configuration
import androidx.activity.ComponentActivity
import androidx.core.util.Consumer
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
/**
* Convenience wrapper for dark mode checking
*/
val Configuration.isSystemInDarkTheme
get() = (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
/**
* Registers listener for configuration changes to retrieve whether system is in dark theme or not.
* Immediately upon subscribing, it sends the current value and then registers listener for changes.
*/
fun ComponentActivity.isSystemInDarkTheme() = callbackFlow {
channel.trySend(resources.configuration.isSystemInDarkTheme)
val listener = Consumer<Configuration> {
channel.trySend(it.isSystemInDarkTheme)
}
addOnConfigurationChangedListener(listener)
awaitClose { removeOnConfigurationChangedListener(listener) }
}
.distinctUntilChanged()
.conflate()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 97 KiB

@ -15,10 +15,10 @@
*/ */
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
`kotlin-dsl` `kotlin-dsl`
alias(libs.plugins.android.lint)
} }
group = "com.google.samples.apps.nowinandroid.buildlogic" group = "com.google.samples.apps.nowinandroid.buildlogic"
@ -46,6 +46,7 @@ dependencies {
compileOnly(libs.ksp.gradlePlugin) compileOnly(libs.ksp.gradlePlugin)
compileOnly(libs.room.gradlePlugin) compileOnly(libs.room.gradlePlugin)
implementation(libs.truth) implementation(libs.truth)
lintChecks(libs.androidx.lint.gradle)
} }
tasks { tasks {
@ -58,59 +59,59 @@ tasks {
gradlePlugin { gradlePlugin {
plugins { plugins {
register("androidApplicationCompose") { register("androidApplicationCompose") {
id = "nowinandroid.android.application.compose" id = libs.plugins.nowinandroid.android.application.compose.get().pluginId
implementationClass = "AndroidApplicationComposeConventionPlugin" implementationClass = "AndroidApplicationComposeConventionPlugin"
} }
register("androidApplication") { register("androidApplication") {
id = "nowinandroid.android.application" id = libs.plugins.nowinandroid.android.application.asProvider().get().pluginId
implementationClass = "AndroidApplicationConventionPlugin" implementationClass = "AndroidApplicationConventionPlugin"
} }
register("androidApplicationJacoco") { register("androidApplicationJacoco") {
id = "nowinandroid.android.application.jacoco" id = libs.plugins.nowinandroid.android.application.jacoco.get().pluginId
implementationClass = "AndroidApplicationJacocoConventionPlugin" implementationClass = "AndroidApplicationJacocoConventionPlugin"
} }
register("androidLibraryCompose") { register("androidLibraryCompose") {
id = "nowinandroid.android.library.compose" id = libs.plugins.nowinandroid.android.library.compose.get().pluginId
implementationClass = "AndroidLibraryComposeConventionPlugin" implementationClass = "AndroidLibraryComposeConventionPlugin"
} }
register("androidLibrary") { register("androidLibrary") {
id = "nowinandroid.android.library" id = libs.plugins.nowinandroid.android.library.asProvider().get().pluginId
implementationClass = "AndroidLibraryConventionPlugin" implementationClass = "AndroidLibraryConventionPlugin"
} }
register("androidFeature") { register("androidFeature") {
id = "nowinandroid.android.feature" id = libs.plugins.nowinandroid.android.feature.get().pluginId
implementationClass = "AndroidFeatureConventionPlugin" implementationClass = "AndroidFeatureConventionPlugin"
} }
register("androidLibraryJacoco") { register("androidLibraryJacoco") {
id = "nowinandroid.android.library.jacoco" id = libs.plugins.nowinandroid.android.library.jacoco.get().pluginId
implementationClass = "AndroidLibraryJacocoConventionPlugin" implementationClass = "AndroidLibraryJacocoConventionPlugin"
} }
register("androidTest") { register("androidTest") {
id = "nowinandroid.android.test" id = libs.plugins.nowinandroid.android.test.get().pluginId
implementationClass = "AndroidTestConventionPlugin" implementationClass = "AndroidTestConventionPlugin"
} }
register("hilt") { register("hilt") {
id = "nowinandroid.hilt" id = libs.plugins.nowinandroid.hilt.get().pluginId
implementationClass = "HiltConventionPlugin" implementationClass = "HiltConventionPlugin"
} }
register("androidRoom") { register("androidRoom") {
id = "nowinandroid.android.room" id = libs.plugins.nowinandroid.android.room.get().pluginId
implementationClass = "AndroidRoomConventionPlugin" implementationClass = "AndroidRoomConventionPlugin"
} }
register("androidFirebase") { register("androidFirebase") {
id = "nowinandroid.android.application.firebase" id = libs.plugins.nowinandroid.android.application.firebase.get().pluginId
implementationClass = "AndroidApplicationFirebaseConventionPlugin" implementationClass = "AndroidApplicationFirebaseConventionPlugin"
} }
register("androidFlavors") { register("androidFlavors") {
id = "nowinandroid.android.application.flavors" id = libs.plugins.nowinandroid.android.application.flavors.get().pluginId
implementationClass = "AndroidApplicationFlavorsConventionPlugin" implementationClass = "AndroidApplicationFlavorsConventionPlugin"
} }
register("androidLint") { register("androidLint") {
id = "nowinandroid.android.lint" id = libs.plugins.nowinandroid.android.lint.get().pluginId
implementationClass = "AndroidLintConventionPlugin" implementationClass = "AndroidLintConventionPlugin"
} }
register("jvmLibrary") { register("jvmLibrary") {
id = "nowinandroid.jvm.library" id = libs.plugins.nowinandroid.jvm.library.get().pluginId
implementationClass = "JvmLibraryConventionPlugin" implementationClass = "JvmLibraryConventionPlugin"
} }
} }

@ -38,7 +38,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
extensions.configure<ApplicationExtension> { extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
defaultConfig.targetSdk = 34 defaultConfig.targetSdk = 35
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
testOptions.animationsDisabled = true testOptions.animationsDisabled = true
configureGradleManagedDevices(this) configureGradleManagedDevices(this)

@ -34,7 +34,7 @@ class AndroidApplicationFirebaseConventionPlugin : Plugin<Project> {
dependencies { dependencies {
val bom = libs.findLibrary("firebase-bom").get() val bom = libs.findLibrary("firebase-bom").get()
add("implementation", platform(bom)) "implementation"(platform(bom))
"implementation"(libs.findLibrary("firebase.analytics").get()) "implementation"(libs.findLibrary("firebase.analytics").get())
"implementation"(libs.findLibrary("firebase.performance").get()) { "implementation"(libs.findLibrary("firebase.performance").get()) {
/* /*

@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.google.samples.apps.nowinandroid.configureJacoco import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
@ -25,7 +25,7 @@ class AndroidApplicationJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply("jacoco") pluginManager.apply("jacoco")
val androidExtension = extensions.getByType<BaseAppModuleExtension>() val androidExtension = extensions.getByType<ApplicationExtension>()
androidExtension.buildTypes.configureEach { androidExtension.buildTypes.configureEach {
enableAndroidTestCoverage = true enableAndroidTestCoverage = true

@ -36,18 +36,18 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
} }
dependencies { dependencies {
add("implementation", project(":core:ui")) "implementation"(project(":core:ui"))
add("implementation", project(":core:designsystem")) "implementation"(project(":core:designsystem"))
add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) "implementation"(libs.findLibrary("androidx.hilt.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) "implementation"(libs.findLibrary("androidx.lifecycle.runtimeCompose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) "implementation"(libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("androidx.navigation.compose").get()) "implementation"(libs.findLibrary("androidx.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) "implementation"(libs.findLibrary("androidx.tracing.ktx").get())
add("implementation", libs.findLibrary("kotlinx.serialization.json").get()) "implementation"(libs.findLibrary("kotlinx.serialization.json").get())
add("testImplementation", libs.findLibrary("androidx.navigation.testing").get()) "testImplementation"(libs.findLibrary("androidx.navigation.testing").get())
add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) "androidTestImplementation"(libs.findLibrary("androidx.lifecycle.runtimeTesting").get())
} }
} }
} }

@ -26,7 +26,6 @@ import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.kotlin
class AndroidLibraryConventionPlugin : Plugin<Project> { class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
@ -39,7 +38,7 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
extensions.configure<LibraryExtension> { extensions.configure<LibraryExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
defaultConfig.targetSdk = 34 defaultConfig.targetSdk = 35
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testOptions.animationsDisabled = true testOptions.animationsDisabled = true
configureFlavors(this) configureFlavors(this)
@ -53,10 +52,10 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
disableUnnecessaryAndroidTests(target) disableUnnecessaryAndroidTests(target)
} }
dependencies { dependencies {
add("androidTestImplementation", libs.findLibrary("kotlin.test").get()) "androidTestImplementation"(libs.findLibrary("kotlin.test").get())
add("testImplementation", libs.findLibrary("kotlin.test").get()) "testImplementation"(libs.findLibrary("kotlin.test").get())
add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) "implementation"(libs.findLibrary("androidx.tracing.ktx").get())
} }
} }
} }

@ -44,4 +44,5 @@ private fun Lint.configure() {
xmlReport = true xmlReport = true
sarifReport = true sarifReport = true
checkDependencies = true checkDependencies = true
disable += "GradleDependency"
} }

@ -41,9 +41,9 @@ class AndroidRoomConventionPlugin : Plugin<Project> {
} }
dependencies { dependencies {
add("implementation", libs.findLibrary("room.runtime").get()) "implementation"(libs.findLibrary("room.runtime").get())
add("implementation", libs.findLibrary("room.ktx").get()) "implementation"(libs.findLibrary("room.ktx").get())
add("ksp", libs.findLibrary("room.compiler").get()) "ksp"(libs.findLibrary("room.compiler").get())
} }
} }
} }

@ -31,7 +31,7 @@ class AndroidTestConventionPlugin : Plugin<Project> {
extensions.configure<TestExtension> { extensions.configure<TestExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
defaultConfig.targetSdk = 34 defaultConfig.targetSdk = 35
configureGradleManagedDevices(this) configureGradleManagedDevices(this)
} }
} }

@ -25,13 +25,13 @@ class HiltConventionPlugin : Plugin<Project> {
with(target) { with(target) {
pluginManager.apply("com.google.devtools.ksp") pluginManager.apply("com.google.devtools.ksp")
dependencies { dependencies {
add("ksp", libs.findLibrary("hilt.compiler").get()) "ksp"(libs.findLibrary("hilt.compiler").get())
} }
// Add support for Jvm Module, base on org.jetbrains.kotlin.jvm // Add support for Jvm Module, base on org.jetbrains.kotlin.jvm
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
dependencies { dependencies {
add("implementation", libs.findLibrary("hilt.core").get()) "implementation"(libs.findLibrary("hilt.core").get())
} }
} }
@ -39,7 +39,7 @@ class HiltConventionPlugin : Plugin<Project> {
pluginManager.withPlugin("com.android.base") { pluginManager.withPlugin("com.android.base") {
pluginManager.apply("dagger.hilt.android.plugin") pluginManager.apply("dagger.hilt.android.plugin")
dependencies { dependencies {
add("implementation", libs.findLibrary("hilt.android").get()) "implementation"(libs.findLibrary("hilt.android").get())
} }
} }
} }

@ -19,7 +19,6 @@ import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.kotlin
class JvmLibraryConventionPlugin : Plugin<Project> { class JvmLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
@ -30,7 +29,7 @@ class JvmLibraryConventionPlugin : Plugin<Project> {
} }
configureKotlinJvm() configureKotlinJvm()
dependencies { dependencies {
add("testImplementation", libs.findLibrary("kotlin.test").get()) "testImplementation"(libs.findLibrary("kotlin.test").get())
} }
} }
} }

@ -37,10 +37,10 @@ internal fun Project.configureAndroidCompose(
dependencies { dependencies {
val bom = libs.findLibrary("androidx-compose-bom").get() val bom = libs.findLibrary("androidx-compose-bom").get()
add("implementation", platform(bom)) "implementation"(platform(bom))
add("androidTestImplementation", platform(bom)) "androidTestImplementation"(platform(bom))
add("implementation", libs.findLibrary("androidx-compose-ui-tooling-preview").get()) "implementation"(libs.findLibrary("androidx-compose-ui-tooling-preview").get())
add("debugImplementation", libs.findLibrary("androidx-compose-ui-tooling").get()) "debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get())
} }
testOptions { testOptions {
@ -53,8 +53,10 @@ internal fun Project.configureAndroidCompose(
extensions.configure<ComposeCompilerGradlePluginExtension> { extensions.configure<ComposeCompilerGradlePluginExtension> {
fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } } fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
fun Provider<*>.relativeToRootProject(dir: String) = flatMap { fun Provider<*>.relativeToRootProject(dir: String) = map {
rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir)) isolated.rootProject.projectDirectory
.dir("build")
.dir(projectDir.toRelativeString(rootDir))
}.map { it.dir(dir) } }.map { it.dir(dir) }
project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue() project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue()
@ -66,6 +68,6 @@ internal fun Project.configureAndroidCompose(
.let(reportsDestination::set) .let(reportsDestination::set)
stabilityConfigurationFile = stabilityConfigurationFile =
rootProject.layout.projectDirectory.file("compose_compiler_config.conf") isolated.rootProject.projectDirectory.file("compose_compiler_config.conf")
} }
} }

@ -39,8 +39,6 @@ import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.register
import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.process.ExecOperations import org.gradle.process.ExecOperations
import java.io.File
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@CacheableTask @CacheableTask
@ -123,15 +121,17 @@ fun Project.configureBadgingTasks(
val generateBadging = val generateBadging =
tasks.register<GenerateBadgingTask>(generateBadgingTaskName) { tasks.register<GenerateBadgingTask>(generateBadgingTaskName) {
apk = variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE) apk = variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE)
aapt2Executable.set(
aapt2Executable = File( // TODO: Replace with `sdkComponents.aapt2` when it's available in AGP
baseExtension.sdkDirectory, // https://issuetracker.google.com/issues/376815836
componentsExtension.sdkComponents.sdkDirectory.map { directory ->
directory.file(
"${SdkConstants.FD_BUILD_TOOLS}/" + "${SdkConstants.FD_BUILD_TOOLS}/" +
"${baseExtension.buildToolsVersion}/" + "${baseExtension.buildToolsVersion}/" +
SdkConstants.FN_AAPT2, SdkConstants.FN_AAPT2,
) )
}
)
badging = project.layout.buildDirectory.file( badging = project.layout.buildDirectory.file(
"outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt",
) )
@ -140,7 +140,7 @@ fun Project.configureBadgingTasks(
val updateBadgingTaskName = "update${capitalizedVariantName}Badging" val updateBadgingTaskName = "update${capitalizedVariantName}Badging"
tasks.register<Copy>(updateBadgingTaskName) { tasks.register<Copy>(updateBadgingTaskName) {
from(generateBadging.get().badging) from(generateBadging.map(GenerateBadgingTask::badging))
into(project.layout.projectDirectory) into(project.layout.projectDirectory)
} }
@ -148,7 +148,7 @@ fun Project.configureBadgingTasks(
tasks.register<CheckBadgingTask>(checkBadgingTaskName) { tasks.register<CheckBadgingTask>(checkBadgingTaskName) {
goldenBadging = project.layout.projectDirectory.file("${variant.name}-badging.txt") goldenBadging = project.layout.projectDirectory.file("${variant.name}-badging.txt")
generatedBadging = generateBadging.get().badging generatedBadging.set(generateBadging.flatMap(GenerateBadgingTask::badging))
this.updateBadgingTaskName = updateBadgingTaskName this.updateBadgingTaskName = updateBadgingTaskName

@ -19,10 +19,12 @@ package com.google.samples.apps.nowinandroid
import com.android.build.api.artifact.ScopedArtifact import com.android.build.api.artifact.ScopedArtifact
import com.android.build.api.variant.AndroidComponentsExtension import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ScopedArtifacts import com.android.build.api.variant.ScopedArtifacts
import com.android.build.api.variant.SourceDirectories
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.Directory import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFile
import org.gradle.api.provider.ListProperty import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.assign import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
@ -88,11 +90,14 @@ internal fun Project.configureJacoco(
html.required = true html.required = true
} }
// TODO: This is missing files in src/debug/, src/prod, src/demo, src/demoDebug... fun SourceDirectories.Flat?.toFilePaths(): Provider<List<String>> = this
?.all
?.map { directories -> directories.map { it.asFile.path } }
?: provider { emptyList() }
sourceDirectories.setFrom( sourceDirectories.setFrom(
files( files(
"$projectDir/src/main/java", variant.sources.java.toFilePaths(),
"$projectDir/src/main/kotlin", variant.sources.kotlin.toFilePaths()
), ),
) )

@ -26,8 +26,8 @@ import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.provideDelegate
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
/** /**
* Configure base Kotlin with Android options * Configure base Kotlin with Android options
@ -36,7 +36,7 @@ internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension<*, *, *, *, *, *>,
) { ) {
commonExtension.apply { commonExtension.apply {
compileSdk = 34 compileSdk = 35
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
@ -54,7 +54,7 @@ internal fun Project.configureKotlinAndroid(
configureKotlin<KotlinAndroidProjectExtension>() configureKotlin<KotlinAndroidProjectExtension>()
dependencies { dependencies {
add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get()) "coreLibraryDesugaring"(libs.findLibrary("android.desugarJdkLibs").get())
} }
} }
@ -75,7 +75,7 @@ internal fun Project.configureKotlinJvm() {
/** /**
* Configure base Kotlin options * Configure base Kotlin options
*/ */
private inline fun <reified T : KotlinTopLevelExtension> Project.configureKotlin() = configure<T> { private inline fun <reified T : KotlinBaseExtension> Project.configureKotlin() = configure<T> {
// Treat all Kotlin warnings as errors (disabled by default) // Treat all Kotlin warnings as errors (disabled by default)
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
val warningsAsErrors: String? by project val warningsAsErrors: String? by project

@ -16,23 +16,26 @@ enum class FlavorDimension {
@Suppress("EnumEntryName") @Suppress("EnumEntryName")
enum class NiaFlavor(val dimension: FlavorDimension, val applicationIdSuffix: String? = null) { enum class NiaFlavor(val dimension: FlavorDimension, val applicationIdSuffix: String? = null) {
demo(FlavorDimension.contentType, applicationIdSuffix = ".demo"), demo(FlavorDimension.contentType, applicationIdSuffix = ".demo"),
prod(FlavorDimension.contentType) prod(FlavorDimension.contentType),
} }
fun configureFlavors( fun configureFlavors(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension<*, *, *, *, *, *>,
flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {} flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {},
) { ) {
commonExtension.apply { commonExtension.apply {
flavorDimensions += FlavorDimension.contentType.name FlavorDimension.values().forEach { flavorDimension ->
flavorDimensions += flavorDimension.name
}
productFlavors { productFlavors {
NiaFlavor.values().forEach { NiaFlavor.values().forEach { niaFlavor ->
create(it.name) { register(niaFlavor.name) {
dimension = it.dimension.name dimension = niaFlavor.dimension.name
flavorConfigurationBlock(this, it) flavorConfigurationBlock(this, niaFlavor)
if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) { if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) {
if (it.applicationIdSuffix != null) { if (niaFlavor.applicationIdSuffix != null) {
applicationIdSuffix = it.applicationIdSuffix applicationIdSuffix = niaFlavor.applicationIdSuffix
} }
} }
} }

@ -14,6 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
pluginManagement {
repositories {
gradlePluginPortal()
google()
}
}
dependencyResolutionManagement { dependencyResolutionManagement {
repositories { repositories {
google { google {

@ -21,9 +21,6 @@ plugins {
} }
android { android {
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
namespace = "com.google.samples.apps.nowinandroid.core.designsystem" namespace = "com.google.samples.apps.nowinandroid.core.designsystem"
} }
@ -47,6 +44,4 @@ dependencies {
testImplementation(libs.hilt.android.testing) testImplementation(libs.hilt.android.testing)
testImplementation(libs.robolectric) testImplementation(libs.robolectric)
testImplementation(projects.core.screenshotTesting) testImplementation(projects.core.screenshotTesting)
androidTestImplementation(libs.bundles.androidx.compose.ui.test)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.3 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: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.6 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: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.9 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.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 703 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save