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) <noreply@anthropic.com>
This commit is contained in:
Anas Rashid 2026-10-04 19:14:22 +02:00
parent 90b527522b
commit d320137063
2 changed files with 38 additions and 52 deletions

View File

@ -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.
-->
<activity
android:name=".ProcessTextActivity"
@ -55,19 +55,6 @@
</intent-filter>
</activity-alias>
<activity-alias
android:name=".AskAssistantText"
android:icon="@drawable/ic_ask"
android:targetActivity=".ProcessTextActivity"
android:exported="true"
android:label="@string/assistant_ask">
<intent-filter>
<action android:name="android.intent.action.PROCESS_TEXT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ShareText"
android:icon="@drawable/ic_share"

View File

@ -39,7 +39,7 @@ import androidx.core.graphics.drawable.toDrawable
import com.ganjoor.android.data.AssistantSettings
import com.ganjoor.android.data.Dictionary
import com.ganjoor.android.data.LocalAssistant
import com.ganjoor.android.ui.AssistantAnswer
import com.ganjoor.android.ui.AssistantResultSheet
import com.ganjoor.android.ui.LocalSettings
import com.ganjoor.android.ui.Settings
import com.ganjoor.android.ui.WordLookup
@ -88,11 +88,7 @@ class ProcessTextActivity : ComponentActivity() {
return
}
val action = when (intent.component?.className?.substringAfterLast('.')) {
"AskAssistantText" -> 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 -> {
LaunchedEffect(sharing) {
if (sharing) {
context.shareText(selected)
onClose()
}
action == SelectionAction.Ask && !askLocally -> {
context.shareText("$prompt\n\n$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 },
)
}
}