@@ -83,25 +83,31 @@ private enum class AppTab(val label: String, val icon: ImageVector) {
8383
8484private data class UiSettings (
8585 val webhookUrl : String ,
86+ val webhookMethod : String ,
8687 val forwardingEnabled : Boolean ,
8788 val filterMode : FilterMode ,
8889 val filterPackagesRaw : String ,
8990 val authMode : AuthMode ,
9091 val bearerToken : String ,
9192 val customHeadersRaw : String ,
93+ val queryParamsRaw : String ,
94+ val payloadTemplateRaw : String ,
9295 val maxRetriesRaw : String ,
9396 val batchSizeRaw : String
9497)
9598
9699private fun AppSettings.toUiSettings (): UiSettings {
97100 return UiSettings (
98101 webhookUrl = webhookUrl,
102+ webhookMethod = webhookMethod,
99103 forwardingEnabled = forwardingEnabled,
100104 filterMode = filterMode,
101105 filterPackagesRaw = filterPackages.joinToString(" \n " ),
102106 authMode = authMode,
103107 bearerToken = bearerToken,
104108 customHeadersRaw = customHeadersRaw,
109+ queryParamsRaw = queryParamsRaw,
110+ payloadTemplateRaw = payloadTemplateRaw,
105111 maxRetriesRaw = maxRetries.toString(),
106112 batchSizeRaw = batchSize.toString()
107113 )
@@ -179,12 +185,14 @@ private fun MainScreen(settingsStore: SettingsStore) {
179185 val result = withContext(Dispatchers .IO ) {
180186 WebhookClient ().send(
181187 url = uiSettings.webhookUrl,
182- method = " POST " ,
188+ method = uiSettings.webhookMethod ,
183189 headers = buildHeadersPreview(
184190 authMode = uiSettings.authMode,
185191 token = uiSettings.bearerToken,
186192 customHeadersRaw = uiSettings.customHeadersRaw
187193 ),
194+ queryParams = parseKeyValuePairs(uiSettings.queryParamsRaw),
195+ payloadTemplate = uiSettings.payloadTemplateRaw,
188196 item = QueueItem (
189197 packageName = " com.test.package" ,
190198 appName = " Webhook Test" ,
@@ -383,6 +391,15 @@ private fun WebhookScreen(
383391 singleLine = true
384392 )
385393
394+ DropdownSelector (
395+ label = " HTTP method" ,
396+ value = uiSettings.webhookMethod,
397+ options = listOf (" GET" , " POST" , " PUT" , " PATCH" ),
398+ onSelected = {
399+ onSettingsChange(uiSettings.copy(webhookMethod = it))
400+ }
401+ )
402+
386403 DropdownSelector (
387404 label = " Auth mode" ,
388405 value = uiSettings.authMode.name,
@@ -410,6 +427,24 @@ private fun WebhookScreen(
410427 label = { Text (" Custom headers (Key: Value per line)" ) }
411428 )
412429
430+ OutlinedTextField (
431+ modifier = Modifier
432+ .fillMaxWidth()
433+ .height(140 .dp),
434+ value = uiSettings.queryParamsRaw,
435+ onValueChange = { onSettingsChange(uiSettings.copy(queryParamsRaw = it)) },
436+ label = { Text (" Query params (key=value per line)" ) }
437+ )
438+
439+ OutlinedTextField (
440+ modifier = Modifier
441+ .fillMaxWidth()
442+ .height(200 .dp),
443+ value = uiSettings.payloadTemplateRaw,
444+ onValueChange = { onSettingsChange(uiSettings.copy(payloadTemplateRaw = it)) },
445+ label = { Text (" Payload template (JSON with {title} {text} etc.)" ) }
446+ )
447+
413448 Button (modifier = Modifier .fillMaxWidth(), onClick = onSave) {
414449 Text (" Save Webhook Settings" )
415450 }
@@ -639,12 +674,15 @@ private fun QueueStatCard(
639674
640675private fun saveSettings (settingsStore : SettingsStore , uiSettings : UiSettings ) {
641676 settingsStore.webhookUrl = uiSettings.webhookUrl
677+ settingsStore.webhookMethod = uiSettings.webhookMethod
642678 settingsStore.forwardingEnabled = uiSettings.forwardingEnabled
643679 settingsStore.filterMode = uiSettings.filterMode
644680 settingsStore.filterPackages = SettingsStore .parsePackages(uiSettings.filterPackagesRaw)
645681 settingsStore.authMode = uiSettings.authMode
646682 settingsStore.bearerToken = uiSettings.bearerToken
647683 settingsStore.customHeadersRaw = uiSettings.customHeadersRaw
684+ settingsStore.queryParamsRaw = uiSettings.queryParamsRaw
685+ settingsStore.payloadTemplateRaw = uiSettings.payloadTemplateRaw
648686 settingsStore.maxRetries = uiSettings.maxRetriesRaw.toIntOrNull() ? : 10
649687 settingsStore.batchSize = uiSettings.batchSizeRaw.toIntOrNull() ? : 20
650688}
@@ -658,6 +696,21 @@ private fun isNotificationListenerEnabled(context: Context): Boolean {
658696 return enabled.contains(target.flattenToString())
659697}
660698
699+ private fun parseKeyValuePairs (raw : String ): Map <String , String > {
700+ val map = linkedMapOf<String , String >()
701+ raw.lines().forEach { line ->
702+ val trimmed = line.trim()
703+ if (trimmed.isEmpty()) return @forEach
704+ val idx = trimmed.indexOf(' =' )
705+ if (idx > 0 ) {
706+ val key = trimmed.substring(0 , idx).trim()
707+ val value = trimmed.substring(idx + 1 ).trim()
708+ if (key.isNotEmpty()) map[key] = value
709+ }
710+ }
711+ return map
712+ }
713+
661714private fun buildHeadersPreview (
662715 authMode : AuthMode ,
663716 token : String ,
0 commit comments