Skip to content

Commit a4a90f9

Browse files
committed
Adding cronet sample
1 parent b4446a0 commit a4a90f9

File tree

14 files changed

+708
-0
lines changed

14 files changed

+708
-0
lines changed

sample/cronet/app/build.gradle.kts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import java.util.Properties
2+
3+
plugins {
4+
alias(libs.plugins.android.application)
5+
alias(libs.plugins.kotlin.android)
6+
alias(libs.plugins.kotlin.compose)
7+
id("com.datadoghq.dd-sdk-android-gradle-plugin")
8+
}
9+
10+
val localProperties = Properties().apply {
11+
val localPropertiesFile = rootProject.file("local.properties")
12+
if (localPropertiesFile.exists()) {
13+
load(localPropertiesFile.inputStream())
14+
}
15+
}
16+
17+
android {
18+
namespace = "com.datadog.cronet.sample"
19+
compileSdk = 36
20+
21+
defaultConfig {
22+
applicationId = "com.datadog.cronet.sample"
23+
minSdk = 24
24+
targetSdk = 36
25+
versionCode = 1
26+
versionName = "1.0"
27+
28+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
29+
30+
buildConfigField(
31+
"String",
32+
"DD_APP_ID",
33+
"\"${localProperties.getProperty("DD_APP_ID", "")}\""
34+
)
35+
36+
buildConfigField(
37+
"String",
38+
"DD_API_TOKEN",
39+
"\"${localProperties.getProperty("DD_API_TOKEN", "")}\""
40+
)
41+
}
42+
43+
compileOptions {
44+
sourceCompatibility = JavaVersion.VERSION_11
45+
targetCompatibility = JavaVersion.VERSION_11
46+
}
47+
kotlinOptions {
48+
jvmTarget = "11"
49+
}
50+
buildFeatures {
51+
compose = true
52+
buildConfig = true
53+
}
54+
}
55+
56+
dependencies {
57+
implementation(libs.bundles.core)
58+
implementation(libs.bundles.compose)
59+
implementation(libs.bundles.datadog)
60+
implementation(libs.play.services.cronet)
61+
implementation(platform(libs.androidx.compose.bom))
62+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
<uses-permission android:name="android.permission.INTERNET" />
5+
6+
<application
7+
android:name=".SampleApplication"
8+
android:icon="@android:mipmap/sym_def_app_icon"
9+
android:label="@string/app_name">
10+
<activity
11+
android:name=".MainActivity"
12+
android:exported="true"
13+
android:label="@string/app_name">
14+
<intent-filter>
15+
<action android:name="android.intent.action.MAIN" />
16+
17+
<category android:name="android.intent.category.LAUNCHER" />
18+
</intent-filter>
19+
</activity>
20+
</application>
21+
</manifest>
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package com.datadog.cronet.sample
2+
3+
import android.graphics.BitmapFactory
4+
import android.os.Bundle
5+
import androidx.activity.ComponentActivity
6+
import androidx.activity.compose.setContent
7+
import androidx.activity.enableEdgeToEdge
8+
import androidx.compose.foundation.Image
9+
import androidx.compose.foundation.layout.Arrangement
10+
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.Spacer
12+
import androidx.compose.foundation.layout.fillMaxSize
13+
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
15+
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.material3.Button
17+
import androidx.compose.material3.CircularProgressIndicator
18+
import androidx.compose.material3.MaterialTheme
19+
import androidx.compose.material3.Scaffold
20+
import androidx.compose.material3.Text
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.runtime.getValue
23+
import androidx.compose.runtime.mutableStateOf
24+
import androidx.compose.runtime.remember
25+
import androidx.compose.runtime.setValue
26+
import androidx.compose.ui.Alignment
27+
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.graphics.ImageBitmap
29+
import androidx.compose.ui.graphics.asImageBitmap
30+
import androidx.compose.ui.unit.dp
31+
import org.chromium.net.CronetEngine
32+
import org.chromium.net.CronetException
33+
import org.chromium.net.UrlRequest
34+
import org.chromium.net.UrlResponseInfo
35+
import java.nio.ByteBuffer
36+
import java.util.concurrent.Executors
37+
38+
class MainActivity : ComponentActivity() {
39+
override fun onCreate(savedInstanceState: Bundle?) {
40+
super.onCreate(savedInstanceState)
41+
enableEdgeToEdge()
42+
setContent {
43+
MaterialTheme {
44+
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
45+
ImageDownloaderScreen(
46+
cronetEngine = (application as SampleApplication).cronetEngine,
47+
modifier = Modifier.padding(innerPadding)
48+
)
49+
}
50+
}
51+
}
52+
}
53+
}
54+
55+
@Composable
56+
fun ImageDownloaderScreen(
57+
cronetEngine: CronetEngine,
58+
modifier: Modifier = Modifier
59+
) {
60+
var imageBitmap by remember { mutableStateOf<ImageBitmap?>(null) }
61+
var isLoading by remember { mutableStateOf(false) }
62+
var errorMessage by remember { mutableStateOf<String?>(null) }
63+
64+
Column(
65+
modifier = modifier
66+
.fillMaxSize()
67+
.padding(16.dp),
68+
horizontalAlignment = Alignment.CenterHorizontally,
69+
verticalArrangement = Arrangement.Center
70+
) {
71+
Button(
72+
onClick = {
73+
isLoading = true
74+
errorMessage = null
75+
downloadImage(
76+
cronetEngine = cronetEngine,
77+
url = "https://storage.googleapis.com/cronet/sun.jpg",
78+
onSuccess = { bitmap ->
79+
imageBitmap = bitmap
80+
isLoading = false
81+
},
82+
onError = { error ->
83+
errorMessage = error
84+
isLoading = false
85+
}
86+
)
87+
},
88+
enabled = !isLoading
89+
) {
90+
Text("Download Image")
91+
}
92+
93+
Spacer(modifier = Modifier.height(16.dp))
94+
95+
when {
96+
isLoading -> {
97+
CircularProgressIndicator()
98+
}
99+
errorMessage != null -> {
100+
Text("Error: $errorMessage")
101+
}
102+
imageBitmap != null -> {
103+
Image(
104+
bitmap = imageBitmap!!,
105+
contentDescription = "Downloaded image",
106+
modifier = Modifier.fillMaxWidth()
107+
)
108+
}
109+
}
110+
}
111+
}
112+
113+
private fun downloadImage(
114+
cronetEngine: CronetEngine,
115+
url: String,
116+
onSuccess: (ImageBitmap) -> Unit,
117+
onError: (String) -> Unit
118+
) {
119+
val executor = Executors.newSingleThreadExecutor()
120+
val byteArrayOutputStream = mutableListOf<ByteArray>()
121+
122+
val callback = object : UrlRequest.Callback() {
123+
override fun onRedirectReceived(
124+
request: UrlRequest,
125+
info: UrlResponseInfo,
126+
newLocationUrl: String
127+
) {
128+
request.followRedirect()
129+
}
130+
131+
override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) {
132+
request.read(ByteBuffer.allocateDirect(32 * 1024))
133+
}
134+
135+
override fun onReadCompleted(
136+
request: UrlRequest,
137+
info: UrlResponseInfo,
138+
byteBuffer: ByteBuffer
139+
) {
140+
byteBuffer.flip()
141+
val bytes = ByteArray(byteBuffer.remaining())
142+
byteBuffer.get(bytes)
143+
byteArrayOutputStream.add(bytes)
144+
byteBuffer.clear()
145+
request.read(byteBuffer)
146+
}
147+
148+
override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
149+
val totalSize = byteArrayOutputStream.sumOf { it.size }
150+
val imageBytes = ByteArray(totalSize)
151+
var offset = 0
152+
for (chunk in byteArrayOutputStream) {
153+
System.arraycopy(chunk, 0, imageBytes, offset, chunk.size)
154+
offset += chunk.size
155+
}
156+
157+
val bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
158+
if (bitmap != null) {
159+
onSuccess(bitmap.asImageBitmap())
160+
} else {
161+
onError("Failed to decode image")
162+
}
163+
}
164+
165+
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
166+
onError(error.message ?: "Unknown error")
167+
}
168+
}
169+
170+
val requestBuilder = cronetEngine.newUrlRequestBuilder(url, callback, executor)
171+
requestBuilder.build().start()
172+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.datadog.cronet.sample
2+
3+
import android.app.Application
4+
import com.datadog.android.Datadog
5+
import com.datadog.android.core.configuration.BatchSize
6+
import com.datadog.android.core.configuration.Configuration
7+
import com.datadog.android.core.configuration.UploadFrequency
8+
import com.datadog.android.cronet.DatadogCronetEngine
9+
import com.datadog.android.privacy.TrackingConsent
10+
import com.datadog.android.rum.ExperimentalRumApi
11+
import com.datadog.android.rum.Rum
12+
import com.datadog.android.rum.RumConfiguration
13+
import org.chromium.net.CronetEngine
14+
15+
class SampleApplication : Application() {
16+
internal lateinit var cronetEngine: CronetEngine
17+
18+
@OptIn(ExperimentalRumApi::class)
19+
override fun onCreate() {
20+
super.onCreate()
21+
Datadog.initialize(
22+
context = this,
23+
trackingConsent = TrackingConsent.GRANTED,
24+
configuration = Configuration
25+
.Builder(
26+
clientToken = BuildConfig.DD_API_TOKEN,
27+
env = "env",
28+
)
29+
.setFirstPartyHosts(listOf("storage.googleapis.com"))
30+
.setBatchSize(BatchSize.SMALL)
31+
.setUploadFrequency(UploadFrequency.FREQUENT)
32+
.build()
33+
)
34+
35+
val rumConfig = RumConfiguration.Builder(BuildConfig.DD_APP_ID)
36+
.collectAccessibility(true)
37+
.trackBackgroundEvents(true)
38+
.build()
39+
40+
Rum.enable(rumConfig)
41+
42+
cronetEngine = DatadogCronetEngine.Builder(this).build()
43+
}
44+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<resources>
2+
<string name="app_name">CronetSampleApplication</string>
3+
</resources>

sample/cronet/build.gradle.kts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
2+
plugins {
3+
alias(libs.plugins.android.application) apply false
4+
alias(libs.plugins.kotlin.android) apply false
5+
alias(libs.plugins.kotlin.compose) apply false
6+
}
7+
8+
buildscript {
9+
dependencies {
10+
classpath("com.datadoghq:dd-sdk-android-gradle-plugin:1.21.0")
11+
}
12+
}

sample/cronet/gradle.properties

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Project-wide Gradle settings.
2+
# IDE (e.g. Android Studio) users:
3+
# Gradle settings configured through the IDE *will override*
4+
# any settings specified in this file.
5+
# For more details on how to configure your build environment visit
6+
# http://www.gradle.org/docs/current/userguide/build_environment.html
7+
# Specifies the JVM arguments used for the daemon process.
8+
# The setting is particularly useful for tweaking memory settings.
9+
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10+
# When configured, Gradle will run in incubating parallel mode.
11+
# This option should only be used with decoupled projects. For more details, visit
12+
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13+
# org.gradle.parallel=true
14+
# AndroidX package structure to make it clearer which packages are bundled with the
15+
# Android operating system, and which are packaged with your app's APK
16+
# https://developer.android.com/topic/libraries/support-library/androidx-rn
17+
android.useAndroidX=true
18+
# Kotlin code style for this project: "official" or "obsolete":
19+
kotlin.code.style=official
20+
# Enables namespacing of each library's R class so that its R class includes only the
21+
# resources declared in the library itself and none from the library's dependencies,
22+
# thereby reducing the size of the R class for that library
23+
android.nonTransitiveRClass=true

0 commit comments

Comments
 (0)