Skip to content

Commit 1ea1854

Browse files
authored
[feat] add NetworkRequestDetailBottomSheet (#76)
* add NetworkRequestDetailBottomSheet to show captured network request details
1 parent bf97dc7 commit 1ea1854

File tree

16 files changed

+1198
-144
lines changed

16 files changed

+1198
-144
lines changed

debugoverlay-core/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ dependencies {
6666

6767
debugImplementation(libs.androidx.compose.ui.tooling)
6868

69+
// Json
70+
implementation(libs.kotlinx.serialization.json)
71+
6972
// Lifecycle for synthetic lifecycle owner
7073
implementation(libs.androidx.lifecycle.runtime)
7174
implementation(libs.androidx.lifecycle.runtime.ktx)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.ms.square.debugoverlay.internal.data
2+
3+
internal enum class TextType {
4+
JSON,
5+
HTML,
6+
XML,
7+
PLAIN,
8+
;
9+
10+
companion object Companion {
11+
12+
/**
13+
* Detect text type from http content-type header and content.
14+
*/
15+
fun from(body: String, contentType: String?): TextType {
16+
// Check content-type header first
17+
contentType?.lowercase()?.let { ct ->
18+
when {
19+
ct.contains("json") -> return JSON
20+
ct.contains("html") -> return HTML
21+
ct.contains("xml") -> return XML
22+
}
23+
}
24+
// Fallback: Detect from content
25+
val trimmed = body.trimStart()
26+
return when {
27+
trimmed.startsWith("{") || trimmed.startsWith("[") -> JSON
28+
trimmed.startsWith(
29+
"<!DOCTYPE html",
30+
ignoreCase = true
31+
) || trimmed.startsWith("<html", ignoreCase = true) -> HTML
32+
trimmed.startsWith("<?xml") || trimmed.startsWith("<") -> XML
33+
else -> PLAIN
34+
}
35+
}
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.ms.square.debugoverlay.internal.data
2+
3+
import androidx.core.net.toUri
4+
5+
internal data class UrlParts(val scheme: String, val domain: String, val path: String) {
6+
companion object {
7+
fun from(url: String): UrlParts {
8+
val uri = url.toUri()
9+
return UrlParts(
10+
scheme = uri.scheme ?: "",
11+
domain = uri.host ?: "",
12+
path = uri.path ?: "/"
13+
)
14+
}
15+
}
16+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.ms.square.debugoverlay.internal.ui
2+
3+
import androidx.compose.foundation.layout.padding
4+
import androidx.compose.foundation.shape.RoundedCornerShape
5+
import androidx.compose.material3.MaterialTheme
6+
import androidx.compose.material3.Surface
7+
import androidx.compose.material3.Text
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.text.font.FontFamily
12+
import androidx.compose.ui.text.font.FontWeight
13+
import androidx.compose.ui.unit.dp
14+
import androidx.compose.ui.unit.sp
15+
import com.ms.square.debugoverlay.internal.util.httpMethodColor
16+
import com.ms.square.debugoverlay.internal.util.httpStatusColor
17+
18+
/**
19+
* HTTP method badge (GET, POST, etc.)
20+
*/
21+
@Composable
22+
internal fun MethodBadge(method: String, modifier: Modifier = Modifier) {
23+
Surface(
24+
modifier = modifier,
25+
color = method.httpMethodColor,
26+
shape = RoundedCornerShape(4.dp)
27+
) {
28+
Text(
29+
text = method,
30+
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
31+
style = MaterialTheme.typography.labelSmall,
32+
color = Color.Black,
33+
fontWeight = FontWeight.Bold,
34+
fontSize = 10.sp
35+
)
36+
}
37+
}
38+
39+
/**
40+
* Status code badge with color coding.
41+
*/
42+
@Composable
43+
internal fun StatusCodeBadge(statusCode: Int?, modifier: Modifier = Modifier) {
44+
Text(
45+
text = statusCode?.toString() ?: "ERR",
46+
modifier = modifier,
47+
style = MaterialTheme.typography.titleMedium,
48+
color = statusCode.httpStatusColor,
49+
fontWeight = FontWeight.Bold,
50+
fontFamily = FontFamily.Monospace
51+
)
52+
}

debugoverlay-core/src/main/kotlin/com/ms/square/debugoverlay/internal/ui/LogEntryDetailBottomSheet.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.ms.square.debugoverlay.internal.ui
22

3-
import android.content.ClipData
43
import androidx.compose.foundation.background
54
import androidx.compose.foundation.layout.Arrangement
65
import androidx.compose.foundation.layout.Box
@@ -28,7 +27,6 @@ import androidx.compose.runtime.Composable
2827
import androidx.compose.runtime.rememberCoroutineScope
2928
import androidx.compose.ui.Alignment
3029
import androidx.compose.ui.Modifier
31-
import androidx.compose.ui.platform.ClipEntry
3230
import androidx.compose.ui.platform.LocalClipboard
3331
import androidx.compose.ui.platform.LocalContext
3432
import androidx.compose.ui.res.stringResource
@@ -39,7 +37,7 @@ import androidx.compose.ui.unit.sp
3937
import com.ms.square.debugoverlay.core.R
4038
import com.ms.square.debugoverlay.internal.data.model.LogcatEntry
4139
import com.ms.square.debugoverlay.internal.data.model.toColor
42-
import kotlinx.coroutines.launch
40+
import com.ms.square.debugoverlay.internal.util.copyToClipboard
4341

4442
private const val BOTTOM_SHEET_HEIGHT_FRACTION = 0.8f
4543
private const val TIMESTAMP_DISPLAY_LENGTH = 12 // HH:MM:SS.mmm
@@ -236,11 +234,8 @@ private fun DetailActionButtons(logEntry: LogcatEntry, onFilterTag: (String) ->
236234
// Copy button
237235
Button(
238236
onClick = {
239-
scope.launch {
240-
val clipboardLabel = context.getString(R.string.debugoverlay_clipboard_label)
241-
val clipEntry = ClipEntry(ClipData.newPlainText(clipboardLabel, logEntry.rawLine))
242-
clipboard.setClipEntry(clipEntry)
243-
}
237+
val clipboardLabel = context.getString(R.string.debugoverlay_clipboard_label_logcat)
238+
scope.copyToClipboard(clipboard, logEntry.rawLine, clipboardLabel)
244239
},
245240
modifier = Modifier.weight(1f)
246241
) {

0 commit comments

Comments
 (0)