Skip to content

Commit 96d9f4f

Browse files
committed
Merge branch 'main' into andrey/router-play
* main: (29 commits) chore: release main (#344) feat: Android double masking with frame drop (#342) chore: update LaunchDarkly SDK dependencies (#336) chore: Android SR - cleanup privacy settings (#341) chore: release main (#340) feat: Added privacy options: maskViews, maskXMLViewIds, maskImageViews (#339) test: O11Y-908 - Add Android CI workflows (#337) chore: release main (#335) feat: publish umd for broser environments (#334) chore: release main (#333) feat: Pause and resume replay capture on app background/foreground (#329) fix: Fix compose coordinate offset. (#331) chore: release main (#324) fix(highlight.run): correct privacy masking for empty strings (#332) feat: Android SR Identify support (#330) feat: Gzip compression for Graphql request body (#328) docs: Update readme with options refactor (#326) feat: Graphql client memory optimization (#325) feat: Limit accumulating canvas buffer (#322) chore: Fix existing RRwebGraphQLReplayLogExporterTest tests (#321) ...
2 parents c8c3e77 + 17be0e0 commit 96d9f4f

75 files changed

Lines changed: 5123 additions & 1079 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/android-e2e.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: 'Android E2E App Tests'
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
paths:
7+
- 'e2e/android/**'
8+
- 'sdk/@launchdarkly/observability-android/**'
9+
- '.github/workflows/android-e2e.yml'
10+
pull_request:
11+
types: [opened, synchronize]
12+
paths:
13+
- 'e2e/android/**'
14+
- 'sdk/@launchdarkly/observability-android/**'
15+
- '.github/workflows/android-e2e.yml'
16+
17+
permissions:
18+
contents: read
19+
20+
concurrency: ${{ github.workflow }}-${{ github.ref }}
21+
jobs:
22+
test:
23+
name: E2E Tests
24+
runs-on: ubuntu-22.04-8core-32gb
25+
defaults:
26+
run:
27+
working-directory: ./e2e/android
28+
steps:
29+
- uses: actions/checkout@v4
30+
with:
31+
token: ${{ secrets.GITHUB_TOKEN }}
32+
33+
- name: Set up JDK 17
34+
uses: actions/setup-java@v4
35+
with:
36+
distribution: 'temurin'
37+
java-version: 17
38+
39+
- name: Set up Android SDK
40+
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 commit SHA - https://github.com/android-actions/setup-android/releases/tag/v3.2.2
41+
42+
- name: Run unit tests
43+
run: ./gradlew :app:testDebugUnitTest
44+
45+
- name: Upload test reports
46+
if: always()
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: android-e2e-unit-test-results
50+
path: e2e/android/**/build/test-results/**/TEST-*.xml
51+
retention-days: 7
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: 'Android Observability'
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
paths:
7+
- 'sdk/@launchdarkly/observability-android/**'
8+
- '.github/workflows/android-observability.yml'
9+
pull_request:
10+
types: [opened, synchronize]
11+
paths:
12+
- 'sdk/@launchdarkly/observability-android/**'
13+
- '.github/workflows/android-observability.yml'
14+
15+
permissions:
16+
contents: read
17+
18+
concurrency: ${{ github.workflow }}-${{ github.ref }}
19+
jobs:
20+
test:
21+
name: Unit Tests
22+
runs-on: ubuntu-22.04-8core-32gb
23+
defaults:
24+
run:
25+
working-directory: ./sdk/@launchdarkly/observability-android
26+
steps:
27+
- uses: actions/checkout@v4
28+
with:
29+
token: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Set up JDK 17
32+
uses: actions/setup-java@v4
33+
with:
34+
distribution: 'temurin'
35+
java-version: 17
36+
37+
- name: Set up Android SDK
38+
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 commit SHA - https://github.com/android-actions/setup-android/releases/tag/v3.2.2
39+
40+
- name: Run unit tests
41+
run: ./gradlew :lib:testDebugUnitTest
42+
43+
- name: Upload test reports
44+
if: always()
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: android-unit-test-results
48+
path: sdk/@launchdarkly/observability-android/**/build/test-results/**/TEST-*.xml
49+
retention-days: 7

.release-please-manifest.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"go": "0.4.0",
3-
"sdk/@launchdarkly/observability": "0.4.9",
4-
"sdk/@launchdarkly/observability-android": "0.19.0",
3+
"sdk/@launchdarkly/observability": "0.5.0",
4+
"sdk/@launchdarkly/observability-android": "0.23.0",
55
"sdk/@launchdarkly/observability-dotnet": "0.3.0",
66
"sdk/@launchdarkly/observability-node": "0.3.1",
77
"sdk/@launchdarkly/observability-python": "0.1.1",
88
"sdk/@launchdarkly/observability-react-native": "0.7.0",
9-
"sdk/@launchdarkly/session-replay": "0.4.9",
10-
"sdk/highlight-run": "9.23.0"
9+
"sdk/@launchdarkly/session-replay": "0.5.0",
10+
"sdk/highlight-run": "9.25.0"
1111
}

e2e/android/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ dependencies {
5454
// Uncomment to use the publicly released version (note this may be behind branch/main)
5555
// implementation("com.launchdarkly:launchdarkly-observability-android:0.2.0")
5656

57-
implementation("com.launchdarkly:launchdarkly-android-client-sdk:5.9.0")
57+
implementation("com.launchdarkly:launchdarkly-android-client-sdk:5.10.0")
5858

5959
implementation("io.opentelemetry:opentelemetry-api:1.51.0")
6060
implementation("io.opentelemetry:opentelemetry-sdk:1.51.0")

e2e/android/app/src/main/java/com/example/androidobservability/BaseApplication.kt

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
package com.example.androidobservability
22

33
import android.app.Application
4-
import com.launchdarkly.observability.api.Options
4+
import android.widget.ImageView
5+
import com.launchdarkly.observability.api.ObservabilityOptions
56
import com.launchdarkly.observability.client.TelemetryInspector
7+
import com.launchdarkly.observability.plugin.Observability
8+
import com.launchdarkly.observability.replay.PrivacyProfile
9+
import com.launchdarkly.observability.replay.ReplayOptions
10+
import com.launchdarkly.observability.replay.plugin.SessionReplay
11+
import com.launchdarkly.observability.replay.view
612
import com.launchdarkly.sdk.ContextKind
713
import com.launchdarkly.sdk.LDContext
814
import com.launchdarkly.sdk.android.Components
15+
import com.launchdarkly.sdk.android.LDAndroidLogging
916
import com.launchdarkly.sdk.android.LDClient
1017
import com.launchdarkly.sdk.android.LDConfig
11-
import com.launchdarkly.observability.plugin.Observability
12-
import com.launchdarkly.observability.replay.PrivacyProfile
13-
import com.launchdarkly.observability.replay.ReplayInstrumentation
14-
import com.launchdarkly.observability.replay.ReplayOptions
15-
import com.launchdarkly.sdk.android.LDAndroidLogging
16-
import com.launchdarkly.sdk.android.integrations.Plugin
1718
import io.opentelemetry.api.common.AttributeKey
1819
import io.opentelemetry.api.common.Attributes
19-
import java.util.Collections
2020

2121
open class BaseApplication : Application() {
2222

@@ -26,20 +26,17 @@ open class BaseApplication : Application() {
2626
const val LAUNCHDARKLY_MOBILE_KEY = "MOBILE_KEY_GOES_HERE"
2727
}
2828

29-
var pluginOptions = Options(
29+
var observabilityOptions = ObservabilityOptions(
3030
resourceAttributes = Attributes.of(
3131
AttributeKey.stringKey("example"), "value"
3232
),
3333
debug = true,
34-
logAdapter = LDAndroidLogging.adapter(),
35-
// TODO: consider these being factories so that the obs plugin can pass instantiation data, log adapter
36-
instrumentations = listOf(
37-
ReplayInstrumentation(
38-
options = ReplayOptions(
39-
privacyProfile = PrivacyProfile(maskText = false)
40-
)
41-
)
34+
tracesApi = ObservabilityOptions.TracesApi(includeErrors = false, includeSpans = false),
35+
metricsApi = ObservabilityOptions.MetricsApi.disabled(),
36+
instrumentations = ObservabilityOptions.Instrumentations(
37+
crashReporting = true, launchTime = true, activityLifecycle = false
4238
),
39+
logAdapter = LDAndroidLogging.adapter(),
4340
)
4441

4542
var telemetryInspector: TelemetryInspector? = null
@@ -49,7 +46,19 @@ open class BaseApplication : Application() {
4946
val observabilityPlugin = Observability(
5047
application = this@BaseApplication,
5148
mobileKey = LAUNCHDARKLY_MOBILE_KEY,
52-
options = testUrl?.let { pluginOptions.copy(backendUrl = it, otlpEndpoint = it) } ?: pluginOptions
49+
options = testUrl?.let { observabilityOptions.copy(backendUrl = it, otlpEndpoint = it) } ?: observabilityOptions
50+
)
51+
52+
val sessionReplayPlugin = SessionReplay(
53+
options = ReplayOptions(
54+
privacyProfile = PrivacyProfile(
55+
maskText = false,
56+
maskViews = listOf(
57+
view(ImageView::class.java),
58+
view("android.widget.TextView")
59+
),
60+
maskXMLViewIds = listOf("smoothieTitle"))
61+
)
5362
)
5463

5564
// Set LAUNCHDARKLY_MOBILE_KEY to your LaunchDarkly mobile key found on the LaunchDarkly
@@ -60,7 +69,10 @@ open class BaseApplication : Application() {
6069
.mobileKey(LAUNCHDARKLY_MOBILE_KEY)
6170
.plugins(
6271
Components.plugins().setPlugins(
63-
Collections.singletonList<Plugin>(observabilityPlugin)
72+
listOf(
73+
observabilityPlugin,
74+
sessionReplayPlugin
75+
)
6476
)
6577
)
6678
.build()

e2e/android/app/src/main/java/com/example/androidobservability/MainActivity.kt

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.Arrangement
1818
import androidx.compose.foundation.rememberScrollState
1919
import androidx.compose.foundation.verticalScroll
2020
import androidx.compose.material3.Button
21+
import androidx.compose.material3.ButtonDefaults
2122
import androidx.compose.material3.OutlinedTextField
2223
import androidx.compose.material3.Scaffold
2324
import androidx.compose.material3.Text
@@ -36,12 +37,16 @@ import androidx.compose.foundation.layout.FlowRow
3637
import androidx.compose.foundation.layout.ExperimentalLayoutApi
3738
import androidx.compose.material3.HorizontalDivider
3839
import androidx.compose.ui.platform.LocalContext
40+
import androidx.compose.ui.graphics.Color
3941
import com.example.androidobservability.masking.ComposeMaskingActivity
4042
import com.example.androidobservability.masking.ComposeUserFormActivity
4143
import com.example.androidobservability.masking.XMLUserFormActivity
4244
import com.example.androidobservability.masking.XMLMaskingActivity
4345
import com.example.androidobservability.smoothie.SmoothieListActivity
4446
import com.example.androidobservability.ui.theme.AndroidObservabilityTheme
47+
import com.example.androidobservability.ui.theme.DangerRed
48+
import com.example.androidobservability.ui.theme.IdentifyBgColor
49+
import com.example.androidobservability.ui.theme.IdentifyTextColor
4550

4651
class MainActivity : ComponentActivity() {
4752

@@ -81,6 +86,8 @@ class MainActivity : ComponentActivity() {
8186
)
8287
HorizontalDivider(modifier = Modifier.padding(bottom = 16.dp))
8388

89+
IdentifyButtons(viewModel = viewModel)
90+
8491
InstrumentationButtons(viewModel = viewModel)
8592

8693
MetricButtons(viewModel = viewModel)
@@ -184,7 +191,11 @@ private fun InstrumentationButtons(viewModel: ViewModel) {
184191
Button(
185192
onClick = {
186193
viewModel.triggerCrash()
187-
}
194+
},
195+
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
196+
containerColor = DangerRed,
197+
contentColor = Color.White
198+
)
188199
) {
189200
Text("Trigger Crash")
190201
}
@@ -316,12 +327,57 @@ private fun MaskingButtons() {
316327
}
317328
}
318329

330+
@Composable
331+
private fun IdentifyButtons(viewModel: ViewModel) {
332+
Spacer(modifier = Modifier.height(16.dp))
333+
334+
Text(
335+
text = "Identify:",
336+
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
337+
modifier = Modifier.padding(bottom = 8.dp, top = 8.dp)
338+
)
339+
340+
Row(
341+
modifier = Modifier
342+
.fillMaxWidth()
343+
.padding(start = 8.dp, bottom = 8.dp),
344+
horizontalArrangement = Arrangement.spacedBy(8.dp)
345+
) {
346+
Button(
347+
onClick = { viewModel.identifyUser() },
348+
colors = ButtonDefaults.buttonColors(
349+
containerColor = IdentifyBgColor,
350+
contentColor = IdentifyTextColor
351+
)
352+
) {
353+
Text("User")
354+
}
355+
Button(
356+
onClick = { viewModel.identifyMulti() },
357+
colors = ButtonDefaults.buttonColors(
358+
containerColor = IdentifyBgColor,
359+
contentColor = IdentifyTextColor
360+
)
361+
) {
362+
Text("Multi")
363+
}
364+
Button(
365+
onClick = { viewModel.identifyAnonymous() },
366+
colors = ButtonDefaults.buttonColors(
367+
containerColor = IdentifyBgColor,
368+
contentColor = IdentifyTextColor
369+
)
370+
) {
371+
Text("Anon")
372+
}
373+
}
374+
}
375+
319376
@Composable
320377
private fun CustomerApiButtons(viewModel: ViewModel) {
321378
var customLogText by remember { mutableStateOf("") }
322379
var customSpanText by remember { mutableStateOf("") }
323380
var flagKey by remember { mutableStateOf("") }
324-
var customContextKey by remember { mutableStateOf("") }
325381

326382
Text(
327383
text = "Customer API",
@@ -332,7 +388,11 @@ private fun CustomerApiButtons(viewModel: ViewModel) {
332388
Button(
333389
onClick = {
334390
viewModel.triggerError()
335-
}
391+
},
392+
colors = ButtonDefaults.buttonColors(
393+
containerColor = DangerRed,
394+
contentColor = Color.White
395+
)
336396
) {
337397
Text("Trigger Error")
338398
}
@@ -400,20 +460,5 @@ private fun CustomerApiButtons(viewModel: ViewModel) {
400460
Text("Evaluate boolean flag")
401461
}
402462

403-
Spacer(modifier = Modifier.height(16.dp))
404463

405-
OutlinedTextField(
406-
value = customContextKey,
407-
onValueChange = { customContextKey = it },
408-
label = { Text("LD context key") },
409-
modifier = Modifier.padding(8.dp)
410-
)
411-
Button(
412-
onClick = {
413-
viewModel.identifyLDContext(customContextKey)
414-
},
415-
modifier = Modifier.padding(8.dp)
416-
) {
417-
Text("Identify LD Context")
418-
}
419464
}

0 commit comments

Comments
 (0)