implement widget text template
This commit is contained in:
@@ -43,6 +43,8 @@ class FlowWidgetProvider : AppWidgetProvider() {
|
|||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
val manager = AppWidgetManager.getInstance(appContext)
|
val manager = AppWidgetManager.getInstance(appContext)
|
||||||
val ids = widgetIds(appContext, manager, intent)
|
val ids = widgetIds(appContext, manager, intent)
|
||||||
|
val widgetConfig = loadWidgetConfig(appContext)
|
||||||
|
val widgetVariables = loadEnvironmentVariables(appContext).toEnvironmentMap()
|
||||||
|
|
||||||
ids.forEach { widgetId ->
|
ids.forEach { widgetId ->
|
||||||
manager.updateAppWidget(
|
manager.updateAppWidget(
|
||||||
@@ -51,6 +53,12 @@ class FlowWidgetProvider : AppWidgetProvider() {
|
|||||||
context = appContext,
|
context = appContext,
|
||||||
widgetId = widgetId,
|
widgetId = widgetId,
|
||||||
state = WidgetState(
|
state = WidgetState(
|
||||||
|
title = renderWidgetTemplate(
|
||||||
|
template = widgetConfig.titleTemplate,
|
||||||
|
values = widgetVariables,
|
||||||
|
fallback = WidgetDisplayConfig().titleTemplate,
|
||||||
|
maxLength = 48
|
||||||
|
),
|
||||||
subtitle = "Running saved flow",
|
subtitle = "Running saved flow",
|
||||||
status = "Running",
|
status = "Running",
|
||||||
meta = "Starting",
|
meta = "Starting",
|
||||||
@@ -99,7 +107,7 @@ class FlowWidgetProvider : AppWidgetProvider() {
|
|||||||
private const val ENVIRONMENT_KEY = "environment_variables"
|
private const val ENVIRONMENT_KEY = "environment_variables"
|
||||||
private const val FLOW_KEY = "request_flow"
|
private const val FLOW_KEY = "request_flow"
|
||||||
private const val WIDGET_STATE_KEY = "widget_state"
|
private const val WIDGET_STATE_KEY = "widget_state"
|
||||||
private const val COLOR_PRIMARY = 0xFF006B5B.toInt()
|
private const val WIDGET_CONFIG_KEY = "widget_config"
|
||||||
private const val COLOR_AMBER = 0xFFC47A12.toInt()
|
private const val COLOR_AMBER = 0xFFC47A12.toInt()
|
||||||
private const val COLOR_GREEN = 0xFF147A56.toInt()
|
private const val COLOR_GREEN = 0xFF147A56.toInt()
|
||||||
private const val COLOR_RED = 0xFFC7383D.toInt()
|
private const val COLOR_RED = 0xFFC7383D.toInt()
|
||||||
@@ -118,6 +126,14 @@ private data class WidgetState(
|
|||||||
val statusColor: Int = 0xFF006B5B.toInt()
|
val statusColor: Int = 0xFF006B5B.toInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class WidgetDisplayConfig(
|
||||||
|
val titleTemplate: String = "PostHUB Flow",
|
||||||
|
val subtitleTemplate: String = "{{flow_steps}} step flow",
|
||||||
|
val statusTemplate: String = "{{status_code}}",
|
||||||
|
val metaTemplate: String = "{{duration_ms}} ms {{bytes}}",
|
||||||
|
val bodyTemplate: String = "{{summary}}"
|
||||||
|
)
|
||||||
|
|
||||||
private data class WidgetRequestField(
|
private data class WidgetRequestField(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val key: String,
|
val key: String,
|
||||||
@@ -236,12 +252,27 @@ private fun runSavedFlow(
|
|||||||
onProgress: (WidgetState) -> Unit
|
onProgress: (WidgetState) -> Unit
|
||||||
): WidgetState {
|
): WidgetState {
|
||||||
val steps = loadFlowSteps(context).ifEmpty { defaultWidgetFlow() }
|
val steps = loadFlowSteps(context).ifEmpty { defaultWidgetFlow() }
|
||||||
|
val widgetConfig = loadWidgetConfig(context)
|
||||||
var environmentRows = loadEnvironmentVariables(context)
|
var environmentRows = loadEnvironmentVariables(context)
|
||||||
var lastResult: WidgetRequestResult? = null
|
var lastResult: WidgetRequestResult? = null
|
||||||
|
|
||||||
steps.forEachIndexed { index, step ->
|
steps.forEachIndexed { index, step ->
|
||||||
|
val progressValues = environmentRows.toEnvironmentMap() + mapOf(
|
||||||
|
"flow_steps" to steps.size.toString(),
|
||||||
|
"step_index" to (index + 1).toString(),
|
||||||
|
"step_name" to step.request.name,
|
||||||
|
"status" to "Running",
|
||||||
|
"status_code" to "Running",
|
||||||
|
"summary" to step.request.url
|
||||||
|
)
|
||||||
onProgress(
|
onProgress(
|
||||||
WidgetState(
|
WidgetState(
|
||||||
|
title = renderWidgetTemplate(
|
||||||
|
template = widgetConfig.titleTemplate,
|
||||||
|
values = progressValues,
|
||||||
|
fallback = WidgetDisplayConfig().titleTemplate,
|
||||||
|
maxLength = 48
|
||||||
|
),
|
||||||
subtitle = "Step ${index + 1} of ${steps.size}",
|
subtitle = "Step ${index + 1} of ${steps.size}",
|
||||||
status = "Running",
|
status = "Running",
|
||||||
meta = step.request.name,
|
meta = step.request.name,
|
||||||
@@ -257,11 +288,27 @@ private fun runSavedFlow(
|
|||||||
lastResult = result
|
lastResult = result
|
||||||
|
|
||||||
if (result.error != null) {
|
if (result.error != null) {
|
||||||
|
val errorValues = environmentRows.toEnvironmentMap() + mapOf(
|
||||||
|
"flow_steps" to steps.size.toString(),
|
||||||
|
"step_index" to (index + 1).toString(),
|
||||||
|
"step_name" to step.request.name,
|
||||||
|
"status" to "Error",
|
||||||
|
"status_code" to "Error",
|
||||||
|
"status_text" to result.statusText,
|
||||||
|
"duration_ms" to result.durationMs.toString(),
|
||||||
|
"bytes" to formatBytes(result.byteCount),
|
||||||
|
"byte_count" to result.byteCount.toString(),
|
||||||
|
"final_url" to result.finalUrl,
|
||||||
|
"summary" to result.error,
|
||||||
|
"body" to result.error,
|
||||||
|
"raw_body" to result.rawBody
|
||||||
|
)
|
||||||
return WidgetState(
|
return WidgetState(
|
||||||
subtitle = "Failed at ${step.request.name}",
|
title = renderWidgetTemplate(widgetConfig.titleTemplate, errorValues, WidgetDisplayConfig().titleTemplate, 48),
|
||||||
status = "Error",
|
subtitle = renderWidgetTemplate(widgetConfig.subtitleTemplate, errorValues, "Failed at ${step.request.name}", 80),
|
||||||
meta = result.statusText,
|
status = renderWidgetTemplate(widgetConfig.statusTemplate, errorValues, "Error", 18),
|
||||||
body = result.error,
|
meta = renderWidgetTemplate(widgetConfig.metaTemplate, errorValues, result.statusText, 80),
|
||||||
|
body = renderWidgetTemplate(widgetConfig.bodyTemplate, errorValues, result.error, 420),
|
||||||
statusColor = 0xFFC7383D.toInt()
|
statusColor = 0xFFC7383D.toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -274,6 +321,17 @@ private fun runSavedFlow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val result = lastResult ?: return WidgetState(
|
val result = lastResult ?: return WidgetState(
|
||||||
|
title = renderWidgetTemplate(
|
||||||
|
template = widgetConfig.titleTemplate,
|
||||||
|
values = mapOf(
|
||||||
|
"flow_steps" to "0",
|
||||||
|
"status" to "Empty",
|
||||||
|
"status_code" to "Empty",
|
||||||
|
"summary" to "Create or add requests to a flow, then refresh this widget."
|
||||||
|
),
|
||||||
|
fallback = WidgetDisplayConfig().titleTemplate,
|
||||||
|
maxLength = 48
|
||||||
|
),
|
||||||
subtitle = "No flow steps",
|
subtitle = "No flow steps",
|
||||||
status = "Empty",
|
status = "Empty",
|
||||||
meta = "Open PostHUB",
|
meta = "Open PostHUB",
|
||||||
@@ -281,11 +339,13 @@ private fun runSavedFlow(
|
|||||||
statusColor = 0xFF66736D.toInt()
|
statusColor = 0xFF66736D.toInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val finalValues = widgetTemplateValues(result, environmentRows, steps.size)
|
||||||
return WidgetState(
|
return WidgetState(
|
||||||
subtitle = "${steps.size} step flow",
|
title = renderWidgetTemplate(widgetConfig.titleTemplate, finalValues, WidgetDisplayConfig().titleTemplate, 48),
|
||||||
status = result.statusCode?.toString() ?: "Done",
|
subtitle = renderWidgetTemplate(widgetConfig.subtitleTemplate, finalValues, "${steps.size} step flow", 80),
|
||||||
meta = "${result.durationMs} ms ${formatBytes(result.byteCount)}",
|
status = renderWidgetTemplate(widgetConfig.statusTemplate, finalValues, result.statusCode?.toString() ?: "Done", 18),
|
||||||
body = summarizeWidgetResponse(result),
|
meta = renderWidgetTemplate(widgetConfig.metaTemplate, finalValues, "${result.durationMs} ms ${formatBytes(result.byteCount)}", 80),
|
||||||
|
body = renderWidgetTemplate(widgetConfig.bodyTemplate, finalValues, summarizeWidgetResponse(result), 420),
|
||||||
statusColor = statusColor(result.statusCode)
|
statusColor = statusColor(result.statusCode)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -422,7 +482,7 @@ private fun loadLastWidgetState(context: Context): WidgetState {
|
|||||||
.getSharedPreferences("posthub_state", Context.MODE_PRIVATE)
|
.getSharedPreferences("posthub_state", Context.MODE_PRIVATE)
|
||||||
.getString("widget_state", null)
|
.getString("widget_state", null)
|
||||||
|
|
||||||
if (raw.isNullOrBlank()) return WidgetState()
|
if (raw.isNullOrBlank()) return defaultRenderedWidgetState(context)
|
||||||
|
|
||||||
return runCatching {
|
return runCatching {
|
||||||
val json = JSONObject(raw)
|
val json = JSONObject(raw)
|
||||||
@@ -437,6 +497,91 @@ private fun loadLastWidgetState(context: Context): WidgetState {
|
|||||||
}.getOrDefault(WidgetState())
|
}.getOrDefault(WidgetState())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun defaultRenderedWidgetState(context: Context): WidgetState {
|
||||||
|
val config = loadWidgetConfig(context)
|
||||||
|
val values = loadEnvironmentVariables(context).toEnvironmentMap() + mapOf(
|
||||||
|
"flow_steps" to "0",
|
||||||
|
"status" to "Ready",
|
||||||
|
"status_code" to "Ready",
|
||||||
|
"status_text" to "Ready",
|
||||||
|
"duration_ms" to "",
|
||||||
|
"bytes" to "",
|
||||||
|
"byte_count" to "",
|
||||||
|
"final_url" to "",
|
||||||
|
"summary" to "Run your saved flow from the home screen.",
|
||||||
|
"body" to "Run your saved flow from the home screen.",
|
||||||
|
"raw_body" to ""
|
||||||
|
)
|
||||||
|
|
||||||
|
return WidgetState(
|
||||||
|
title = renderWidgetTemplate(config.titleTemplate, values, WidgetDisplayConfig().titleTemplate, 48),
|
||||||
|
subtitle = renderWidgetTemplate(config.subtitleTemplate, values, "Home runner", 80),
|
||||||
|
status = renderWidgetTemplate(config.statusTemplate, values, "Ready", 18),
|
||||||
|
meta = renderWidgetTemplate(config.metaTemplate, values, "Tap refresh", 80),
|
||||||
|
body = renderWidgetTemplate(config.bodyTemplate, values, "Run your saved flow from the home screen.", 420),
|
||||||
|
statusColor = 0xFF006B5B.toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadWidgetConfig(context: Context): WidgetDisplayConfig {
|
||||||
|
val raw = context
|
||||||
|
.getSharedPreferences("posthub_state", Context.MODE_PRIVATE)
|
||||||
|
.getString("widget_config", null)
|
||||||
|
|
||||||
|
if (raw.isNullOrBlank()) return WidgetDisplayConfig()
|
||||||
|
|
||||||
|
return runCatching {
|
||||||
|
val json = JSONObject(raw)
|
||||||
|
WidgetDisplayConfig(
|
||||||
|
titleTemplate = json.optString("titleTemplate", WidgetDisplayConfig().titleTemplate),
|
||||||
|
subtitleTemplate = json.optString("subtitleTemplate", WidgetDisplayConfig().subtitleTemplate),
|
||||||
|
statusTemplate = json.optString("statusTemplate", WidgetDisplayConfig().statusTemplate),
|
||||||
|
metaTemplate = json.optString("metaTemplate", WidgetDisplayConfig().metaTemplate),
|
||||||
|
bodyTemplate = json.optString("bodyTemplate", WidgetDisplayConfig().bodyTemplate)
|
||||||
|
)
|
||||||
|
}.getOrDefault(WidgetDisplayConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun widgetTemplateValues(
|
||||||
|
result: WidgetRequestResult,
|
||||||
|
environmentRows: List<WidgetRequestField>,
|
||||||
|
flowSteps: Int
|
||||||
|
): Map<String, String> {
|
||||||
|
val statusValue = result.statusCode?.toString() ?: "Done"
|
||||||
|
return environmentRows.toEnvironmentMap() + mapOf(
|
||||||
|
"flow_steps" to flowSteps.toString(),
|
||||||
|
"status" to statusValue,
|
||||||
|
"status_code" to statusValue,
|
||||||
|
"status_text" to result.statusText,
|
||||||
|
"duration_ms" to result.durationMs.toString(),
|
||||||
|
"bytes" to formatBytes(result.byteCount),
|
||||||
|
"byte_count" to result.byteCount.toString(),
|
||||||
|
"final_url" to result.finalUrl,
|
||||||
|
"summary" to summarizeWidgetResponse(result),
|
||||||
|
"body" to result.body,
|
||||||
|
"raw_body" to result.rawBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderWidgetTemplate(
|
||||||
|
template: String,
|
||||||
|
values: Map<String, String>,
|
||||||
|
fallback: String,
|
||||||
|
maxLength: Int
|
||||||
|
): String {
|
||||||
|
val source = template.ifBlank { fallback }
|
||||||
|
val rendered = source.applyVariables(values).trim().ifBlank {
|
||||||
|
fallback.applyVariables(values).trim()
|
||||||
|
}
|
||||||
|
return rendered.limitWidgetText(maxLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.limitWidgetText(maxLength: Int): String {
|
||||||
|
if (length <= maxLength) return this
|
||||||
|
if (maxLength <= 3) return take(maxLength)
|
||||||
|
return take(maxLength - 3).trimEnd() + "..."
|
||||||
|
}
|
||||||
|
|
||||||
private fun saveLastWidgetState(
|
private fun saveLastWidgetState(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: WidgetState
|
state: WidgetState
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ private val VariablePattern = Regex("""\{\{\s*([A-Za-z0-9_.-]+)\s*\}\}""")
|
|||||||
private const val PrefName = "posthub_state"
|
private const val PrefName = "posthub_state"
|
||||||
private const val EnvironmentKey = "environment_variables"
|
private const val EnvironmentKey = "environment_variables"
|
||||||
private const val FlowKey = "request_flow"
|
private const val FlowKey = "request_flow"
|
||||||
|
private const val WidgetConfigKey = "widget_config"
|
||||||
|
|
||||||
private object AppColors {
|
private object AppColors {
|
||||||
private val darkMode = mutableStateOf(false)
|
private val darkMode = mutableStateOf(false)
|
||||||
@@ -230,6 +231,14 @@ private data class FlowRunEntry(
|
|||||||
val savedVariables: Int = 0
|
val savedVariables: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class WidgetConfig(
|
||||||
|
val titleTemplate: String = "PostHUB Flow",
|
||||||
|
val subtitleTemplate: String = "{{flow_steps}} step flow",
|
||||||
|
val statusTemplate: String = "{{status_code}}",
|
||||||
|
val metaTemplate: String = "{{duration_ms}} ms {{bytes}}",
|
||||||
|
val bodyTemplate: String = "{{summary}}"
|
||||||
|
)
|
||||||
|
|
||||||
private fun appSections() = listOf(
|
private fun appSections() = listOf(
|
||||||
AppSection("request", "Request", "Composer", AppColors.Primary),
|
AppSection("request", "Request", "Composer", AppColors.Primary),
|
||||||
AppSection("clients", "Clients", "MCI TCI", AppColors.Coral),
|
AppSection("clients", "Clients", "MCI TCI", AppColors.Coral),
|
||||||
@@ -285,6 +294,7 @@ private fun ApiClientApp() {
|
|||||||
loadFlowSteps(appContext).ifEmpty { defaultFlowSteps(builtInRequests) }
|
loadFlowSteps(appContext).ifEmpty { defaultFlowSteps(builtInRequests) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
var widgetConfig by remember { mutableStateOf(loadWidgetConfig(appContext)) }
|
||||||
var flowRun by remember { mutableStateOf<List<FlowRunEntry>>(emptyList()) }
|
var flowRun by remember { mutableStateOf<List<FlowRunEntry>>(emptyList()) }
|
||||||
var response by remember { mutableStateOf<RequestResult?>(null) }
|
var response by remember { mutableStateOf<RequestResult?>(null) }
|
||||||
var history by remember { mutableStateOf<List<HistoryItem>>(emptyList()) }
|
var history by remember { mutableStateOf<List<HistoryItem>>(emptyList()) }
|
||||||
@@ -302,6 +312,11 @@ private fun ApiClientApp() {
|
|||||||
saveFlowSteps(appContext, next)
|
saveFlowSteps(appContext, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateWidgetConfig(next: WidgetConfig) {
|
||||||
|
widgetConfig = next
|
||||||
|
saveWidgetConfig(appContext, next)
|
||||||
|
}
|
||||||
|
|
||||||
fun currentRequest(): SavedRequest {
|
fun currentRequest(): SavedRequest {
|
||||||
return SavedRequest(
|
return SavedRequest(
|
||||||
name = requestName(method, url),
|
name = requestName(method, url),
|
||||||
@@ -540,8 +555,10 @@ private fun ApiClientApp() {
|
|||||||
steps = flowSteps,
|
steps = flowSteps,
|
||||||
runEntries = flowRun,
|
runEntries = flowRun,
|
||||||
isRunning = isFlowRunning,
|
isRunning = isFlowRunning,
|
||||||
|
widgetConfig = widgetConfig,
|
||||||
onAddCurrent = ::addCurrentRequestToFlow,
|
onAddCurrent = ::addCurrentRequestToFlow,
|
||||||
onRun = ::runFlow,
|
onRun = ::runFlow,
|
||||||
|
onWidgetConfigChange = ::updateWidgetConfig,
|
||||||
onLoad = { loadSavedRequest(it.request) },
|
onLoad = { loadSavedRequest(it.request) },
|
||||||
onRemove = { step ->
|
onRemove = { step ->
|
||||||
updateFlowSteps(flowSteps.filterNot { it.id == step.id })
|
updateFlowSteps(flowSteps.filterNot { it.id == step.id })
|
||||||
@@ -1164,8 +1181,10 @@ private fun FlowPanel(
|
|||||||
steps: List<FlowStep>,
|
steps: List<FlowStep>,
|
||||||
runEntries: List<FlowRunEntry>,
|
runEntries: List<FlowRunEntry>,
|
||||||
isRunning: Boolean,
|
isRunning: Boolean,
|
||||||
|
widgetConfig: WidgetConfig,
|
||||||
onAddCurrent: () -> Unit,
|
onAddCurrent: () -> Unit,
|
||||||
onRun: () -> Unit,
|
onRun: () -> Unit,
|
||||||
|
onWidgetConfigChange: (WidgetConfig) -> Unit,
|
||||||
onLoad: (FlowStep) -> Unit,
|
onLoad: (FlowStep) -> Unit,
|
||||||
onRemove: (FlowStep) -> Unit
|
onRemove: (FlowStep) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -1234,10 +1253,114 @@ private fun FlowPanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WidgetTextEditor(
|
||||||
|
config = widgetConfig,
|
||||||
|
onConfigChange = onWidgetConfigChange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WidgetTextEditor(
|
||||||
|
config: WidgetConfig,
|
||||||
|
onConfigChange: (WidgetConfig) -> Unit
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
color = AppColors.PanelSoft,
|
||||||
|
border = BorderStroke(1.dp, AppColors.Border)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
SectionTitle(title = "Widget text", meta = "Templates")
|
||||||
|
TextButton(onClick = { onConfigChange(WidgetConfig()) }) {
|
||||||
|
Text("Reset", fontWeight = FontWeight.Bold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
StatusPill("{{summary}}", AppColors.Primary)
|
||||||
|
StatusPill("{{status_code}}", AppColors.Green)
|
||||||
|
StatusPill("{{duration_ms}}", AppColors.Amber)
|
||||||
|
StatusPill("{{public_ip}}", AppColors.Coral)
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetTemplateField(
|
||||||
|
label = "Title",
|
||||||
|
value = config.titleTemplate,
|
||||||
|
placeholder = "PostHUB Flow",
|
||||||
|
onValueChange = { onConfigChange(config.copy(titleTemplate = it)) }
|
||||||
|
)
|
||||||
|
WidgetTemplateField(
|
||||||
|
label = "Subtitle",
|
||||||
|
value = config.subtitleTemplate,
|
||||||
|
placeholder = "{{flow_steps}} step flow",
|
||||||
|
onValueChange = { onConfigChange(config.copy(subtitleTemplate = it)) }
|
||||||
|
)
|
||||||
|
WidgetTemplateField(
|
||||||
|
label = "Status",
|
||||||
|
value = config.statusTemplate,
|
||||||
|
placeholder = "{{status_code}}",
|
||||||
|
onValueChange = { onConfigChange(config.copy(statusTemplate = it)) }
|
||||||
|
)
|
||||||
|
WidgetTemplateField(
|
||||||
|
label = "Meta",
|
||||||
|
value = config.metaTemplate,
|
||||||
|
placeholder = "{{duration_ms}} ms {{bytes}}",
|
||||||
|
onValueChange = { onConfigChange(config.copy(metaTemplate = it)) }
|
||||||
|
)
|
||||||
|
WidgetTemplateField(
|
||||||
|
label = "Body",
|
||||||
|
value = config.bodyTemplate,
|
||||||
|
placeholder = "{{summary}}",
|
||||||
|
minLines = 2,
|
||||||
|
onValueChange = { onConfigChange(config.copy(bodyTemplate = it)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WidgetTemplateField(
|
||||||
|
label: String,
|
||||||
|
value: String,
|
||||||
|
placeholder: String,
|
||||||
|
minLines: Int = 1,
|
||||||
|
onValueChange: (String) -> Unit
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
minLines = minLines,
|
||||||
|
maxLines = if (minLines > 1) 3 else 1,
|
||||||
|
singleLine = minLines == 1,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
textStyle = MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace),
|
||||||
|
label = { Text(label) },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = placeholder,
|
||||||
|
color = AppColors.Muted,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FlowStepRow(
|
private fun FlowStepRow(
|
||||||
index: Int,
|
index: Int,
|
||||||
@@ -2762,6 +2885,43 @@ private fun saveFlowSteps(
|
|||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadWidgetConfig(context: Context): WidgetConfig {
|
||||||
|
val raw = context
|
||||||
|
.getSharedPreferences(PrefName, Context.MODE_PRIVATE)
|
||||||
|
.getString(WidgetConfigKey, null)
|
||||||
|
|
||||||
|
if (raw.isNullOrBlank()) return WidgetConfig()
|
||||||
|
|
||||||
|
return runCatching {
|
||||||
|
val json = JSONObject(raw)
|
||||||
|
WidgetConfig(
|
||||||
|
titleTemplate = json.optString("titleTemplate", WidgetConfig().titleTemplate),
|
||||||
|
subtitleTemplate = json.optString("subtitleTemplate", WidgetConfig().subtitleTemplate),
|
||||||
|
statusTemplate = json.optString("statusTemplate", WidgetConfig().statusTemplate),
|
||||||
|
metaTemplate = json.optString("metaTemplate", WidgetConfig().metaTemplate),
|
||||||
|
bodyTemplate = json.optString("bodyTemplate", WidgetConfig().bodyTemplate)
|
||||||
|
)
|
||||||
|
}.getOrDefault(WidgetConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveWidgetConfig(
|
||||||
|
context: Context,
|
||||||
|
config: WidgetConfig
|
||||||
|
) {
|
||||||
|
val json = JSONObject()
|
||||||
|
.put("titleTemplate", config.titleTemplate)
|
||||||
|
.put("subtitleTemplate", config.subtitleTemplate)
|
||||||
|
.put("statusTemplate", config.statusTemplate)
|
||||||
|
.put("metaTemplate", config.metaTemplate)
|
||||||
|
.put("bodyTemplate", config.bodyTemplate)
|
||||||
|
|
||||||
|
context
|
||||||
|
.getSharedPreferences(PrefName, Context.MODE_PRIVATE)
|
||||||
|
.edit()
|
||||||
|
.putString(WidgetConfigKey, json.toString())
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
private fun requestFieldsToJson(rows: List<RequestField>): JSONArray {
|
private fun requestFieldsToJson(rows: List<RequestField>): JSONArray {
|
||||||
val array = JSONArray()
|
val array = JSONArray()
|
||||||
rows.forEach { row ->
|
rows.forEach { row ->
|
||||||
|
|||||||
Reference in New Issue
Block a user