feat(android): toggle debug canvas status
This commit is contained in:
190
apps/android/app/src/main/assets/CanvasScaffold/scaffold.html
Normal file
190
apps/android/app/src/main/assets/CanvasScaffold/scaffold.html
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
|
<title>Canvas</title>
|
||||||
|
<style>
|
||||||
|
:root { color-scheme: dark; }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
body::before, body::after { animation: none !important; }
|
||||||
|
}
|
||||||
|
html,body { height:100%; margin:0; }
|
||||||
|
body {
|
||||||
|
background:
|
||||||
|
radial-gradient(1200px 900px at 15% 20%, rgba(42, 113, 255, 0.18), rgba(0,0,0,0) 55%),
|
||||||
|
radial-gradient(900px 700px at 85% 30%, rgba(255, 0, 138, 0.14), rgba(0,0,0,0) 60%),
|
||||||
|
radial-gradient(1000px 900px at 60% 90%, rgba(0, 209, 255, 0.10), rgba(0,0,0,0) 60%),
|
||||||
|
#000;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content:"";
|
||||||
|
position: fixed;
|
||||||
|
inset: -20%;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(0deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 1px,
|
||||||
|
transparent 1px, transparent 48px),
|
||||||
|
repeating-linear-gradient(90deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 1px,
|
||||||
|
transparent 1px, transparent 48px);
|
||||||
|
transform: translate3d(0,0,0) rotate(-7deg);
|
||||||
|
will-change: transform, opacity;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
opacity: 0.45;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: clawdis-grid-drift 140s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
body::after {
|
||||||
|
content:"";
|
||||||
|
position: fixed;
|
||||||
|
inset: -35%;
|
||||||
|
background:
|
||||||
|
radial-gradient(900px 700px at 30% 30%, rgba(42,113,255,0.16), rgba(0,0,0,0) 60%),
|
||||||
|
radial-gradient(800px 650px at 70% 35%, rgba(255,0,138,0.12), rgba(0,0,0,0) 62%),
|
||||||
|
radial-gradient(900px 800px at 55% 75%, rgba(0,209,255,0.10), rgba(0,0,0,0) 62%);
|
||||||
|
filter: blur(28px);
|
||||||
|
opacity: 0.52;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transform: translate3d(0,0,0);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: clawdis-glow-drift 110s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
@supports (mix-blend-mode: screen) {
|
||||||
|
body::after { mix-blend-mode: screen; }
|
||||||
|
}
|
||||||
|
@supports not (mix-blend-mode: screen) {
|
||||||
|
body::after { opacity: 0.70; }
|
||||||
|
}
|
||||||
|
@keyframes clawdis-grid-drift {
|
||||||
|
0% { transform: translate3d(-12px, 8px, 0) rotate(-7deg); opacity: 0.40; }
|
||||||
|
50% { transform: translate3d( 10px,-7px, 0) rotate(-6.6deg); opacity: 0.56; }
|
||||||
|
100% { transform: translate3d(-8px, 6px, 0) rotate(-7.2deg); opacity: 0.42; }
|
||||||
|
}
|
||||||
|
@keyframes clawdis-glow-drift {
|
||||||
|
0% { transform: translate3d(-18px, 12px, 0) scale(1.02); opacity: 0.40; }
|
||||||
|
50% { transform: translate3d( 14px,-10px, 0) scale(1.05); opacity: 0.52; }
|
||||||
|
100% { transform: translate3d(-10px, 8px, 0) scale(1.03); opacity: 0.43; }
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display:block;
|
||||||
|
width:100vw;
|
||||||
|
height:100vh;
|
||||||
|
touch-action: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
#clawdis-status {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: none;
|
||||||
|
place-items: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
#clawdis-status .card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(18, 18, 22, 0.42);
|
||||||
|
border: 1px solid rgba(255,255,255,0.08);
|
||||||
|
box-shadow: 0 18px 60px rgba(0,0,0,0.55);
|
||||||
|
-webkit-backdrop-filter: blur(14px);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
#clawdis-status .title {
|
||||||
|
font: 600 20px -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
color: rgba(255,255,255,0.92);
|
||||||
|
text-shadow: 0 0 22px rgba(42, 113, 255, 0.35);
|
||||||
|
}
|
||||||
|
#clawdis-status .subtitle {
|
||||||
|
margin-top: 6px;
|
||||||
|
font: 500 12px -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
|
||||||
|
color: rgba(255,255,255,0.58);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="clawdis-canvas"></canvas>
|
||||||
|
<div id="clawdis-status">
|
||||||
|
<div class="card">
|
||||||
|
<div class="title" id="clawdis-status-title">Ready</div>
|
||||||
|
<div class="subtitle" id="clawdis-status-subtitle">Waiting for agent</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const canvas = document.getElementById('clawdis-canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const statusEl = document.getElementById('clawdis-status');
|
||||||
|
const titleEl = document.getElementById('clawdis-status-title');
|
||||||
|
const subtitleEl = document.getElementById('clawdis-status-subtitle');
|
||||||
|
const debugStatusEnabledByQuery = (() => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const raw = params.get('debugStatus') ?? params.get('debug');
|
||||||
|
if (!raw) return false;
|
||||||
|
const normalized = String(raw).trim().toLowerCase();
|
||||||
|
return normalized === '1' || normalized === 'true' || normalized === 'yes';
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
let debugStatusEnabled = debugStatusEnabledByQuery;
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
const w = Math.max(1, Math.floor(window.innerWidth * dpr));
|
||||||
|
const h = Math.max(1, Math.floor(window.innerHeight * dpr));
|
||||||
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', resize);
|
||||||
|
resize();
|
||||||
|
|
||||||
|
const setDebugStatusEnabled = (enabled) => {
|
||||||
|
debugStatusEnabled = !!enabled;
|
||||||
|
if (!statusEl) return;
|
||||||
|
if (!debugStatusEnabled) {
|
||||||
|
statusEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (statusEl && !debugStatusEnabled) {
|
||||||
|
statusEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
window.__clawdis = {
|
||||||
|
canvas,
|
||||||
|
ctx,
|
||||||
|
setDebugStatusEnabled,
|
||||||
|
setStatus: (title, subtitle) => {
|
||||||
|
if (!statusEl || !debugStatusEnabled) return;
|
||||||
|
if (!title && !subtitle) {
|
||||||
|
statusEl.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statusEl.style.display = 'grid';
|
||||||
|
if (titleEl && typeof title === 'string') titleEl.textContent = title;
|
||||||
|
if (subtitleEl && typeof subtitle === 'string') subtitleEl.textContent = subtitle;
|
||||||
|
if (!debugStatusEnabled) {
|
||||||
|
clearTimeout(window.__statusTimeout);
|
||||||
|
window.__statusTimeout = setTimeout(() => {
|
||||||
|
statusEl.style.display = 'none';
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
clearTimeout(window.__statusTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -38,6 +38,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
|
|||||||
val manualEnabled: StateFlow<Boolean> = runtime.manualEnabled
|
val manualEnabled: StateFlow<Boolean> = runtime.manualEnabled
|
||||||
val manualHost: StateFlow<String> = runtime.manualHost
|
val manualHost: StateFlow<String> = runtime.manualHost
|
||||||
val manualPort: StateFlow<Int> = runtime.manualPort
|
val manualPort: StateFlow<Int> = runtime.manualPort
|
||||||
|
val canvasDebugStatusEnabled: StateFlow<Boolean> = runtime.canvasDebugStatusEnabled
|
||||||
|
|
||||||
val chatSessionKey: StateFlow<String> = runtime.chatSessionKey
|
val chatSessionKey: StateFlow<String> = runtime.chatSessionKey
|
||||||
val chatSessionId: StateFlow<String?> = runtime.chatSessionId
|
val chatSessionId: StateFlow<String?> = runtime.chatSessionId
|
||||||
@@ -78,6 +79,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
|
|||||||
runtime.setManualPort(value)
|
runtime.setManualPort(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCanvasDebugStatusEnabled(value: Boolean) {
|
||||||
|
runtime.setCanvasDebugStatusEnabled(value)
|
||||||
|
}
|
||||||
|
|
||||||
fun setWakeWords(words: List<String>) {
|
fun setWakeWords(words: List<String>) {
|
||||||
runtime.setWakeWords(words)
|
runtime.setWakeWords(words)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ class NodeRuntime(context: Context) {
|
|||||||
val manualHost: StateFlow<String> = prefs.manualHost
|
val manualHost: StateFlow<String> = prefs.manualHost
|
||||||
val manualPort: StateFlow<Int> = prefs.manualPort
|
val manualPort: StateFlow<Int> = prefs.manualPort
|
||||||
val lastDiscoveredStableId: StateFlow<String> = prefs.lastDiscoveredStableId
|
val lastDiscoveredStableId: StateFlow<String> = prefs.lastDiscoveredStableId
|
||||||
|
val canvasDebugStatusEnabled: StateFlow<Boolean> = prefs.canvasDebugStatusEnabled
|
||||||
|
|
||||||
private var didAutoConnect = false
|
private var didAutoConnect = false
|
||||||
private var suppressWakeWordsSync = false
|
private var suppressWakeWordsSync = false
|
||||||
@@ -246,6 +247,22 @@ class NodeRuntime(context: Context) {
|
|||||||
connect(target)
|
connect(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
combine(
|
||||||
|
canvasDebugStatusEnabled,
|
||||||
|
statusText,
|
||||||
|
serverName,
|
||||||
|
remoteAddress,
|
||||||
|
) { debugEnabled, status, server, remote ->
|
||||||
|
Quad(debugEnabled, status, server, remote)
|
||||||
|
}.distinctUntilChanged()
|
||||||
|
.collect { (debugEnabled, status, server, remote) ->
|
||||||
|
canvas.setDebugStatusEnabled(debugEnabled)
|
||||||
|
if (!debugEnabled) return@collect
|
||||||
|
canvas.setDebugStatus(status, server ?: remote)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setForeground(value: Boolean) {
|
fun setForeground(value: Boolean) {
|
||||||
@@ -276,6 +293,10 @@ class NodeRuntime(context: Context) {
|
|||||||
prefs.setManualPort(value)
|
prefs.setManualPort(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCanvasDebugStatusEnabled(value: Boolean) {
|
||||||
|
prefs.setCanvasDebugStatusEnabled(value)
|
||||||
|
}
|
||||||
|
|
||||||
fun setWakeWords(words: List<String>) {
|
fun setWakeWords(words: List<String>) {
|
||||||
prefs.setWakeWords(words)
|
prefs.setWakeWords(words)
|
||||||
scheduleWakeWordsSyncIfNeeded()
|
scheduleWakeWordsSyncIfNeeded()
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ class SecurePrefs(context: Context) {
|
|||||||
MutableStateFlow(prefs.getString("bridge.lastDiscoveredStableId", "")!!)
|
MutableStateFlow(prefs.getString("bridge.lastDiscoveredStableId", "")!!)
|
||||||
val lastDiscoveredStableId: StateFlow<String> = _lastDiscoveredStableId
|
val lastDiscoveredStableId: StateFlow<String> = _lastDiscoveredStableId
|
||||||
|
|
||||||
|
private val _canvasDebugStatusEnabled =
|
||||||
|
MutableStateFlow(prefs.getBoolean("canvas.debugStatusEnabled", false))
|
||||||
|
val canvasDebugStatusEnabled: StateFlow<Boolean> = _canvasDebugStatusEnabled
|
||||||
|
|
||||||
private val _wakeWords = MutableStateFlow(loadWakeWords())
|
private val _wakeWords = MutableStateFlow(loadWakeWords())
|
||||||
val wakeWords: StateFlow<List<String>> = _wakeWords
|
val wakeWords: StateFlow<List<String>> = _wakeWords
|
||||||
|
|
||||||
@@ -107,6 +111,11 @@ class SecurePrefs(context: Context) {
|
|||||||
_manualPort.value = value
|
_manualPort.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCanvasDebugStatusEnabled(value: Boolean) {
|
||||||
|
prefs.edit { putBoolean("canvas.debugStatusEnabled", value) }
|
||||||
|
_canvasDebugStatusEnabled.value = value
|
||||||
|
}
|
||||||
|
|
||||||
fun loadBridgeToken(): String? {
|
fun loadBridgeToken(): String? {
|
||||||
val key = "bridge.token.${_instanceId.value}"
|
val key = "bridge.token.${_instanceId.value}"
|
||||||
return prefs.getString(key, null)
|
return prefs.getString(key, null)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
|
import org.json.JSONObject
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
@@ -25,6 +26,9 @@ class CanvasController {
|
|||||||
|
|
||||||
@Volatile private var webView: WebView? = null
|
@Volatile private var webView: WebView? = null
|
||||||
@Volatile private var url: String? = null
|
@Volatile private var url: String? = null
|
||||||
|
@Volatile private var debugStatusEnabled: Boolean = false
|
||||||
|
@Volatile private var debugStatusTitle: String? = null
|
||||||
|
@Volatile private var debugStatusSubtitle: String? = null
|
||||||
|
|
||||||
private val scaffoldAssetUrl = "file:///android_asset/CanvasScaffold/scaffold.html"
|
private val scaffoldAssetUrl = "file:///android_asset/CanvasScaffold/scaffold.html"
|
||||||
|
|
||||||
@@ -36,6 +40,7 @@ class CanvasController {
|
|||||||
fun attach(webView: WebView) {
|
fun attach(webView: WebView) {
|
||||||
this.webView = webView
|
this.webView = webView
|
||||||
reload()
|
reload()
|
||||||
|
applyDebugStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun navigate(url: String) {
|
fun navigate(url: String) {
|
||||||
@@ -48,6 +53,21 @@ class CanvasController {
|
|||||||
|
|
||||||
fun isDefaultCanvas(): Boolean = url == null
|
fun isDefaultCanvas(): Boolean = url == null
|
||||||
|
|
||||||
|
fun setDebugStatusEnabled(enabled: Boolean) {
|
||||||
|
debugStatusEnabled = enabled
|
||||||
|
applyDebugStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDebugStatus(title: String?, subtitle: String?) {
|
||||||
|
debugStatusTitle = title
|
||||||
|
debugStatusSubtitle = subtitle
|
||||||
|
applyDebugStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPageFinished() {
|
||||||
|
applyDebugStatus()
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun withWebViewOnMain(crossinline block: (WebView) -> Unit) {
|
private inline fun withWebViewOnMain(crossinline block: (WebView) -> Unit) {
|
||||||
val wv = webView ?: return
|
val wv = webView ?: return
|
||||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||||
@@ -68,6 +88,32 @@ class CanvasController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyDebugStatus() {
|
||||||
|
val enabled = debugStatusEnabled
|
||||||
|
val title = debugStatusTitle
|
||||||
|
val subtitle = debugStatusSubtitle
|
||||||
|
withWebViewOnMain { wv ->
|
||||||
|
val titleJs = title?.let { JSONObject.quote(it) } ?: "null"
|
||||||
|
val subtitleJs = subtitle?.let { JSONObject.quote(it) } ?: "null"
|
||||||
|
val js = """
|
||||||
|
(() => {
|
||||||
|
try {
|
||||||
|
const api = globalThis.__clawdis;
|
||||||
|
if (!api) return;
|
||||||
|
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||||
|
api.setDebugStatusEnabled(${if (enabled) "true" else "false"});
|
||||||
|
}
|
||||||
|
if (!${if (enabled) "true" else "false"}) return;
|
||||||
|
if (typeof api.setStatus === 'function') {
|
||||||
|
api.setStatus($titleJs, $subtitleJs);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
})();
|
||||||
|
""".trimIndent()
|
||||||
|
wv.evaluateJavascript(js, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun eval(javaScript: String): String =
|
suspend fun eval(javaScript: String): String =
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
val wv = webView ?: throw IllegalStateException("no webview")
|
val wv = webView ?: throw IllegalStateException("no webview")
|
||||||
|
|||||||
@@ -187,6 +187,10 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
|||||||
"onReceivedHttpError: ${errorResponse.statusCode} ${errorResponse.reasonPhrase} ${request.url}",
|
"onReceivedHttpError: ${errorResponse.statusCode} ${errorResponse.reasonPhrase} ${request.url}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPageFinished(view: WebView, url: String?) {
|
||||||
|
viewModel.canvas.onPageFinished()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setBackgroundColor(Color.BLACK)
|
setBackgroundColor(Color.BLACK)
|
||||||
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
|||||||
val manualEnabled by viewModel.manualEnabled.collectAsState()
|
val manualEnabled by viewModel.manualEnabled.collectAsState()
|
||||||
val manualHost by viewModel.manualHost.collectAsState()
|
val manualHost by viewModel.manualHost.collectAsState()
|
||||||
val manualPort by viewModel.manualPort.collectAsState()
|
val manualPort by viewModel.manualPort.collectAsState()
|
||||||
|
val canvasDebugStatusEnabled by viewModel.canvasDebugStatusEnabled.collectAsState()
|
||||||
val statusText by viewModel.statusText.collectAsState()
|
val statusText by viewModel.statusText.collectAsState()
|
||||||
val serverName by viewModel.serverName.collectAsState()
|
val serverName by viewModel.serverName.collectAsState()
|
||||||
val remoteAddress by viewModel.remoteAddress.collectAsState()
|
val remoteAddress by viewModel.remoteAddress.collectAsState()
|
||||||
@@ -394,6 +395,23 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item { HorizontalDivider() }
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
item { Text("Debug", style = MaterialTheme.typography.titleSmall) }
|
||||||
|
item {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = { Text("Debug Canvas Status") },
|
||||||
|
supportingContent = { Text("Show status text in the canvas when debug is enabled.") },
|
||||||
|
trailingContent = {
|
||||||
|
Switch(
|
||||||
|
checked = canvasDebugStatusEnabled,
|
||||||
|
onCheckedChange = viewModel::setCanvasDebugStatusEnabled,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
item { Spacer(modifier = Modifier.height(20.dp)) }
|
item { Spacer(modifier = Modifier.height(20.dp)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user