Skip to content

Commit 1245969

Browse files
oschwaldclaude
andcommitted
feat(sample): Add collapsible sections for device data display
Display device data in expandable/collapsible sections instead of raw JSON. Uses reflection to dynamically iterate over DeviceData properties, so new fields are automatically included without code changes. Changes: - Add collapsible section UI with tap to expand/collapse - Show summary at top, detailed JSON in collapsible sections below - Add kotlin-reflect dependency for dynamic property iteration - All sections collapsed by default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent d306783 commit 1245969

File tree

4 files changed

+106
-11
lines changed

4 files changed

+106
-11
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ material = "1.12.0"
4242
[libraries]
4343
# Kotlin
4444
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
45+
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
4546
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
4647
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
4748
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }

sample/build.gradle.kts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
plugins {
22
alias(libs.plugins.android.application)
33
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.kotlin.serialization)
45
alias(libs.plugins.detekt)
56
alias(libs.plugins.ktlint)
67
}
78

89
android {
910
namespace = "com.maxmind.device.sample"
10-
compileSdk = libs.versions.compileSdk.get().toInt()
11+
compileSdk =
12+
libs.versions.compileSdk
13+
.get()
14+
.toInt()
1115

1216
defaultConfig {
1317
applicationId = "com.maxmind.device.sample"
14-
minSdk = libs.versions.minSdk.get().toInt()
15-
targetSdk = libs.versions.targetSdk.get().toInt()
18+
minSdk =
19+
libs.versions.minSdk
20+
.get()
21+
.toInt()
22+
targetSdk =
23+
libs.versions.targetSdk
24+
.get()
25+
.toInt()
1626
versionCode = 1
1727
versionName = "1.0"
1828

@@ -24,7 +34,7 @@ android {
2434
isMinifyEnabled = true
2535
proguardFiles(
2636
getDefaultProguardFile("proguard-android-optimize.txt"),
27-
"proguard-rules.pro"
37+
"proguard-rules.pro",
2838
)
2939
}
3040
debug {
@@ -52,8 +62,10 @@ dependencies {
5262

5363
// Kotlin
5464
implementation(libs.kotlin.stdlib)
65+
implementation(libs.kotlin.reflect)
5566
implementation(libs.kotlinx.coroutines.core)
5667
implementation(libs.kotlinx.coroutines.android)
68+
implementation(libs.kotlinx.serialization.json)
5769

5870
// AndroidX
5971
implementation(libs.androidx.core.ktx)

sample/src/main/java/com/maxmind/device/sample/MainActivity.kt

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
package com.maxmind.device.sample
22

3+
import android.graphics.Typeface
34
import android.os.Bundle
45
import android.util.Log
6+
import android.util.TypedValue
7+
import android.view.View
8+
import android.widget.LinearLayout
9+
import android.widget.TextView
510
import androidx.appcompat.app.AppCompatActivity
611
import androidx.lifecycle.lifecycleScope
712
import com.google.android.material.snackbar.Snackbar
813
import com.maxmind.device.DeviceTracker
914
import com.maxmind.device.config.SdkConfig
1015
import com.maxmind.device.sample.databinding.ActivityMainBinding
1116
import kotlinx.coroutines.launch
17+
import kotlinx.serialization.encodeToString
18+
import kotlinx.serialization.json.Json
19+
import kotlinx.serialization.serializer
20+
import kotlin.reflect.full.memberProperties
1221

1322
/**
1423
* Main activity demonstrating the MaxMind Device Tracker usage.
@@ -17,6 +26,7 @@ class MainActivity : AppCompatActivity() {
1726

1827
private lateinit var binding: ActivityMainBinding
1928
private var logText = StringBuilder()
29+
private val json = Json { prettyPrint = true }
2030

2131
override fun onCreate(savedInstanceState: Bundle?) {
2232
super.onCreate(savedInstanceState)
@@ -92,6 +102,25 @@ class MainActivity : AppCompatActivity() {
92102
appendLog(" SDK Version: ${deviceData.build.sdkVersion}")
93103
appendLog(" Screen: ${deviceData.display.widthPixels}x${deviceData.display.heightPixels} (${deviceData.display.densityDpi}dpi)")
94104
appendLog(" Timestamp: ${deviceData.deviceTime}")
105+
appendLog("")
106+
107+
// Dynamically add collapsible sections for each property
108+
deviceData::class.memberProperties.forEach { prop ->
109+
val value = prop.getter.call(deviceData)
110+
if (value != null) {
111+
val content = try {
112+
json.encodeToString(serializer(prop.returnType), value)
113+
} catch (e: Exception) {
114+
value.toString()
115+
}
116+
addCollapsibleSection(prop.name, content)
117+
}
118+
}
119+
120+
// Scroll to top to show summary first
121+
binding.scrollView.post {
122+
binding.scrollView.fullScroll(android.view.View.FOCUS_UP)
123+
}
95124

96125
showMessage("Device data collected")
97126
} catch (e: Exception) {
@@ -102,6 +131,46 @@ class MainActivity : AppCompatActivity() {
102131
}
103132
}
104133

134+
private fun addCollapsibleSection(title: String, content: String) {
135+
val header = TextView(this).apply {
136+
text = "$title"
137+
setTypeface(typeface, Typeface.BOLD)
138+
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
139+
setPadding(0, dpToPx(12), 0, dpToPx(4))
140+
setTextColor(getColor(R.color.section_header))
141+
}
142+
143+
val contentView = TextView(this).apply {
144+
text = content
145+
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
146+
typeface = Typeface.MONOSPACE
147+
setPadding(dpToPx(16), dpToPx(4), 0, dpToPx(12))
148+
setTextColor(getColor(R.color.section_content))
149+
setBackgroundColor(getColor(R.color.surface))
150+
visibility = View.GONE
151+
}
152+
153+
header.setOnClickListener {
154+
if (contentView.visibility == View.GONE) {
155+
contentView.visibility = View.VISIBLE
156+
header.text = "$title"
157+
} else {
158+
contentView.visibility = View.GONE
159+
header.text = "$title"
160+
}
161+
}
162+
163+
binding.logContainer.addView(header)
164+
binding.logContainer.addView(contentView)
165+
}
166+
167+
private fun dpToPx(dp: Int): Int =
168+
TypedValue.applyDimension(
169+
TypedValue.COMPLEX_UNIT_DIP,
170+
dp.toFloat(),
171+
resources.displayMetrics
172+
).toInt()
173+
105174
private fun sendDeviceData() {
106175
try {
107176
val sdk = DeviceTracker.getInstance()
@@ -143,6 +212,11 @@ class MainActivity : AppCompatActivity() {
143212
private fun clearLog() {
144213
logText.clear()
145214
binding.tvLog.text = ""
215+
// Remove all views except the tvLog TextView
216+
val childCount = binding.logContainer.childCount
217+
if (childCount > 1) {
218+
binding.logContainer.removeViews(1, childCount - 1)
219+
}
146220
appendLog("Log cleared.")
147221
}
148222

sample/src/main/res/layout/activity_main.xml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,23 @@
115115
android:clipToPadding="false"
116116
android:fillViewport="true">
117117

118-
<TextView
119-
android:id="@+id/tvLog"
118+
<LinearLayout
119+
android:id="@+id/logContainer"
120120
android:layout_width="match_parent"
121121
android:layout_height="wrap_content"
122-
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
123-
android:fontFamily="monospace"
124-
android:textSize="12sp"
125-
android:textIsSelectable="true"
126-
tools:text="Log output will appear here..." />
122+
android:orientation="vertical">
123+
124+
<TextView
125+
android:id="@+id/tvLog"
126+
android:layout_width="match_parent"
127+
android:layout_height="wrap_content"
128+
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
129+
android:fontFamily="monospace"
130+
android:textSize="12sp"
131+
android:textIsSelectable="true"
132+
tools:text="Log output will appear here..." />
133+
134+
</LinearLayout>
127135

128136
</ScrollView>
129137

0 commit comments

Comments
 (0)