Skip to content
Merged
1 change: 1 addition & 0 deletions e2e/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")

implementation("com.google.android.material:material:1.12.0")
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand Down
8 changes: 8 additions & 0 deletions e2e/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
android:exported="false"
android:label="@string/title_activity_secondary"
android:theme="@style/Theme.AndroidObservability" />
<activity
android:name=".CreditCardActivity"
android:exported="false"
android:theme="@style/Theme.AndroidObservability" />
<activity
android:name="com.smoothie.SmoothieListActivity"
android:exported="false"
android:theme="@style/Theme.AndroidObservability" />
<service
android:name=".ObservabilityForegroundService"
android:exported="false" />
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.example.androidobservability

import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.ComponentActivity

class CreditCardActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_credit_card)

val inputCardholderName = findViewById<EditText>(R.id.input_cardholder_name)
val inputCardNumber = findViewById<EditText>(R.id.input_card_number)
val inputExpiry = findViewById<EditText>(R.id.input_expiry)
val inputCvv = findViewById<EditText>(R.id.input_cvv)
val inputZip = findViewById<EditText>(R.id.input_zip_code)
val buttonSave = findViewById<Button>(R.id.button_save_card)

buttonSave.setOnClickListener {
val name = inputCardholderName.text?.toString()?.trim().orEmpty()
val number = inputCardNumber.text?.toString()?.replace(" ", "")?.trim().orEmpty()
val expiry = inputExpiry.text?.toString()?.trim().orEmpty()
val cvv = inputCvv.text?.toString()?.trim().orEmpty()
val zip = inputZip.text?.toString()?.trim().orEmpty()

var isValid = true

if (name.isEmpty()) {
inputCardholderName.error = "Cardholder name is required"
isValid = false
} else {
inputCardholderName.error = null
}

if (number.length !in 13..19 || number.any { !it.isDigit() }) {
inputCardNumber.error = "Enter a valid card number"
isValid = false
} else {
inputCardNumber.error = null
}

val expiryRegex = Regex("""^(0[1-9]|1[0-2])/\d{2}$""")
if (!expiryRegex.matches(expiry)) {
inputExpiry.error = "Use MM/YY"
isValid = false
} else {
inputExpiry.error = null
}

if (cvv.length !in 3..4 || cvv.any { !it.isDigit() }) {
inputCvv.error = "Enter a valid CVV"
isValid = false
} else {
inputCvv.error = null
}

if (zip.isEmpty() || zip.length < 3 || zip.length > 10) {
inputZip.error = "Enter a valid ZIP/Postal code"
isValid = false
} else {
inputZip.error = null
}

if (isValid) {
Toast.makeText(this, "Card saved", Toast.LENGTH_SHORT).show()
finish()
}
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ class MainActivity : ComponentActivity() {
) {
Text("Go to Secondary Activity")
}
Button(
onClick = {
[email protected](
Intent(
this@MainActivity,
CreditCardActivity::class.java
)
)
}
) {
Text("Open Credit Card (XML)")
}
Button(
onClick = {
[email protected](
Intent(
this@MainActivity,
com.smoothie.SmoothieListActivity::class.java
)
)
}
) {
Text("Open Fruta (XML)")
}
Button(
onClick = {
viewModel.triggerHttpRequests()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.example.androidobservability

import android.os.Bundle
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
Expand All @@ -26,11 +31,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.androidobservability.ui.theme.AndroidObservabilityTheme
import com.launchdarkly.observability.api.ldMask

class SecondaryActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -64,6 +71,15 @@ fun UserInfoForm(modifier: Modifier = Modifier) {
var cardholderName by remember { mutableStateOf("") }

val scrollState = rememberScrollState()
val addressRotationTransition = rememberInfiniteTransition(label = "addressRotationTransition")
val addressRotationDegrees by addressRotationTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 3000, easing = LinearEasing)
),
label = "addressRotationDegrees"
)

Column(
modifier = modifier
Expand All @@ -73,7 +89,8 @@ fun UserInfoForm(modifier: Modifier = Modifier) {
) {
Text(
text = "User Information Form",
style = MaterialTheme.typography.headlineMedium
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.ldMask()
)

// Password Section
Expand Down Expand Up @@ -102,7 +119,9 @@ fun UserInfoForm(modifier: Modifier = Modifier) {

// Address Section
Card(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.rotate(addressRotationDegrees),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
Expand Down
49 changes: 49 additions & 0 deletions e2e/android/app/src/main/java/com/smoothie/SmoothieAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.smoothie
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package should be inside com.example.androidobservability (The whole code of the app goes there - unless you use different modules, which is not the case)


import android.graphics.Bitmap
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.androidobservability.R
import com.launchdarkly.observability.api.ldMask

data class SmoothieItem(
val title: String,
val imageFileName: String
)

class SmoothieAdapter(
private val smoothies: List<SmoothieItem>,
private val imageLoader: (String) -> Bitmap?
) : RecyclerView.Adapter<SmoothieAdapter.ViewHolder>() {

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.smoothieImage)
init {
imageView.ldMask()
}
val titleView: TextView = itemView.findViewById(R.id.smoothieTitle)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_smoothie, parent, false)
return ViewHolder(view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = smoothies[position]
holder.titleView.text = item.title
val bitmap = imageLoader(item.imageFileName)
if (bitmap != null) {
holder.imageView.setImageBitmap(bitmap)
} else {
holder.imageView.setImageResource(R.mipmap.ic_launcher)
}
}

override fun getItemCount(): Int = smoothies.size
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.smoothie
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


import android.graphics.BitmapFactory
import android.os.Bundle
import android.app.Activity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.androidobservability.R

class SmoothieListActivity : Activity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_smoothie_list)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = SmoothieAdapter(
smoothies = buildSmoothies(),
imageLoader = { fileName ->
assets.open("smoothie/images/$fileName").use { input ->
BitmapFactory.decodeStream(input)
}
}
)
}

private fun buildSmoothies(): List<SmoothieItem> {
return listOf(
SmoothieItem("Berry Blue", "berry-blue.jpg"),
SmoothieItem("Carrot Chops", "carrot-chops.jpg"),
SmoothieItem("Hulking Lemonade", "hulking-lemonade.jpg"),
SmoothieItem("Kiwi Cutie", "kiwi-cutie.jpg"),
SmoothieItem("Lemonberry", "lemonberry.jpg"),
SmoothieItem("Love You Berry Much", "love-you-berry-much.jpg"),
SmoothieItem("Mango Jambo", "mango-jambo.jpg"),
SmoothieItem("One in a Melon", "one-in-a-melon.jpg"),
SmoothieItem("Papa's Papaya", "papas-papaya.jpg"),
SmoothieItem("Peanut Butter Cup", "peanut-butter-cup.jpg"),
SmoothieItem("Piña y Coco", "pina-y-coco.jpg"),
SmoothieItem("Sailor Man", "sailor-man.jpg"),
SmoothieItem("That's a S'more", "thats-a-smore.jpg"),
SmoothieItem("That's Berry Bananas", "thats-berry-bananas.jpg"),
SmoothieItem("Tropical Blue", "tropical-blue.jpg")
)
}
}

Loading
Loading