From 9a434a5299dad424dd7c6d42c9c71003fffc74c8 Mon Sep 17 00:00:00 2001 From: Daniel Alome Date: Wed, 2 Sep 2026 07:56:07 +0100 Subject: [PATCH 1/2] ADFA-4500 | Show snippet dialogs and toasts through PluginWindows Undocking Snippets into a floating window and tapping Add crashed the app, and saving a filled-in snippet crashed it again after the file had already been written. An undocked fragment runs against a window context created for TYPE_APPLICATION_OVERLAY, and the platform requires every window added through it to carry that type. MaterialAlertDialogBuilder(ctx).show() builds a TYPE_APPLICATION window and Toast.makeText a TYPE_TOAST one, so both threw IllegalArgumentException while floating. Docked, ctx is the activity, so the same code worked. Route both through PluginWindows: showDialog applies the type the context requires, showToast posts against the application context, which imposes no window type. Both are no-ops while docked, so a single call site is correct in either state. Applied to both dialogs (the editor and the delete confirmation) and all three toasts. min_ide_version moves to 26.36, the release that ships PluginWindows. --- snippets/src/main/AndroidManifest.xml | 2 +- .../snippets/ui/SnippetManagerFragment.kt | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/snippets/src/main/AndroidManifest.xml b/snippets/src/main/AndroidManifest.xml index 6e63b1d6..45e0f12b 100644 --- a/snippets/src/main/AndroidManifest.xml +++ b/snippets/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ + android:value="26.36" /> Date: Wed, 2 Sep 2026 23:44:40 +0100 Subject: [PATCH 2/2] ADFA-4500 | Move the snippet dialog strings to strings.xml Review feedback: user-facing text belongs in resources. Covers every user-facing literal in the two functions this PR touches, not only the three toasts flagged, so the file is not left half converted: the editor and delete dialog titles, their Save/Cancel/Delete buttons and the delete confirmation, alongside the validation and save toasts. The delete confirmation takes the prefix as a positional argument rather than interpolating it. The Log.e message is left as it is; it is not user-facing. --- .../snippets/ui/SnippetManagerFragment.kt | 24 +++++++++++-------- snippets/src/main/res/values/strings.xml | 13 ++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 snippets/src/main/res/values/strings.xml diff --git a/snippets/src/main/kotlin/com/codeonthego/snippets/ui/SnippetManagerFragment.kt b/snippets/src/main/kotlin/com/codeonthego/snippets/ui/SnippetManagerFragment.kt index 498e9298..6c3d7a50 100644 --- a/snippets/src/main/kotlin/com/codeonthego/snippets/ui/SnippetManagerFragment.kt +++ b/snippets/src/main/kotlin/com/codeonthego/snippets/ui/SnippetManagerFragment.kt @@ -144,9 +144,13 @@ class SnippetManagerFragment : Fragment() { } val editDialog = MaterialAlertDialogBuilder(ctx) - .setTitle(if (existing != null) "Edit Snippet" else "New Snippet") + .setTitle( + ctx.getString( + if (existing != null) R.string.snippet_editor_title_edit else R.string.snippet_editor_title_new + ) + ) .setView(dialogView) - .setPositiveButton("Save") { _, _ -> + .setPositiveButton(ctx.getString(R.string.snippet_action_save)) { _, _ -> val prefix = prefixInput.text?.toString()?.trim() ?: "" val desc = descInput.text?.toString()?.trim() ?: "" val lang = langSpinner.selectedItem?.toString() ?: "" @@ -154,7 +158,7 @@ class SnippetManagerFragment : Fragment() { val body = bodyInput.text?.toString()?.split("\n") ?: emptyList() if (prefix.isEmpty() || desc.isEmpty() || lang.isEmpty() || scope.isEmpty()) { - PluginWindows.showToast(ctx, "All fields are required") + PluginWindows.showToast(ctx, ctx.getString(R.string.snippet_msg_all_fields_required)) return@setPositiveButton } @@ -175,7 +179,7 @@ class SnippetManagerFragment : Fragment() { save() renderList() } - .setNegativeButton("Cancel", null) + .setNegativeButton(ctx.getString(R.string.snippet_action_cancel), null) .create() PluginWindows.showDialog(editDialog) applyDialogButtonColors(editDialog) @@ -186,14 +190,14 @@ class SnippetManagerFragment : Fragment() { val entry = snippets.getOrNull(index) ?: return val deleteDialog = MaterialAlertDialogBuilder(ctx) - .setTitle("Delete Snippet") - .setMessage("Delete \"${entry.prefix}\"?") - .setPositiveButton("Delete") { _, _ -> + .setTitle(ctx.getString(R.string.snippet_delete_title)) + .setMessage(ctx.getString(R.string.snippet_delete_confirm, entry.prefix)) + .setPositiveButton(ctx.getString(R.string.snippet_action_delete)) { _, _ -> snippets.removeAt(index) save() renderList() } - .setNegativeButton("Cancel", null) + .setNegativeButton(ctx.getString(R.string.snippet_action_cancel), null) .create() PluginWindows.showDialog(deleteDialog) applyDialogButtonColors(deleteDialog) @@ -213,10 +217,10 @@ class SnippetManagerFragment : Fragment() { SnippetsConfigParser.write(file, SnippetsConfig(snippets.toList())) SnippetsPlugin.instance?.invalidateCache() SnippetsPlugin.instance?.refreshRegistry() - PluginWindows.showToast(requireContext(), "Snippets saved") + PluginWindows.showToast(requireContext(), getString(R.string.snippet_msg_saved)) } catch (e: Exception) { Log.e("SnippetManager", "Failed to save snippets", e) - PluginWindows.showToast(requireContext(), "Failed to save") + PluginWindows.showToast(requireContext(), getString(R.string.snippet_msg_save_failed)) } } diff --git a/snippets/src/main/res/values/strings.xml b/snippets/src/main/res/values/strings.xml new file mode 100644 index 00000000..25a84fce --- /dev/null +++ b/snippets/src/main/res/values/strings.xml @@ -0,0 +1,13 @@ + + + New Snippet + Edit Snippet + Save + Cancel + Delete + Delete Snippet + Delete \"%1$s\"? + All fields are required + Snippets saved + Failed to save +