Merge branch 'main' into lifecycle2.7.0

* main:
  Bump protobuf from 3.24.4 to 3.25.2 (#1214)
  Remove legacy benchmark build type (#1207)
  Use androidx.tracing everywhere
  Revert fast initialization of Coil
  Add comment for Retrofit async OkHttp
  Optimize OkHttp initialization for Retrofit
  Add braces
  Inline initialization
  Don't use runBlocking if async init completed
  Launch loader directly without additional coroutine scope
  Fix comment
  Improve lazy loading for Coil + OkHttp
  Restores screenshot tests in foryou (#1199)
  Fix name and message of dependency guard actions (#1196)

Change-Id: Ifcbfcfba4b7d8c24599e207b4a05dfea6125fde7
pull/1210/head
Don Turner 5 months ago
commit 69f4287ab5

@ -55,11 +55,10 @@ jobs:
continue-on-error: false continue-on-error: false
if: steps.dependencyguard_verify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository if: steps.dependencyguard_verify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository
run: | run: |
echo "::error::Dependency Guard failed, please run the following to update baselines:\n" \ echo "::error::Dependency Guard failed, please update baselines with: ./gradlew dependencyGuardBaseline" && exit 1
" ./gradlew dependencyGuardBaseline" && exit 1
# Runs if previous job failed # Runs if previous job failed
- name: Generate new screenshots if verification failed and it's a PR - name: Generate new Dependency Guard baselines if verification failed and it's a PR
id: dependencyguard_baseline id: dependencyguard_baseline
if: steps.dependencyguard_verify.outcome == 'failure' && github.event_name == 'pull_request' if: steps.dependencyguard_verify.outcome == 'failure' && github.event_name == 'pull_request'
run: | run: |
@ -110,13 +109,9 @@ jobs:
- name: Build all build type and flavor permutations - name: Build all build type and flavor permutations
run: ./gradlew :app:assemble :benchmarks:assemble run: ./gradlew :app:assemble :benchmarks:assemble
-x pixel6Api33ProdNonMinifiedReleaseAndroidTest -x pixel6Api33ProdNonMinifiedReleaseAndroidTest
-x pixel6Api33ProdNonMinifiedBenchmarkAndroidTest
-x pixel6Api33DemoNonMinifiedReleaseAndroidTest -x pixel6Api33DemoNonMinifiedReleaseAndroidTest
-x pixel6Api33DemoNonMinifiedBenchmarkAndroidTest
-x collectDemoNonMinifiedReleaseBaselineProfile -x collectDemoNonMinifiedReleaseBaselineProfile
-x collectDemoNonMinifiedBenchmarkBaselineProfile
-x collectProdNonMinifiedReleaseBaselineProfile -x collectProdNonMinifiedReleaseBaselineProfile
-x collectProdNonMinifiedBenchmarkBaselineProfile
- name: Upload build outputs (APKs) - name: Upload build outputs (APKs)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4

@ -70,7 +70,8 @@ androidx.profileinstaller:profileinstaller:1.3.1
androidx.savedstate:savedstate-ktx:1.2.1 androidx.savedstate:savedstate-ktx:1.2.1
androidx.savedstate:savedstate:1.2.1 androidx.savedstate:savedstate:1.2.1
androidx.startup:startup-runtime:1.1.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-animated:1.1.0
androidx.vectordrawable:vectordrawable:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0
androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.versionedparcelable:versionedparcelable:1.1.1

@ -57,17 +57,6 @@ android {
// Ensure Baseline Profile is fresh for release builds. // Ensure Baseline Profile is fresh for release builds.
baselineProfile.automaticGenerationDuringBuild = true 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 { packaging {

@ -105,8 +105,8 @@ androidx.savedstate:savedstate:1.2.1
androidx.sqlite:sqlite-framework:2.4.0 androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0 androidx.sqlite:sqlite:2.4.0
androidx.startup:startup-runtime:1.1.1 androidx.startup:startup-runtime:1.1.1
androidx.tracing:tracing-ktx:1.1.0 androidx.tracing:tracing-ktx:1.3.0-alpha02
androidx.tracing:tracing:1.1.0 androidx.tracing:tracing:1.3.0-alpha02
androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable-animated:1.1.0
androidx.vectordrawable:vectordrawable:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0
androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.versionedparcelable:versionedparcelable:1.1.1
@ -168,8 +168,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:3.24.4 com.google.protobuf:protobuf-javalite:3.25.2
com.google.protobuf:protobuf-kotlin-lite:3.24.4 com.google.protobuf:protobuf-kotlin-lite:3.25.2
com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0 com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0
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

@ -23,7 +23,6 @@ import com.google.samples.apps.nowinandroid.sync.initializers.Sync
import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider
/** /**
* [Application] class for NiA * [Application] class for NiA
@ -31,7 +30,7 @@ import javax.inject.Provider
@HiltAndroidApp @HiltAndroidApp
class NiaApplication : Application(), ImageLoaderFactory { class NiaApplication : Application(), ImageLoaderFactory {
@Inject @Inject
lateinit var imageLoader: Provider<ImageLoader> lateinit var imageLoader: dagger.Lazy<ImageLoader>
@Inject @Inject
lateinit var profileVerifierLogger: ProfileVerifierLogger lateinit var profileVerifierLogger: ProfileVerifierLogger

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.google.samples.apps.nowinandroid.NiaBuildType
import com.google.samples.apps.nowinandroid.configureFlavors import com.google.samples.apps.nowinandroid.configureFlavors
plugins { plugins {
@ -35,23 +34,6 @@ android {
buildConfig = true 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, // 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 // 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. // benchmarks on demo, so we benchmark on stable data.

@ -30,7 +30,6 @@ import java.io.ByteArrayOutputStream
val PACKAGE_NAME = buildString { val PACKAGE_NAME = buildString {
append("com.google.samples.apps.nowinandroid") append("com.google.samples.apps.nowinandroid")
append(BuildConfig.APP_FLAVOR_SUFFIX) append(BuildConfig.APP_FLAVOR_SUFFIX)
append(BuildConfig.APP_BUILD_TYPE_SUFFIX)
} }
fun UiDevice.flingElementDownUp(element: UiObject2) { fun UiDevice.flingElementDownUp(element: UiObject2) {

@ -44,6 +44,7 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("androidx.tracing.ktx").get())
add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get())
} }

@ -21,6 +21,7 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests
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.configure import org.gradle.kotlin.dsl.configure
@ -51,6 +52,8 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
} }
dependencies { dependencies {
add("testImplementation", kotlin("test")) add("testImplementation", kotlin("test"))
add("implementation", libs.findLibrary("androidx.tracing.ktx").get())
} }
} }
} }

@ -22,5 +22,4 @@ package com.google.samples.apps.nowinandroid
enum class NiaBuildType(val applicationIdSuffix: String? = null) { enum class NiaBuildType(val applicationIdSuffix: String? = null) {
DEBUG(".debug"), DEBUG(".debug"),
RELEASE, RELEASE,
BENCHMARK(".benchmark")
} }

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.network.di package com.google.samples.apps.nowinandroid.core.network.di
import android.content.Context import android.content.Context
import androidx.tracing.trace
import coil.ImageLoader import coil.ImageLoader
import coil.decode.SvgDecoder import coil.decode.SvgDecoder
import coil.util.DebugLogger import coil.util.DebugLogger
@ -51,16 +52,18 @@ internal object NetworkModule {
@Provides @Provides
@Singleton @Singleton
fun okHttpCallFactory(): Call.Factory = OkHttpClient.Builder() fun okHttpCallFactory(): Call.Factory = trace("NiaOkHttpClient") {
.addInterceptor( OkHttpClient.Builder()
HttpLoggingInterceptor() .addInterceptor(
.apply { HttpLoggingInterceptor()
if (BuildConfig.DEBUG) { .apply {
setLevel(HttpLoggingInterceptor.Level.BODY) if (BuildConfig.DEBUG) {
} setLevel(HttpLoggingInterceptor.Level.BODY)
}, }
) },
.build() )
.build()
}
/** /**
* Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this * Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this
@ -72,20 +75,21 @@ internal object NetworkModule {
@Provides @Provides
@Singleton @Singleton
fun imageLoader( fun imageLoader(
okHttpCallFactory: Call.Factory, // We specifically request dagger.Lazy here, so that it's not instantiated from Dagger.
okHttpCallFactory: dagger.Lazy<Call.Factory>,
@ApplicationContext application: Context, @ApplicationContext application: Context,
): ImageLoader = ImageLoader.Builder(application) ): ImageLoader = trace("NiaImageLoader") {
.callFactory(okHttpCallFactory) ImageLoader.Builder(application)
.components { .callFactory { okHttpCallFactory.get() }
add(SvgDecoder.Factory()) .components { add(SvgDecoder.Factory()) }
} // Assume most content images are versioned urls
// Assume most content images are versioned urls // but some problematic images are fetching each time
// but some problematic images are fetching each time .respectCacheHeaders(false)
.respectCacheHeaders(false) .apply {
.apply { if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG) { logger(DebugLogger())
logger(DebugLogger()) }
} }
} .build()
.build() }
} }

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.core.network.retrofit 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.BuildConfig
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
@ -73,17 +74,21 @@ private data class NetworkResponse<T>(
@Singleton @Singleton
internal class RetrofitNiaNetwork @Inject constructor( internal class RetrofitNiaNetwork @Inject constructor(
networkJson: Json, networkJson: Json,
okhttpCallFactory: Call.Factory, okhttpCallFactory: dagger.Lazy<Call.Factory>,
) : NiaNetworkDataSource { ) : NiaNetworkDataSource {
private val networkApi = Retrofit.Builder() private val networkApi = trace("RetrofitNiaNetwork") {
.baseUrl(NIA_BASE_URL) Retrofit.Builder()
.callFactory(okhttpCallFactory) .baseUrl(NIA_BASE_URL)
.addConverterFactory( // We use callFactory lambda here with dagger.Lazy<Call.Factory>
networkJson.asConverterFactory("application/json".toMediaType()), // to prevent initializing OkHttp on the main thread.
) .callFactory { okhttpCallFactory.get().newCall(it) }
.build() .addConverterFactory(
.create(RetrofitNiaNetworkApi::class.java) networkJson.asConverterFactory("application/json".toMediaType()),
)
.build()
.create(RetrofitNiaNetworkApi::class.java)
}
override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> = override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
networkApi.getTopics(ids = ids).data networkApi.getTopics(ids = ids).data

@ -18,6 +18,7 @@ plugins {
alias(libs.plugins.nowinandroid.android.feature) alias(libs.plugins.nowinandroid.android.feature)
alias(libs.plugins.nowinandroid.android.library.compose) alias(libs.plugins.nowinandroid.android.library.compose)
alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.nowinandroid.android.library.jacoco)
alias(libs.plugins.roborazzi)
} }
android { android {
@ -32,6 +33,7 @@ dependencies {
testImplementation(libs.hilt.android.testing) testImplementation(libs.hilt.android.testing)
testImplementation(libs.robolectric) testImplementation(libs.robolectric)
testImplementation(projects.core.testing) testImplementation(projects.core.testing)
testDemoImplementation(libs.roborazzi)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)
} }

@ -81,9 +81,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max import androidx.compose.ui.unit.max
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.trace
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.tracing.trace
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus.Denied import com.google.accompanist.permissions.PermissionStatus.Denied
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

@ -24,7 +24,7 @@ androidxTestCore = "1.5.0"
androidxTestExt = "1.1.5" androidxTestExt = "1.1.5"
androidxTestRules = "1.5.0" androidxTestRules = "1.5.0"
androidxTestRunner = "1.5.2" androidxTestRunner = "1.5.2"
androidxTracing = "1.1.0" androidxTracing = "1.3.0-alpha02"
androidxUiAutomator = "2.2.0" androidxUiAutomator = "2.2.0"
androidxWindowManager = "1.2.0" androidxWindowManager = "1.2.0"
androidxWork = "2.9.0" androidxWork = "2.9.0"
@ -46,7 +46,7 @@ kotlinxDatetime = "0.5.0"
kotlinxSerializationJson = "1.6.0" kotlinxSerializationJson = "1.6.0"
ksp = "1.9.21-1.0.16" ksp = "1.9.21-1.0.16"
okhttp = "4.12.0" okhttp = "4.12.0"
protobuf = "3.24.4" protobuf = "3.25.2"
protobufPlugin = "0.9.4" protobufPlugin = "0.9.4"
retrofit = "2.9.0" retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "1.0.0" retrofitKotlinxSerializationJson = "1.0.0"

Loading…
Cancel
Save