diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..5facbdfb4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,2 @@
+[*.{kt,kts}]
+ktlint_function_naming_ignore_when_annotated_with=Composable
diff --git a/.github/workflows/pr-check.yaml b/.github/workflows/pr-check.yaml
index dfa336d1f..1b0d553f6 100644
--- a/.github/workflows/pr-check.yaml
+++ b/.github/workflows/pr-check.yaml
@@ -13,6 +13,8 @@ jobs:
with:
distribution: temurin
java-version: 17
+ - name: touch local props
+ run: touch local.properties
- name: run gradle
run: ./gradlew check
diff --git a/buildSrc/src/main/kotlin/otel.errorprone-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.errorprone-conventions.gradle.kts
index 669532c42..680f64d28 100644
--- a/buildSrc/src/main/kotlin/otel.errorprone-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/otel.errorprone-conventions.gradle.kts
@@ -47,6 +47,8 @@ tasks {
nullaway {
severity.set(CheckSeverity.ERROR)
+ // Prevent generated binding code in demo app from failing the build
+ unannotatedSubPackages.add("io.opentelemetry.android.demo.databinding")
}
// Builder 'return this;' pattern
diff --git a/demo-app/README.md b/demo-app/README.md
new file mode 100644
index 000000000..1870f3d76
--- /dev/null
+++ b/demo-app/README.md
@@ -0,0 +1,16 @@
+
+# OpenTelemetry Android Demo App
+
+This is an app built to demonstrate how to configure and use the OpenTelemetry Android agent
+to observe app and user behavior.
+
+This is very much a work in progress.
+
+## Features
+
+* TBD
+
+
+## How to use
+
+* TBD
diff --git a/demo-app/build.gradle.kts b/demo-app/build.gradle.kts
new file mode 100644
index 000000000..de5bd6049
--- /dev/null
+++ b/demo-app/build.gradle.kts
@@ -0,0 +1,72 @@
+import java.io.FileInputStream
+import java.util.Properties
+
+plugins {
+ id("otel.android-app-conventions")
+}
+
+val localProperties = Properties()
+localProperties.load(FileInputStream(rootProject.file("local.properties")))
+
+android {
+ namespace = "io.opentelemetry.android.demo"
+
+ defaultConfig {
+ applicationId = "io.opentelemetry.android.demo"
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary = true
+ }
+ }
+
+ buildTypes {
+ all {
+ val accessToken = localProperties["rum.access.token"] as String?
+ resValue("string", "rum_access_token", accessToken ?: "fakebroken")
+ }
+ release {
+ isMinifyEnabled = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+ buildFeatures {
+ compose = true
+ viewBinding = true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion = "1.5.13"
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.material)
+ implementation(libs.androidx.lifecycle.livedata.ktx)
+ implementation(libs.androidx.lifecycle.viewmodel.ktx)
+ implementation(libs.androidx.navigation.fragment.ktx)
+ implementation(libs.androidx.navigation.ui.ktx)
+ coreLibraryDesugaring(libs.desugarJdkLibs)
+
+ implementation(project(":android-agent"))
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.ui)
+ implementation(libs.androidx.ui.graphics)
+ implementation(libs.androidx.ui.tooling.preview)
+ implementation(libs.androidx.material3)
+ implementation(libs.opentelemetry.sdk)
+ implementation(libs.opentelemetry.exporter.otlp)
+ testImplementation(libs.bundles.junit)
+ androidTestImplementation(libs.androidx.junit)
+ debugImplementation(libs.androidx.ui.tooling)
+ debugImplementation(libs.androidx.ui.test.manifest)
+}
diff --git a/demo-app/src/androidTest/java/io/opentelemetry/android/demo/ExampleInstrumentedTest.kt b/demo-app/src/androidTest/java/io/opentelemetry/android/demo/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..36c60ee27
--- /dev/null
+++ b/demo-app/src/androidTest/java/io/opentelemetry/android/demo/ExampleInstrumentedTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ Assert.assertEquals("io.opentelemetry.android.demo", appContext.packageName)
+ }
+}
diff --git a/demo-app/src/main/AndroidManifest.xml b/demo-app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..c1cc57afb
--- /dev/null
+++ b/demo-app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/ic_launcher-playstore.png b/demo-app/src/main/ic_launcher-playstore.png
new file mode 100644
index 000000000..bb6d13ee4
Binary files /dev/null and b/demo-app/src/main/ic_launcher-playstore.png differ
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/AstronomyShopActivity.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/AstronomyShopActivity.kt
new file mode 100644
index 000000000..123baeb0b
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/AstronomyShopActivity.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.findNavController
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.setupActionBarWithNavController
+import androidx.navigation.ui.setupWithNavController
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import io.opentelemetry.android.demo.databinding.ActivityAstronomyShopBinding
+
+class AstronomyShopActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityAstronomyShopBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = ActivityAstronomyShopBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ val navView: BottomNavigationView = binding.navView
+
+ val navController = findNavController(R.id.nav_host_fragment_activity_astronomy_shop)
+ // Passing each menu ID as a set of Ids because each
+ // menu should be considered as top level destinations.
+ val appBarConfiguration =
+ AppBarConfiguration(
+ setOf(
+ R.id.navigation_home,
+ R.id.navigation_dashboard,
+ R.id.navigation_notifications,
+ ),
+ )
+ setupActionBarWithNavController(navController, appBarConfiguration)
+ navView.setupWithNavController(navController)
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/CenterText.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/CenterText.kt
new file mode 100644
index 000000000..3b02b973b
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/CenterText.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.sp
+
+val gothamFont =
+ FontFamily(
+ Font(R.font.gotham_bold, FontWeight.Bold),
+ )
+
+@Composable
+fun CenterText(
+ text: AnnotatedString,
+ fontSize: TextUnit = 12.sp,
+) {
+ Text(
+ text,
+ textAlign = TextAlign.Center,
+ fontSize = fontSize,
+ modifier = Modifier.fillMaxWidth(),
+ fontFamily = gothamFont,
+ style = TextStyle.Default.copy(textAlign = TextAlign.Center),
+ )
+}
+
+@Composable
+fun CenterText(
+ text: String,
+ fontSize: TextUnit = 12.sp,
+) {
+ Text(
+ text,
+ textAlign = TextAlign.Center,
+ fontSize = fontSize,
+ modifier = Modifier.fillMaxWidth(),
+ fontFamily = gothamFont,
+ style = TextStyle.Default.copy(textAlign = TextAlign.Center),
+ )
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/DemoViewModel.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/DemoViewModel.kt
new file mode 100644
index 000000000..d5175bfb4
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/DemoViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+class DemoViewModel : ViewModel() {
+ val sessionIdState = MutableStateFlow("? unknown ?")
+ private val tracer = OtelSampleApplication.tracer("otel.demo")!!
+
+ init {
+ viewModelScope.launch {
+ while (true) {
+ delay(5000)
+ // TODO: Do some work here maybe
+ }
+ }
+ }
+
+ private fun updateSession() {
+ // TODO
+ }
+
+ private fun sendTrace(
+ type: String,
+ value: Float,
+ ) {
+ // A metric should be a better fit, but for now we're using spans.
+ tracer.spanBuilder(type).setAttribute("value", value.toDouble()).startSpan().end()
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/MainActivity.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/MainActivity.kt
new file mode 100644
index 000000000..36ad68275
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/MainActivity.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import io.opentelemetry.android.demo.theme.DemoAppTheme
+
+class MainActivity : ComponentActivity() {
+ private val viewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ DemoAppTheme {
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceEvenly,
+ ) {
+ Row(
+ Modifier.padding(all = 20.dp),
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ CenterText(
+ fontSize = 40.sp,
+ text =
+ buildAnnotatedString {
+ withStyle(style = SpanStyle(color = Color(0xFFF5A800))) {
+ append("Open")
+ }
+ withStyle(style = SpanStyle(color = Color(0xFF425CC7))) {
+ append("Telemetry")
+ }
+ withStyle(style = SpanStyle(color = Color.Black)) {
+ append(" Android Demo")
+ }
+ toAnnotatedString()
+ },
+ )
+ }
+ SessionId(viewModel.sessionIdState)
+ MainOtelButton(
+ painterResource(id = R.drawable.otel_icon),
+ )
+ val context = LocalContext.current
+ OpenStoreButton(text = "Click to begin", onClick = {
+ context.startActivity(Intent(this@MainActivity, AstronomyShopActivity::class.java))
+ })
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/MainOtelButton.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/MainOtelButton.kt
new file mode 100644
index 000000000..4d60c81ff
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/MainOtelButton.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.unit.dp
+import io.opentelemetry.api.trace.SpanKind
+
+@Composable
+fun MainOtelButton(icon: Painter) {
+ Row {
+ Spacer(modifier = Modifier.height(5.dp))
+ Button(
+ onClick = { generateClickEvent() },
+ modifier = Modifier.padding(20.dp),
+ colors = ButtonDefaults.buttonColors(containerColor = Color.Black),
+ content = {
+ Image(
+ painter = icon,
+ contentDescription = null,
+ Modifier
+ .width(150.dp)
+ .padding(30.dp),
+ )
+ },
+ )
+ }
+}
+
+fun generateClickEvent() {
+ // TODO: Make an actual otel event
+ val tracer = OtelSampleApplication.tracer("otel.demo")
+ val span =
+ tracer
+ ?.spanBuilder("logo.clicked")
+ ?.setSpanKind(SpanKind.INTERNAL)
+ ?.startSpan()
+ span?.end()
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/OpenStoreButton.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/OpenStoreButton.kt
new file mode 100644
index 000000000..4a972ed70
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/OpenStoreButton.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun OpenStoreButton(
+ text: String,
+ onClick: () -> Unit,
+) {
+ Row {
+ Spacer(modifier = Modifier.height(5.dp))
+ }
+ Row {
+ Button(
+ onClick = onClick,
+ border = BorderStroke(1.dp, Color.Gray),
+ modifier = Modifier.padding(20.dp).height(90.dp),
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = Color(0xFF425CC7),
+ ),
+ content = {
+ CenterText(text = text, fontSize = 30.sp)
+ },
+ )
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/OtelSampleApplication.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/OtelSampleApplication.kt
new file mode 100644
index 000000000..4d66d7872
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/OtelSampleApplication.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import android.annotation.SuppressLint
+import android.app.Application
+import android.util.Log
+import io.opentelemetry.android.OpenTelemetryRum
+import io.opentelemetry.android.OpenTelemetryRumBuilder
+import io.opentelemetry.android.config.OtelRumConfig
+import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration
+import io.opentelemetry.api.common.AttributeKey.stringKey
+import io.opentelemetry.api.common.Attributes
+import io.opentelemetry.api.trace.Tracer
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
+
+private const val TAG = "otel.demo"
+
+class OtelSampleApplication : Application() {
+ @SuppressLint("RestrictedApi")
+ override fun onCreate() {
+ super.onCreate()
+
+ Log.i(TAG, "Initializing the opentelemetry-android-agent")
+ val diskBufferingConfig =
+ DiskBufferingConfiguration.builder()
+ .setEnabled(true)
+ .setMaxCacheSize(10_000_000)
+ .build()
+ val config =
+ OtelRumConfig()
+ .setGlobalAttributes(Attributes.of(stringKey("toolkit"), "jetpack compose"))
+ .setDiskBufferingConfiguration(diskBufferingConfig)
+
+ // 10.0.2.2 is apparently a special binding to the host running the emulator
+ val ingestUrl = "http://10.0.2.2:4318/v1/traces"
+ val otelRumBuilder: OpenTelemetryRumBuilder =
+ OpenTelemetryRum.builder(this, config)
+ .addSpanExporterCustomizer {
+ OtlpHttpSpanExporter.builder()
+ .setEndpoint(ingestUrl)
+ .build()
+ }
+ try {
+ rum = otelRumBuilder.build()
+ Log.d(TAG, "RUM session started: " + rum!!.rumSessionId)
+ } catch (e: Exception) {
+ Log.e(TAG, "Oh no!", e)
+ }
+ }
+
+ companion object {
+ var rum: OpenTelemetryRum? = null
+
+ fun tracer(name: String): Tracer? {
+ return rum?.openTelemetry?.tracerProvider?.get(name)
+ }
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/SessionId.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/SessionId.kt
new file mode 100644
index 000000000..f702c016e
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/SessionId.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Card
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import kotlinx.coroutines.flow.StateFlow
+
+@Composable
+fun SessionId(sessionId: StateFlow) {
+ Row {
+ Card(modifier = Modifier.size(width = 250.dp, height = 75.dp)) {
+ Column(
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(vertical = 25.dp),
+ Arrangement.Center,
+ ) {
+ CenterText(text = "session.id", fontSize = 12.sp)
+ CenterText(text = sessionId.collectAsState().value, fontSize = 12.sp)
+ }
+ }
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Color.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Color.kt
new file mode 100644
index 000000000..b8c5746b8
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Color.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Theme.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Theme.kt
new file mode 100644
index 000000000..6604f1301
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Theme.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme =
+ darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80,
+ )
+
+private val LightColorScheme =
+ lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40,
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+ )
+
+@Composable
+fun DemoAppTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit,
+) {
+ val colorScheme =
+ when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content,
+ )
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Type.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Type.kt
new file mode 100644
index 000000000..cac0208a7
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/theme/Type.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography =
+ Typography(
+ bodyLarge =
+ TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp,
+ ),
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+ )
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardFragment.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardFragment.kt
new file mode 100644
index 000000000..9a4481934
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardFragment.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.dashboard
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import io.opentelemetry.android.demo.databinding.FragmentDashboardBinding
+
+class DashboardFragment : Fragment() {
+ // Renamed from _binding (default) due to ktlint problem below
+ private var binding: FragmentDashboardBinding? = null
+
+ // This property is only valid between onCreateView and onDestroyView.
+ // Currently failing ktlint due to
+ // https://github.com/pinterest/ktlint/issues/2448
+ // which hasn't hit the aging gradle spotless plugin yet
+// private val binding get() = _binding!!
+ fun getBinding(): FragmentDashboardBinding {
+ return binding!!
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ val dashboardViewModel =
+ ViewModelProvider(this).get(DashboardViewModel::class.java)
+
+ binding = FragmentDashboardBinding.inflate(inflater, container, false)
+ val root: View = getBinding().root
+
+ val textView: TextView = getBinding().textDashboard
+ dashboardViewModel.text.observe(viewLifecycleOwner) {
+ textView.text = it
+ }
+ return root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ binding = null
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardViewModel.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardViewModel.kt
new file mode 100644
index 000000000..af4464db2
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/dashboard/DashboardViewModel.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.dashboard
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class DashboardViewModel : ViewModel() {
+ private val _text =
+ MutableLiveData().apply {
+ value = "This is dashboard Fragment"
+ }
+ val text: LiveData = _text
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeFragment.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeFragment.kt
new file mode 100644
index 000000000..1b80db8a0
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeFragment.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.home
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import io.opentelemetry.android.demo.databinding.FragmentHomeBinding
+
+class HomeFragment : Fragment() {
+ // Renamed from _binding (default) due to ktlint problem below
+ private var binding: FragmentHomeBinding? = null
+
+ // This property is only valid between onCreateView and onDestroyView.
+ // Currently failing ktlint due to
+ // https://github.com/pinterest/ktlint/issues/2448
+ // which hasn't hit the aging gradle spotless plugin yet
+// private val binding get() = _binding!!
+ private fun getBinding(): FragmentHomeBinding {
+ return binding!!
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ val homeViewModel =
+ ViewModelProvider(this).get(HomeViewModel::class.java)
+
+ binding = FragmentHomeBinding.inflate(inflater, container, false)
+ val root: View = getBinding().root
+
+ val textView: TextView = getBinding().textHome
+ homeViewModel.text.observe(viewLifecycleOwner) {
+ textView.text = it
+ }
+ return root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ binding = null
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeViewModel.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeViewModel.kt
new file mode 100644
index 000000000..ccd2c2157
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/home/HomeViewModel.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.home
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class HomeViewModel : ViewModel() {
+ private val _text =
+ MutableLiveData().apply {
+ value = "This is home Fragment"
+ }
+ val text: LiveData = _text
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsFragment.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsFragment.kt
new file mode 100644
index 000000000..34b19adde
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsFragment.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.notifications
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import io.opentelemetry.android.demo.databinding.FragmentNotificationsBinding
+
+class NotificationsFragment : Fragment() {
+ // Renamed from _binding (default) due to ktlint problem below
+ private var binding: FragmentNotificationsBinding? = null
+
+ // This property is only valid between onCreateView and onDestroyView.
+ // Currently failing ktlint due to
+ // https://github.com/pinterest/ktlint/issues/2448
+ // which hasn't hit the aging gradle spotless plugin yet
+// private val binding get() = _binding!!
+ private fun getBinding(): FragmentNotificationsBinding {
+ return binding!!
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ val notificationsViewModel =
+ ViewModelProvider(this).get(NotificationsViewModel::class.java)
+
+ binding = FragmentNotificationsBinding.inflate(inflater, container, false)
+ val root: View = getBinding().root
+
+ val textView: TextView = getBinding().textNotifications
+ notificationsViewModel.text.observe(viewLifecycleOwner) {
+ textView.text = it
+ }
+ return root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ binding = null
+ }
+}
diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsViewModel.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsViewModel.kt
new file mode 100644
index 000000000..678151b81
--- /dev/null
+++ b/demo-app/src/main/java/io/opentelemetry/android/demo/ui/notifications/NotificationsViewModel.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.android.demo.ui.notifications
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class NotificationsViewModel : ViewModel() {
+ private val _text =
+ MutableLiveData().apply {
+ value = "This is notifications Fragment"
+ }
+ val text: LiveData = _text
+}
diff --git a/demo-app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/demo-app/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 000000000..46fc8deec
--- /dev/null
+++ b/demo-app/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo-app/src/main/res/drawable/ic_home_black_24dp.xml b/demo-app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 000000000..f8bb0b556
--- /dev/null
+++ b/demo-app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo-app/src/main/res/drawable/ic_launcher_background.xml b/demo-app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..ca3826a46
--- /dev/null
+++ b/demo-app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-app/src/main/res/drawable/ic_launcher_foreground.xml b/demo-app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/demo-app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/drawable/ic_notifications_black_24dp.xml b/demo-app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 000000000..78b75c39b
--- /dev/null
+++ b/demo-app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/demo-app/src/main/res/drawable/otel_icon.png b/demo-app/src/main/res/drawable/otel_icon.png
new file mode 100644
index 000000000..3d24a3408
Binary files /dev/null and b/demo-app/src/main/res/drawable/otel_icon.png differ
diff --git a/demo-app/src/main/res/font/gotham_bold.ttf b/demo-app/src/main/res/font/gotham_bold.ttf
new file mode 100644
index 000000000..8759d2bd1
Binary files /dev/null and b/demo-app/src/main/res/font/gotham_bold.ttf differ
diff --git a/demo-app/src/main/res/layout/activity_astronomy_shop.xml b/demo-app/src/main/res/layout/activity_astronomy_shop.xml
new file mode 100644
index 000000000..87f9dba2c
--- /dev/null
+++ b/demo-app/src/main/res/layout/activity_astronomy_shop.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/layout/fragment_dashboard.xml b/demo-app/src/main/res/layout/fragment_dashboard.xml
new file mode 100644
index 000000000..166ab0e9e
--- /dev/null
+++ b/demo-app/src/main/res/layout/fragment_dashboard.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/layout/fragment_home.xml b/demo-app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 000000000..f3d9b08ff
--- /dev/null
+++ b/demo-app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/layout/fragment_notifications.xml b/demo-app/src/main/res/layout/fragment_notifications.xml
new file mode 100644
index 000000000..d41793572
--- /dev/null
+++ b/demo-app/src/main/res/layout/fragment_notifications.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/menu/bottom_nav_menu.xml b/demo-app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 000000000..fb6d040b9
--- /dev/null
+++ b/demo-app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp b/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..de0ab944b
Binary files /dev/null and b/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/demo-app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/demo-app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..f91d62b78
Binary files /dev/null and b/demo-app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..d0ec4ec79
Binary files /dev/null and b/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp b/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..8e8eaf9a3
Binary files /dev/null and b/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/demo-app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/demo-app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..fd36de29c
Binary files /dev/null and b/demo-app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..30ee23e7d
Binary files /dev/null and b/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..b02a32f57
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..4097dc285
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..3ac0b8525
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..e849b1cad
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..6f53c8e18
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..e9bb5ef0f
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..f34a218c6
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..52510f46f
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..3257f3a29
Binary files /dev/null and b/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/demo-app/src/main/res/navigation/mobile_navigation.xml b/demo-app/src/main/res/navigation/mobile_navigation.xml
new file mode 100644
index 000000000..edf04115c
--- /dev/null
+++ b/demo-app/src/main/res/navigation/mobile_navigation.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/values/colors.xml b/demo-app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/demo-app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/values/dimens.xml b/demo-app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..e00c2dd14
--- /dev/null
+++ b/demo-app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/values/ic_launcher_background.xml b/demo-app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..c5d5899fd
--- /dev/null
+++ b/demo-app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/values/strings.xml b/demo-app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..de0f7312c
--- /dev/null
+++ b/demo-app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ OpenTelemetryDemoApp
+ AstronomyShopActivity
+ Home
+ Dashboard
+ Notifications
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/values/themes.xml b/demo-app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..f68cbe809
--- /dev/null
+++ b/demo-app/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/xml/backup_rules.xml b/demo-app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/demo-app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/xml/data_extraction_rules.xml b/demo-app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/demo-app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-app/src/main/res/xml/network_security_config.xml b/demo-app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 000000000..69de2ad25
--- /dev/null
+++ b/demo-app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 33a612fa8..6f23a13b6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -35,6 +35,7 @@ volley = "com.android.volley:volley:1.2.1"
opentelemetry-sdk-testing = { module = "io.opentelemetry:opentelemetry-sdk-testing", version.ref = "opentelemetry" }
androidx-test-core = "androidx.test:core:1.5.0"
androidx-test-runner = "androidx.test:runner:1.5.2"
+androidx-junit = "androidx.test.ext:junit:1.1.5"
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
mockk = "io.mockk:mockk:1.13.10"
@@ -60,6 +61,24 @@ android-plugin = "com.android.tools.build:gradle:8.4.0"
byteBuddy-plugin = { module = "net.bytebuddy:byte-buddy-gradle-plugin", version.ref = "byteBuddy" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
+# demo-app
+androidx-core-ktx = "androidx.core:core-ktx:1.13.0"
+androidx-lifecycle-runtime-ktx = "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"
+androidx-compose-bom = "androidx.compose:compose-bom:2024.05.00"
+androidx-activity-compose = "androidx.activity:activity-compose:1.9.0"
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
+material = { group = "com.google.android.material", name = "material", version = "1.12.0" }
+androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version = "2.7.0" }
+androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version = "2.7.0" }
+androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version = "2.7.7" }
+androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version = "2.7.7" }
+
[bundles]
mocking = ["mockito-core", "mockito-junit-jupiter", "mockk"]
junit = ["junit-jupiter-api", "junit-jupiter-engine", "junit-vintage-engine"]
@@ -67,3 +86,4 @@ junit = ["junit-jupiter-api", "junit-jupiter-engine", "junit-vintage-engine"]
[plugins]
publishPlugin = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
+kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6ad7204c7..6ef29b1dc 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -15,3 +15,4 @@ include(":instrumentation:network")
include(":instrumentation:slowrendering")
include(":instrumentation:startup")
include(":instrumentation:volley:library")
+include(":demo-app")