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 @@ + + + +