From d320137063b0bbc2c0300c3b58ea312312a57217 Mon Sep 17 00:00:00 2001 From: Anas Rashid Date: Sun, 4 Oct 2026 19:14:22 +0200 Subject: [PATCH] Drop the "ask an assistant" entry from the selection menu With no local server configured it opened the same chooser Share does, only with a prompt in front of the text. Two entries for one chooser makes the reader choose twice, and the third entry pushed Look up into the overflow; both now fit in the bar. Asking moves to the lookup screen's top bar, where it keeps the part that was actually distinct: a configured server answers in place, and without one the question still goes out with "translate this and explain what it means" already written. Co-Authored-By: Claude Opus 5 (1M context) --- app/src/main/AndroidManifest.xml | 19 +---- .../ganjoor/android/ProcessTextActivity.kt | 71 +++++++++---------- 2 files changed, 38 insertions(+), 52 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 007c21c..571a4f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,9 +33,9 @@ one. Three aliases rather than a single Ganjoor entry: the menu is a place for verbs, and a reader who means "look this up" should not have to pick the app first. - "Ask an assistant" cannot be an entry per assistant — only Claude or Gemini can put their - own name in this menu. It opens the share chooser instead, which lists whichever of them - is installed, with the question already written. + There is no "ask an assistant" entry: with no local server configured it would open the + same chooser Share does, and two entries for one chooser makes the reader choose twice. + Asking lives on the lookup screen instead, where it can use a configured server. --> - - - - - - - - SelectionAction.Ask - "ShareText" -> SelectionAction.Share - else -> SelectionAction.LookUp - } + val sharing = intent.component?.className?.endsWith("ShareText") == true val settings = Settings(applicationContext) val systemInDark = resources.configuration.uiMode and @@ -110,48 +106,34 @@ class ProcessTextActivity : ComponentActivity() { LocalLayoutDirection provides LayoutDirection.Rtl, ) { GanjoorTheme(settings.value.theme, settings.value.language, settings.value.oled) { - SelectionScreen(selected, action, onClose = ::finish) + SelectionScreen(selected, sharing, onClose = ::finish) } } } } } -/** Which verb the reader picked in the selection menu. */ -enum class SelectionAction { LookUp, Ask, Share } - /** - * Each alias goes straight to its action. Share finishes immediately — the system chooser is the - * whole interface — so this screen is only ever seen for a lookup or an answer. + * Share goes straight to the system chooser and finishes, so this screen is only ever seen for a + * lookup. Asking an assistant is a button here rather than an entry of its own in the selection + * menu: with no server configured it opens the same chooser Share does, and two menu entries for + * one chooser is a menu that makes the reader choose twice. */ @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun SelectionScreen(selected: String, action: SelectionAction, onClose: () -> Unit) { +private fun SelectionScreen(selected: String, sharing: Boolean, onClose: () -> Unit) { val context = LocalContext.current val assistant = LocalAssistant.current - val prefs = LocalSettings.current.value - - // Without a server configured there is nothing to ask, so the question goes to whichever - // assistant the reader has installed. That hand-off is also what keeps this F-Droid-clean: - // no vendor SDK, no API key, nothing but an intent the reader confirms. - val askLocally = action == SelectionAction.Ask && assistant.serverReady val prompt = stringResource(R.string.assistant_ask_prompt) + var asking by remember { mutableStateOf(false) } - LaunchedEffect(action) { - when { - action == SelectionAction.Share -> { - context.shareText(selected) - onClose() - } - - action == SelectionAction.Ask && !askLocally -> { - context.shareText("$prompt\n\n$selected") - onClose() - } + LaunchedEffect(sharing) { + if (sharing) { + context.shareText(selected) + onClose() } } - - if (action == SelectionAction.Share || (action == SelectionAction.Ask && !askLocally)) return + if (sharing) return Scaffold( topBar = { @@ -163,9 +145,18 @@ private fun SelectionScreen(selected: String, action: SelectionAction, onClose: } }, actions = { - // The dictionary knows single words; a whole line is better asked about. - IconButton(onClick = { context.shareText("$prompt\n\n$selected") }) { - Icon(painterResource(R.drawable.ic_ask), stringResource(R.string.assistant_ask)) + // The dictionary knows single words; a whole line is better asked about. Your + // own server answers here; without one the question goes to whichever + // assistant is installed, which is what keeps this F-Droid-clean — no vendor + // SDK, no key, nothing but an intent you confirm. + IconButton(onClick = { + if (assistant.serverReady) asking = true + else context.shareText("$prompt\n\n$selected") + }) { + Icon( + painter = painterResource(R.drawable.ic_ask), + contentDescription = stringResource(R.string.assistant_ask), + ) } IconButton(onClick = { context.shareText(selected) }) { Icon(Icons.Default.Share, stringResource(R.string.share)) @@ -175,7 +166,15 @@ private fun SelectionScreen(selected: String, action: SelectionAction, onClose: }, ) { insets -> Column(modifier = Modifier.fillMaxSize().padding(insets)) { - if (askLocally) AssistantAnswer(selected) else WordLookup(selected) + WordLookup(selected) } } + + if (asking) { + AssistantResultSheet( + prompt = "explain", + text = selected, + onDismiss = { asking = false }, + ) + } }