Skip to content

Commit 451e53c

Browse files
committed
RUM-10163: Add Coil3 Integration
1 parent 3e289ad commit 451e53c

File tree

24 files changed

+587
-10
lines changed

24 files changed

+587
-10
lines changed

LICENSE-3rdparty.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import,com.squareup.sqldelight,Apache-2.0,"Copyright 2016 Square, Inc"
6363
import,com.fasterxml.jackson,Apache-2.0,"Copyright 2020 Datadog, Inc."
6464
import,com.datadoghq,Apache-2.0,"Copyright 2017 Datadog, Inc"
6565
import,io.coil-kt,Apache-2.0,Copyright 2021 Coil Contributors
66+
import,io.coil-kt.coil3,Apache-2.0,Copyright 2021 Coil Contributors
6667
import,io.opentelemetry,Apache-2.0,Copyright 2019 The OpenTelemetry Authors
6768
import,io.opentracing,Apache-2.0,Copyright 2016-2017 The OpenTracing Authors
6869
import,io.opentracing.contrib,Apache-2.0,Copyright 2016-2017 The OpenTracing Authors
@@ -72,6 +73,7 @@ import,org.jetbrains,Apache-2.0,Copyright 2010-2019 JetBrains s.r.o. and Kotlin
7273
import,org.jetbrains.kotlin,Apache-2.0,Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors
7374
import,org.jetbrains.kotlinx,Apache-2.0,Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors
7475
import,org.chromium.net,Apache-2.0,"Copyright 2015 The Chromium Authors"
76+
import,org.jetbrains.skiko,Apache-2.0,Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors
7577
import,org.reactivestreams,CC0,Copyright 2014 Reactive Streams
7678
import(test),androidx.autofill,Apache-2.0,Copyright 2018 The Android Open Source Project
7779
import(test),androidx.concurrent,Apache-2.0,Copyright 2018 The Android Open Source Project

detekt_custom_safe_calls.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ datadog:
584584
- "java.lang.ref.WeakReference.constructor(kotlin.Nothing?)"
585585
- "java.lang.ref.WeakReference.constructor(kotlin.String?)"
586586
- "java.lang.ref.WeakReference.get()"
587+
- "java.lang.reflect.isStatic(kotlin.Int)"
587588
- "java.lang.reflect.Field.getSafe(kotlin.Any?)"
588589
- "java.lang.reflect.Field.accessible()"
589590
- "java.lang.StringBuilder.append(kotlin.Char)"

features/dd-sdk-android-session-replay-compose/src/main/kotlin/com/datadog/android/sessionreplay/compose/internal/reflection/ComposeReflection.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.datadog.android.api.InternalLogger
1212
import com.datadog.android.api.feature.FeatureSdkCore
1313
import java.lang.reflect.Field
1414
import java.lang.reflect.Method
15+
import java.lang.reflect.Modifier.isStatic
1516

1617
@Suppress("StringLiteralDuplication")
1718
internal object ComposeReflection {
@@ -179,6 +180,9 @@ internal fun Method.accessible(): Method {
179180

180181
@Suppress("TooGenericExceptionCaught")
181182
internal fun Field.getSafe(target: Any?): Any? {
183+
if (target == null && !isStatic(modifiers)) {
184+
return null
185+
}
182186
return try {
183187
get(target)
184188
} catch (e: IllegalAccessException) {

features/dd-sdk-android-session-replay-compose/src/main/kotlin/com/datadog/android/sessionreplay/compose/internal/utils/ReflectionUtils.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,8 @@ internal class ReflectionUtils {
159159
fun getCoil3AsyncImagePainter(semanticsNode: SemanticsNode): Painter? {
160160
// Check if Coil3 ContentPainterNode is present first to optimize the performance
161161
// by skipping the node chain iteration
162-
if (PainterNodeClass == null) {
163-
return null
164-
}
162+
if (PainterNodeClass == null) return null
163+
165164
val layoutNode = LayoutNodeField?.getSafe(semanticsNode)
166165
val nodeChain = NodesFieldOfLayoutNode?.getSafe(layoutNode)
167166
val headNode = HeadFieldOfNodeChain?.getSafe(nodeChain) as? Modifier.Node
@@ -176,8 +175,7 @@ internal class ReflectionUtils {
176175
currentNode = ChildFieldOfModifierNode?.getSafe(currentNode) as? Modifier.Node
177176
}
178177
val asyncImagePainter = PainterFieldOfPainterNode?.getSafe(painterNode)
179-
val painter =
180-
asyncImagePainter?.let { PainterMethodOfAsync3ImagePainter?.invoke(it) }
178+
val painter = asyncImagePainter?.let { PainterMethodOfAsync3ImagePainter?.invoke(it) }
181179
return painter as? Painter
182180
}
183181

@@ -205,9 +203,8 @@ internal class ReflectionUtils {
205203
fun getAsyncImagePainter(semanticsNode: SemanticsNode): Painter? {
206204
// Check if Coil AsyncImagePainter is present first to optimize the performance
207205
// by skipping the modifier iteration
208-
if (AsyncImagePainterClass == null) {
209-
return null
210-
}
206+
if (AsyncImagePainterClass == null) return null
207+
211208
val asyncPainter = semanticsNode.layoutInfo.getModifierInfo().firstNotNullOfOrNull {
212209
if (ContentPainterModifierClass?.isInstance(it.modifier) == true) {
213210
PainterFieldOfContentPainterModifier?.getSafe(it.modifier)

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ kotlinXmlBuilder = "1.9.3"
8585
sqlDelight = "1.5.5"
8686
coil = "1.0.0"
8787
coilCompose = "2.1.0"
88+
coil3 = "3.0.4"
8889
fresco = "2.3.0"
8990
glide = "4.11.0"
9091
picasso = "2.8"
@@ -237,6 +238,9 @@ kotlinXmlBuilder = {module="org.redundent:kotlin-xml-builder", version.ref="kotl
237238
sqlDelight = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqlDelight" }
238239
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
239240
coilCompose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
241+
coil3 = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }
242+
coil3Compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil3" }
243+
coil3NetworkOkHttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil3" }
240244

241245
frescoCore = { module = "com.facebook.fresco:fresco", version.ref = "fresco" }
242246
frescoOkHttp3 = { module = "com.facebook.fresco:imagepipeline-okhttp3", version.ref = "fresco" }
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Datadog Integration for Coil 3
2+
3+
## Getting Started
4+
5+
To include the Datadog integration for [Coil 3][1] in your project, add the
6+
following to your application's `build.gradle` file.
7+
8+
```groovy
9+
dependencies {
10+
implementation "com.datadoghq:dd-sdk-android-rum:<latest-version>"
11+
implementation "com.datadoghq:dd-sdk-android-okhttp:<latest-version>"
12+
implementation "com.datadoghq:dd-sdk-android-coil3:<latest-version>"
13+
}
14+
```
15+
16+
### Initial Setup
17+
18+
1. Setup RUM monitoring, see the dedicated [Datadog Android RUM Collection documentation][2] to learn how.
19+
2. Setup OkHttp instrumentation with Datadog RUM SDK, see the [dedicated documentation][3] to learn how.
20+
21+
Follow Coil 3's [documentation][4] to:
22+
23+
- Create your own `ImageLoader` by providing your own `OkHttpClient` (configured with `DatadogInterceptor`) using `OkHttpNetworkFetcherFactory`.
24+
25+
```kotlin
26+
val okHttpClient = OkHttpClient.Builder()
27+
.addInterceptor(DatadogInterceptor.Builder(tracedHosts).build())
28+
.build()
29+
30+
val imageLoader = ImageLoader.Builder(context)
31+
.components {
32+
add(OkHttpNetworkFetcherFactory(okHttpClient))
33+
}
34+
.build()
35+
36+
SingletonImageLoader.setSafe { imageLoader }
37+
```
38+
39+
- Decorate the `ImageRequest.Builder` with the `DatadogCoilRequestListener` whenever you perform an image loading request.
40+
41+
```kotlin
42+
imageView.load(uri) {
43+
listener(DatadogCoilRequestListener())
44+
}
45+
```
46+
47+
With this setup:
48+
- The `DatadogInterceptor` on OkHttpClient automatically tracks Coil's network requests (creating APM Traces and RUM Resource events)
49+
- The `DatadogCoilRequestListener` reports image loading failures (creating RUM Error events)
50+
51+
## Contributing
52+
53+
For details on contributing, read the
54+
[Contributing Guide](../../CONTRIBUTING.md).
55+
56+
## License
57+
58+
[Apache License, v2.0](../../LICENSE)
59+
60+
[1]: https://github.com/coil-kt/coil
61+
[2]: https://docs.datadoghq.com/real_user_monitoring/android/?tab=kotlin
62+
[3]: https://docs.datadoghq.com/real_user_monitoring/android/advanced_configuration/?tab=kotlin#automatically-track-network-requests
63+
[4]: https://coil-kt.github.io/coil/upgrading_to_coil3/
64+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class com.datadog.android.coil3.DatadogCoilRequestListener : coil3.request.ImageRequest.Listener
2+
constructor(com.datadog.android.api.SdkCore = Datadog.getInstance())
3+
override fun onError(coil3.request.ImageRequest, coil3.request.ErrorResult)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public final class com/datadog/android/coil3/DatadogCoilRequestListener : coil3/request/ImageRequest$Listener {
2+
public fun <init> ()V
3+
public fun <init> (Lcom/datadog/android/api/SdkCore;)V
4+
public synthetic fun <init> (Lcom/datadog/android/api/SdkCore;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
5+
public fun onError (Lcoil3/request/ImageRequest;Lcoil3/request/ErrorResult;)V
6+
}
7+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
import com.datadog.gradle.config.androidLibraryConfig
8+
import com.datadog.gradle.config.dependencyUpdateConfig
9+
import com.datadog.gradle.config.detektCustomConfig
10+
import com.datadog.gradle.config.javadocConfig
11+
import com.datadog.gradle.config.junitConfig
12+
import com.datadog.gradle.config.kotlinConfig
13+
import com.datadog.gradle.config.publishingConfig
14+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
15+
16+
plugins {
17+
// Build
18+
id("com.android.library")
19+
kotlin("android")
20+
21+
// Publish
22+
`maven-publish`
23+
signing
24+
id("org.jetbrains.dokka-javadoc")
25+
26+
// Analysis tools
27+
id("com.github.ben-manes.versions")
28+
29+
// Tests
30+
id("org.jetbrains.kotlinx.kover")
31+
32+
// Internal Generation
33+
id("com.datadoghq.dependency-license")
34+
id("apiSurface")
35+
id("transitiveDependencies")
36+
id("verificationXml")
37+
id("binary-compatibility-validator")
38+
}
39+
40+
android {
41+
namespace = "com.datadog.android.coil3"
42+
}
43+
44+
dependencies {
45+
implementation(project(":features:dd-sdk-android-rum"))
46+
implementation(libs.kotlin)
47+
implementation(libs.okHttp)
48+
implementation(libs.coil3)
49+
implementation(libs.coil3NetworkOkHttp)
50+
51+
testImplementation(project(":tools:unit")) {
52+
attributes {
53+
attribute(
54+
com.android.build.api.attributes.ProductFlavorAttr.of("platform"),
55+
objects.named("jvm")
56+
)
57+
}
58+
}
59+
testImplementation(libs.bundles.jUnit5)
60+
testImplementation(libs.bundles.testTools)
61+
testImplementation(libs.okHttpMock)
62+
}
63+
64+
kotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11)
65+
androidLibraryConfig()
66+
junitConfig()
67+
javadocConfig()
68+
dependencyUpdateConfig()
69+
publishingConfig(
70+
"A Coil 3 integration to use with the Datadog monitoring library for Android applications."
71+
)
72+
detektCustomConfig()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
4+
~ This product includes software developed at Datadog (https://www.datadoghq.com/).
5+
~ Copyright 2016-Present Datadog, Inc.
6+
-->
7+
8+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
9+
10+
</manifest>
11+

0 commit comments

Comments
 (0)