From 5f0612102d0f1c54cc57039a626efe0126d870c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Mon, 29 Jan 2024 23:00:32 +0100 Subject: [PATCH 01/16] Improve lazy loading for Coil + OkHttp This way, we can load Coil's backend on a background thread and not block the MainThread with it. Previously, the Coil image loader was initialized with the first composed image, which caused ~10ms duration and most likely skipped frames. Change-Id: Iaa583b6adc1df7d7a51dbae1473e539f2c0b0b62 --- .../util/ImageLoaderAsyncFactory.kt | 59 +++++++++++++++++++ .../apps/nowinandroid/NiaApplication.kt | 13 ++-- .../core/network/di/NetworkModule.kt | 52 ++++++++-------- 3 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt new file mode 100644 index 000000000..540082e50 --- /dev/null +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -0,0 +1,59 @@ +/* + * 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 androidx.tracing.trace +import coil.ImageLoader +import coil.ImageLoaderFactory +import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +/** + * This class asynchronously loads the Coil's image loader on a [ApplicationScope], which uses Default dispatcher. + * Reason for this is to prevent initializing Coil (and thus OkHttp internally) with the first image loading + * to prevent skipping frames and performance issues. + * + * Usage: + * - Init creates an async initialization of the image loader. + * - delegate to [newImageLoader] so that Coil can automatically reach for its loader. + */ +class ImageLoaderAsyncFactory @Inject constructor( + @ApplicationScope + appScope: CoroutineScope, + private val imageLoader: dagger.Lazy, +) : ImageLoaderFactory { + private lateinit var asyncNewImageLoader: Deferred + + init { + appScope.launch { + // Initializing asynchronously, but start immediately + asyncNewImageLoader = async { imageLoader.get() } + } + } + + /** + * This runBlocking here is on purpose to prevent any unfinished Coil initialization. + * Most likely this will be already initialized by the time we want to show an image on the screen. + */ + override fun newImageLoader() = + trace("NiaImageLoader.runBlocking") { runBlocking { asyncNewImageLoader.await() } } +} diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 9f0bb2ef7..40fffc4d9 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,21 +17,20 @@ package com.google.samples.apps.nowinandroid import android.app.Application -import coil.ImageLoader -import coil.ImageLoaderFactory +import coil.Coil import com.google.samples.apps.nowinandroid.sync.initializers.Sync +import com.google.samples.apps.nowinandroid.util.ImageLoaderAsyncFactory import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject -import javax.inject.Provider /** * [Application] class for NiA */ @HiltAndroidApp -class NiaApplication : Application(), ImageLoaderFactory { +class NiaApplication : Application() { @Inject - lateinit var imageLoader: Provider + lateinit var imageLoaderAsyncFactory: ImageLoaderAsyncFactory @Inject lateinit var profileVerifierLogger: ProfileVerifierLogger @@ -41,7 +40,7 @@ class NiaApplication : Application(), ImageLoaderFactory { // Initialize Sync; the system responsible for keeping data in the app up to date. Sync.initialize(context = this) profileVerifierLogger() + // We set immediately Coil's image loader factory to prevent initialization with the first image. + Coil.setImageLoader(imageLoaderAsyncFactory) } - - override fun newImageLoader(): ImageLoader = imageLoader.get() } diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index 21d93c0e4..a68683c7c 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -17,6 +17,7 @@ package com.google.samples.apps.nowinandroid.core.network.di import android.content.Context +import androidx.tracing.trace import coil.ImageLoader import coil.decode.SvgDecoder import coil.util.DebugLogger @@ -51,16 +52,18 @@ internal object NetworkModule { @Provides @Singleton - fun okHttpCallFactory(): Call.Factory = OkHttpClient.Builder() - .addInterceptor( - HttpLoggingInterceptor() - .apply { - if (BuildConfig.DEBUG) { - setLevel(HttpLoggingInterceptor.Level.BODY) - } - }, - ) - .build() + fun okHttpCallFactory(): Call.Factory = trace("NiaOkHttpClient") { + OkHttpClient.Builder() + .addInterceptor( + HttpLoggingInterceptor() + .apply { + if (BuildConfig.DEBUG) { + setLevel(HttpLoggingInterceptor.Level.BODY) + } + }, + ) + .build() + } /** * Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this @@ -72,20 +75,21 @@ internal object NetworkModule { @Provides @Singleton fun imageLoader( - okHttpCallFactory: Call.Factory, + // We specifically request dagger.Lazy here, so that it's not instantiated from Dagger. + okHttpCallFactory: dagger.Lazy, @ApplicationContext application: Context, - ): ImageLoader = ImageLoader.Builder(application) - .callFactory(okHttpCallFactory) - .components { - add(SvgDecoder.Factory()) - } - // Assume most content images are versioned urls - // but some problematic images are fetching each time - .respectCacheHeaders(false) - .apply { - if (BuildConfig.DEBUG) { - logger(DebugLogger()) + ): ImageLoader = trace("NiaImageLoader") { + ImageLoader.Builder(application) + .callFactory { okHttpCallFactory.get() } + .components { add(SvgDecoder.Factory()) } + // Assume most content images are versioned urls + // but some problematic images are fetching each time + .respectCacheHeaders(false) + .apply { + if (BuildConfig.DEBUG) { + logger(DebugLogger()) + } } - } - .build() + .build() + } } From 8e3903e90c43f506e522cb0a0d3bbfc294e5204d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 12:57:25 +0100 Subject: [PATCH 02/16] Fix comment Co-authored-by: Yuri Schimke --- .../samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 540082e50..5e613117e 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -45,7 +45,7 @@ class ImageLoaderAsyncFactory @Inject constructor( init { appScope.launch { - // Initializing asynchronously, but start immediately + // Initialize immediately, but need a Deferred for callers asyncNewImageLoader = async { imageLoader.get() } } } From 7c33946ce13c4bb37df453023de7478818ca7748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 12:53:37 +0100 Subject: [PATCH 03/16] Launch loader directly without additional coroutine scope Change-Id: Icbbdbcbcac1a6275857ebe998509f1e09109db7a --- .../samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 5e613117e..aa79b8261 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -44,9 +44,9 @@ class ImageLoaderAsyncFactory @Inject constructor( private lateinit var asyncNewImageLoader: Deferred init { - appScope.launch { + asyncNewImageLoader = appScope.async { // Initialize immediately, but need a Deferred for callers - asyncNewImageLoader = async { imageLoader.get() } + imageLoader.get() } } From e853748cf76b3e77495a7ef642e038e19c0dc68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 13:50:01 +0100 Subject: [PATCH 04/16] Don't use runBlocking if async init completed Change-Id: I641187ce277f434c6fca49a11b3cfccd50ecf5da --- .../apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index aa79b8261..0670f6dc8 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -55,5 +55,8 @@ class ImageLoaderAsyncFactory @Inject constructor( * Most likely this will be already initialized by the time we want to show an image on the screen. */ override fun newImageLoader() = - trace("NiaImageLoader.runBlocking") { runBlocking { asyncNewImageLoader.await() } } + trace("NiaImageLoader.runBlocking") { + if (asyncNewImageLoader.isCompleted) asyncNewImageLoader.getCompleted() + else runBlocking { asyncNewImageLoader.await() } + } } From d1211f15e783fff251da76f86fa3e9efdeb68c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 13:50:33 +0100 Subject: [PATCH 05/16] Inline initialization Change-Id: I0a77eb6457cac27c1a4d604c8efdcbbdce95bc48 --- .../nowinandroid/util/ImageLoaderAsyncFactory.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 0670f6dc8..b746ec974 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -19,11 +19,11 @@ package com.google.samples.apps.nowinandroid.util import androidx.tracing.trace import coil.ImageLoader import coil.ImageLoaderFactory +import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.async -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import javax.inject.Inject @@ -41,14 +41,12 @@ class ImageLoaderAsyncFactory @Inject constructor( appScope: CoroutineScope, private val imageLoader: dagger.Lazy, ) : ImageLoaderFactory { - private lateinit var asyncNewImageLoader: Deferred - init { - asyncNewImageLoader = appScope.async { - // Initialize immediately, but need a Deferred for callers - imageLoader.get() - } - } + /** + * Initialize immediately, but need a Deferred for callers + * [ApplicationScope] already uses [Default] dispatcher, so we don't have to switch it here. + */ + private val asyncNewImageLoader: Deferred = appScope.async { imageLoader.get() } /** * This runBlocking here is on purpose to prevent any unfinished Coil initialization. From f3e89bad4ade836afefc208d1c75b493ab3486e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:06:01 +0100 Subject: [PATCH 06/16] Add braces Change-Id: I859babab7278137a4a2e49e5a085c65632888dd0 --- .../apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index b746ec974..d8f928de5 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -54,7 +54,10 @@ class ImageLoaderAsyncFactory @Inject constructor( */ override fun newImageLoader() = trace("NiaImageLoader.runBlocking") { - if (asyncNewImageLoader.isCompleted) asyncNewImageLoader.getCompleted() - else runBlocking { asyncNewImageLoader.await() } + if (asyncNewImageLoader.isCompleted) { + asyncNewImageLoader.getCompleted() + } else { + runBlocking { asyncNewImageLoader.await() } + } } } From d110d92b87359f52acb5b4adb7ce100843d3705c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:06:23 +0100 Subject: [PATCH 07/16] Optimize OkHttp initialization for Retrofit Change-Id: I347f1080ab5adf774a0cdd3c659cbf25c4820f9a --- .../network/retrofit/RetrofitNiaNetwork.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index 321e856fe..42a0e9d64 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.core.network.retrofit +import androidx.tracing.trace import com.google.samples.apps.nowinandroid.core.network.BuildConfig import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList @@ -73,17 +74,19 @@ private data class NetworkResponse( @Singleton internal class RetrofitNiaNetwork @Inject constructor( networkJson: Json, - okhttpCallFactory: Call.Factory, + okhttpCallFactory: dagger.Lazy, ) : NiaNetworkDataSource { - private val networkApi = Retrofit.Builder() - .baseUrl(NIA_BASE_URL) - .callFactory(okhttpCallFactory) - .addConverterFactory( - networkJson.asConverterFactory("application/json".toMediaType()), - ) - .build() - .create(RetrofitNiaNetworkApi::class.java) + private val networkApi = trace("RetrofitNiaNetwork") { + Retrofit.Builder() + .baseUrl(NIA_BASE_URL) + .callFactory { okhttpCallFactory.get().newCall(it) } + .addConverterFactory( + networkJson.asConverterFactory("application/json".toMediaType()), + ) + .build() + .create(RetrofitNiaNetworkApi::class.java) + } override suspend fun getTopics(ids: List?): List = networkApi.getTopics(ids = ids).data From 4cdc2ad34570e693aa69aac36cda275e9d32c5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:12:32 +0100 Subject: [PATCH 08/16] Add comment for Retrofit async OkHttp Change-Id: Ic7a6887b76caf26f00b58b0753271d426b67e75b --- .../nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index 42a0e9d64..e9fe99d9e 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -80,6 +80,8 @@ internal class RetrofitNiaNetwork @Inject constructor( private val networkApi = trace("RetrofitNiaNetwork") { Retrofit.Builder() .baseUrl(NIA_BASE_URL) + // We use callFactory lambda here with dagger.Lazy + // to prevent initializing OkHttp on the main thread. .callFactory { okhttpCallFactory.get().newCall(it) } .addConverterFactory( networkJson.asConverterFactory("application/json".toMediaType()), From 58483b6166f2ffdc1bb1629229bf05e993c45f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 8 Feb 2024 15:05:12 +0100 Subject: [PATCH 09/16] Revert fast initialization of Coil As discussed in https://github.com/coil-kt/coil/issues/2097 the problem is caused by regitering system services, which will be fixed in 2.6.0 Change-Id: I9085309780508137f10b25ff82deed3c62e5d159 --- .../util/ImageLoaderAsyncFactory.kt | 63 ------------------- .../apps/nowinandroid/NiaApplication.kt | 12 ++-- 2 files changed, 6 insertions(+), 69 deletions(-) delete mode 100644 app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt deleted file mode 100644 index d8f928de5..000000000 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 androidx.tracing.trace -import coil.ImageLoader -import coil.ImageLoaderFactory -import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default -import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking -import javax.inject.Inject - -/** - * This class asynchronously loads the Coil's image loader on a [ApplicationScope], which uses Default dispatcher. - * Reason for this is to prevent initializing Coil (and thus OkHttp internally) with the first image loading - * to prevent skipping frames and performance issues. - * - * Usage: - * - Init creates an async initialization of the image loader. - * - delegate to [newImageLoader] so that Coil can automatically reach for its loader. - */ -class ImageLoaderAsyncFactory @Inject constructor( - @ApplicationScope - appScope: CoroutineScope, - private val imageLoader: dagger.Lazy, -) : ImageLoaderFactory { - - /** - * Initialize immediately, but need a Deferred for callers - * [ApplicationScope] already uses [Default] dispatcher, so we don't have to switch it here. - */ - private val asyncNewImageLoader: Deferred = appScope.async { imageLoader.get() } - - /** - * This runBlocking here is on purpose to prevent any unfinished Coil initialization. - * Most likely this will be already initialized by the time we want to show an image on the screen. - */ - override fun newImageLoader() = - trace("NiaImageLoader.runBlocking") { - if (asyncNewImageLoader.isCompleted) { - asyncNewImageLoader.getCompleted() - } else { - runBlocking { asyncNewImageLoader.await() } - } - } -} diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 40fffc4d9..8e3ad814a 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,9 +17,9 @@ package com.google.samples.apps.nowinandroid import android.app.Application -import coil.Coil +import coil.ImageLoader +import coil.ImageLoaderFactory import com.google.samples.apps.nowinandroid.sync.initializers.Sync -import com.google.samples.apps.nowinandroid.util.ImageLoaderAsyncFactory import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @@ -28,9 +28,9 @@ import javax.inject.Inject * [Application] class for NiA */ @HiltAndroidApp -class NiaApplication : Application() { +class NiaApplication : Application(), ImageLoaderFactory { @Inject - lateinit var imageLoaderAsyncFactory: ImageLoaderAsyncFactory + lateinit var imageLoader: dagger.Lazy @Inject lateinit var profileVerifierLogger: ProfileVerifierLogger @@ -40,7 +40,7 @@ class NiaApplication : Application() { // Initialize Sync; the system responsible for keeping data in the app up to date. Sync.initialize(context = this) profileVerifierLogger() - // We set immediately Coil's image loader factory to prevent initialization with the first image. - Coil.setImageLoader(imageLoaderAsyncFactory) } + + override fun newImageLoader(): ImageLoader = imageLoader.get() } From d9aaec0699c45fd6d59aa1ea13b5068aafd01cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 11:32:49 +0100 Subject: [PATCH 10/16] Use androidx.tracing everywhere Change-Id: I847827efc08f5cbc00f0c809ad992c267f826e50 --- .../src/main/kotlin/AndroidFeatureConventionPlugin.kt | 2 ++ .../src/main/kotlin/AndroidLibraryConventionPlugin.kt | 3 +++ .../samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt | 2 +- gradle/libs.versions.toml | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 7a334beb3..8a76a1ac9 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -44,6 +44,8 @@ class AndroidFeatureConventionPlugin : Plugin { add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index 8eb329f58..d442d94ef 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -21,6 +21,7 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests +import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -51,6 +52,8 @@ class AndroidLibraryConventionPlugin : Plugin { } dependencies { add("testImplementation", kotlin("test")) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index 30134715b..e8b19594f 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -81,7 +81,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.compose.ui.util.trace +import androidx.tracing.trace import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..69a0c42fb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidxTestCore = "1.5.0" androidxTestExt = "1.1.5" androidxTestRules = "1.5.0" androidxTestRunner = "1.5.2" -androidxTracing = "1.1.0" +androidxTracing = "1.3.0-alpha02" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.2.0" androidxWork = "2.9.0" From 254b4642592319538d3f290a058b244f6bb7c708 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Thu, 8 Feb 2024 18:38:51 +0000 Subject: [PATCH 11/16] Update Lifecycle to 2.7.0. Use new LifecycleEventEffect. Change-Id: I71e7aa521f22499fd3f3e9f3ac7c81658f3abc3a --- .../kotlin/AndroidFeatureConventionPlugin.kt | 2 ++ .../feature/bookmarks/BookmarksScreenTest.kt | 31 +++++++++++++++++++ .../feature/bookmarks/BookmarksScreen.kt | 15 ++------- gradle/libs.versions.toml | 3 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 7a334beb3..6def4e630 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -44,6 +44,8 @@ class AndroidFeatureConventionPlugin : Plugin { add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + + add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) } } } diff --git a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index 3d684f9d1..197a447fe 100644 --- a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -17,6 +17,8 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks import androidx.activity.ComponentActivity +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.filter @@ -30,8 +32,11 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToNode +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.testing.TestLifecycleOwner import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals @@ -166,4 +171,30 @@ class BookmarksScreenTest { ) .assertExists() } + + @Test + fun feed_whenLifecycleStops_undoBookmarkedStateIsCleared() = runTest { + + var undoStateCleared = false + val testLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.STARTED) + + composeTestRule.setContent { + CompositionLocalProvider(LocalLifecycleOwner provides testLifecycleOwner){ + BookmarksScreen( + feedState = NewsFeedUiState.Success(emptyList()), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onTopicClick = {}, + onNewsResourceViewed = {}, + clearUndoState = { + undoStateCleared = true + } + ) + } + } + + assertEquals(false, undoStateCleared) + testLifecycleOwner.handleLifecycleEvent(event = Lifecycle.Event.ON_STOP) + assertEquals(true, undoStateCleared) + } } diff --git a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt index 5b54db7cd..7c229c5ea 100644 --- a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt +++ b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt @@ -42,14 +42,12 @@ import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridS import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -60,7 +58,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar @@ -128,15 +126,8 @@ internal fun BookmarksScreen( } } - val lifecycleOwner = LocalLifecycleOwner.current - DisposableEffect(lifecycleOwner) { - val observer = LifecycleEventObserver { _, event -> - if (event == Lifecycle.Event.ON_STOP) { - clearUndoState() - } - } - lifecycleOwner.lifecycle.addObserver(observer) - onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } + LifecycleEventEffect(Lifecycle.Event.ON_STOP) { + clearUndoState() } when (feedState) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..f0b5aa642 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.0.0" androidxEspresso = "3.5.1" androidxHiltNavigationCompose = "1.0.0" -androidxLifecycle = "2.6.2" +androidxLifecycle = "2.7.0" androidxMacroBenchmark = "1.2.2" androidxMetrics = "1.0.0-alpha04" androidxNavigation = "2.7.4" @@ -82,6 +82,7 @@ androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscree androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtimeTesting = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } From f64f1c0aa2b885b0583dd04a5b4055a8952a2b6e Mon Sep 17 00:00:00 2001 From: Ben Weiss Date: Mon, 12 Feb 2024 14:01:47 +0000 Subject: [PATCH 12/16] Remove legacy benchmark build type (#1207) * Remove legacy benchmark build type Now that we have the baseline profile gradle plugin, there is no need for a benchmark build type. The plugin does the setup and configuration and runs against the correct app flavor. * Remove explicit benchmark build type from benchmark module * Remove stale build task exclusions * Fix tracing import location FYI @mlykotom --- .github/workflows/Build.yaml | 4 ---- .../dependencies/releaseRuntimeClasspath.txt | 3 ++- app/build.gradle.kts | 11 ----------- .../prodReleaseRuntimeClasspath.txt | 4 ++-- benchmarks/build.gradle.kts | 18 ------------------ .../google/samples/apps/nowinandroid/Utils.kt | 1 - .../samples/apps/nowinandroid/NiaBuildType.kt | 1 - .../feature/foryou/ForYouScreen.kt | 2 +- 8 files changed, 5 insertions(+), 39 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index fa3a77f55..f8595f221 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -109,13 +109,9 @@ jobs: - name: Build all build type and flavor permutations run: ./gradlew :app:assemble :benchmarks:assemble -x pixel6Api33ProdNonMinifiedReleaseAndroidTest - -x pixel6Api33ProdNonMinifiedBenchmarkAndroidTest -x pixel6Api33DemoNonMinifiedReleaseAndroidTest - -x pixel6Api33DemoNonMinifiedBenchmarkAndroidTest -x collectDemoNonMinifiedReleaseBaselineProfile - -x collectDemoNonMinifiedBenchmarkBaselineProfile -x collectProdNonMinifiedReleaseBaselineProfile - -x collectProdNonMinifiedBenchmarkBaselineProfile - name: Upload build outputs (APKs) uses: actions/upload-artifact@v4 diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index 10f2dfa32..c3f83d734 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -70,7 +70,8 @@ androidx.profileinstaller:profileinstaller:1.3.1 androidx.savedstate:savedstate-ktx:1.2.1 androidx.savedstate:savedstate:1.2.1 androidx.startup:startup-runtime:1.1.1 -androidx.tracing:tracing:1.0.0 +androidx.tracing:tracing-ktx:1.3.0-alpha02 +androidx.tracing:tracing:1.3.0-alpha02 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 38166f7ff..520baa134 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -57,17 +57,6 @@ android { // Ensure Baseline Profile is fresh for release builds. baselineProfile.automaticGenerationDuringBuild = true } - create("benchmark") { - // Enable all the optimizations from release build through initWith(release). - initWith(release) - matchingFallbacks.add("release") - // Debug key signing is available to everyone. - signingConfig = signingConfigs.getByName("debug") - // Only use benchmark proguard rules - proguardFiles("benchmark-rules.pro") - isMinifyEnabled = true - applicationIdSuffix = NiaBuildType.BENCHMARK.applicationIdSuffix - } } packaging { diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index e9e3ebf8e..e19a37db9 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -105,8 +105,8 @@ androidx.savedstate:savedstate:1.2.1 androidx.sqlite:sqlite-framework:2.4.0 androidx.sqlite:sqlite:2.4.0 androidx.startup:startup-runtime:1.1.1 -androidx.tracing:tracing-ktx:1.1.0 -androidx.tracing:tracing:1.1.0 +androidx.tracing:tracing-ktx:1.3.0-alpha02 +androidx.tracing:tracing:1.3.0-alpha02 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 67fccb979..279c4b226 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import com.google.samples.apps.nowinandroid.NiaBuildType import com.google.samples.apps.nowinandroid.configureFlavors plugins { @@ -35,23 +34,6 @@ android { buildConfig = true } - buildTypes { - // This benchmark buildType is used for benchmarking, and should function like your - // release build (for example, with minification on). It's signed with a debug key - // for easy local/CI testing. - create("benchmark") { - // Keep the build type debuggable so we can attach a debugger if needed. - isDebuggable = true - signingConfig = signingConfigs.getByName("debug") - matchingFallbacks.add("release") - buildConfigField( - "String", - "APP_BUILD_TYPE_SUFFIX", - "\"${NiaBuildType.BENCHMARK.applicationIdSuffix ?: ""}\"" - ) - } - } - // Use the same flavor dimensions as the application to allow generating Baseline Profiles on prod, // which is more close to what will be shipped to users (no fake data), but has ability to run the // benchmarks on demo, so we benchmark on stable data. diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/Utils.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/Utils.kt index 9ece991c4..e8fb53c4f 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/Utils.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/Utils.kt @@ -30,7 +30,6 @@ import java.io.ByteArrayOutputStream val PACKAGE_NAME = buildString { append("com.google.samples.apps.nowinandroid") append(BuildConfig.APP_FLAVOR_SUFFIX) - append(BuildConfig.APP_BUILD_TYPE_SUFFIX) } fun UiDevice.flingElementDownUp(element: UiObject2) { diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt index 653506f51..e4f40840d 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt @@ -22,5 +22,4 @@ package com.google.samples.apps.nowinandroid enum class NiaBuildType(val applicationIdSuffix: String? = null) { DEBUG(".debug"), RELEASE, - BENCHMARK(".benchmark") } diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index e8b19594f..e1418d747 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -81,9 +81,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.tracing.trace import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.tracing.trace import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionStatus.Denied import com.google.accompanist.permissions.rememberPermissionState From 6add4d68e310a78fe465cbfdae36b89cde27b1af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:34:23 -0800 Subject: [PATCH 13/16] Bump protobuf from 3.24.4 to 3.25.2 (#1214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump protobuf from 3.24.4 to 3.25.2 Bumps `protobuf` from 3.24.4 to 3.25.2. Updates `com.google.protobuf:protobuf-kotlin-lite` from 3.24.4 to 3.25.2 Updates `com.google.protobuf:protoc` from 3.24.4 to 3.25.2 - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.24.4...v3.25.2) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-kotlin-lite dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.protobuf:protoc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * 🤖 Updates baselines for Dependency Guard --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- app/dependencies/prodReleaseRuntimeClasspath.txt | 4 ++-- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index e19a37db9..759fba4a6 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -168,8 +168,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:3.24.4 -com.google.protobuf:protobuf-kotlin-lite:3.24.4 +com.google.protobuf:protobuf-javalite:3.25.2 +com.google.protobuf:protobuf-kotlin-lite:3.25.2 com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69a0c42fb..003533715 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.0" ksp = "1.9.21-1.0.16" okhttp = "4.12.0" -protobuf = "3.24.4" +protobuf = "3.25.2" protobufPlugin = "0.9.4" retrofit = "2.9.0" retrofitKotlinxSerializationJson = "1.0.0" From 2ad48843f6dbb5eee54c60b1a5600597dc7f23a0 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Mon, 19 Feb 2024 11:46:22 +0000 Subject: [PATCH 14/16] Fix formatting Change-Id: I674d32bc0f1921c431717dbf0f41a74f632322b3 --- .../nowinandroid/feature/bookmarks/BookmarksScreenTest.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index 197a447fe..40f54e4a7 100644 --- a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -174,12 +174,11 @@ class BookmarksScreenTest { @Test fun feed_whenLifecycleStops_undoBookmarkedStateIsCleared() = runTest { - var undoStateCleared = false val testLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.STARTED) composeTestRule.setContent { - CompositionLocalProvider(LocalLifecycleOwner provides testLifecycleOwner){ + CompositionLocalProvider(LocalLifecycleOwner provides testLifecycleOwner) { BookmarksScreen( feedState = NewsFeedUiState.Success(emptyList()), onShowSnackbar = { _, _ -> false }, @@ -188,7 +187,7 @@ class BookmarksScreenTest { onNewsResourceViewed = {}, clearUndoState = { undoStateCleared = true - } + }, ) } } From 08d8f1393a5df2b320d3bfe886dcf6c5bec2a9dc Mon Sep 17 00:00:00 2001 From: dturner Date: Mon, 19 Feb 2024 12:00:16 +0000 Subject: [PATCH 15/16] =?UTF-8?q?=F0=9F=A4=96=20Updates=20baselines=20for?= =?UTF-8?q?=20Dependency=20Guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prodReleaseRuntimeClasspath.txt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index 759fba4a6..eaeff771a 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -71,19 +71,20 @@ androidx.hilt:hilt-navigation:1.0.0 androidx.hilt:hilt-work:1.1.0 androidx.interpolator:interpolator:1.0.0 androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.6.2 -androidx.lifecycle:lifecycle-common:2.6.2 -androidx.lifecycle:lifecycle-livedata-core:2.6.2 -androidx.lifecycle:lifecycle-livedata:2.6.2 -androidx.lifecycle:lifecycle-process:2.6.2 -androidx.lifecycle:lifecycle-runtime-compose:2.6.2 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -androidx.lifecycle:lifecycle-runtime:2.6.2 -androidx.lifecycle:lifecycle-service:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2 -androidx.lifecycle:lifecycle-viewmodel:2.6.2 +androidx.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-compose:2.7.0 +androidx.lifecycle:lifecycle-runtime-ktx:2.7.0 +androidx.lifecycle:lifecycle-runtime:2.7.0 +androidx.lifecycle:lifecycle-service:2.7.0 +androidx.lifecycle:lifecycle-viewmodel-compose: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.loader:loader:1.0.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.metrics:metrics-performance:1.0.0-alpha04 From dba36d694fbd9fd974793a69f6f3e59669b99a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Alc=C3=A9rreca?= Date: Mon, 19 Feb 2024 19:18:50 +0100 Subject: [PATCH 16/16] [CI] Adds free-disk-space action to fix CI (#1219) * [CI] Adds free-disk-space action to fix CI --- .github/workflows/Build.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index f8595f221..5f501b6c0 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -147,6 +147,17 @@ jobs: api-level: [26, 30] steps: + - name: Delete unnecessary tools 🔧 + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: false # Don't remove Android tools + tool-cache: true # Remove image tool cache - rm -rf "$AGENT_TOOLSDIRECTORY" + dotnet: true # rm -rf /usr/share/dotnet + haskell: true # rm -rf /opt/ghc... + swap-storage: true # rm -f /mnt/swapfile (4GiB) + docker-images: false # Takes 16s, enable if needed in the future + large-packages: false # includes google-cloud-sdk and it's slow + - name: Enable KVM group perms run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules