From 1973d77aec0ba4a7bd8dd14cb603b365384c9d71 Mon Sep 17 00:00:00 2001 From: Patrice Haltaya Date: Sat, 31 Oct 2026 19:27:44 +0100 Subject: [PATCH] fix(selection): no other app's text action in Compose's toolbar either 3.1.1 turned the text classifier off ("Translate" sent a note online), and the body's native field already keeps editing entries only (MenuDuChampDuCorps). Compose's fields (title, search, vault) did not: Compose foundation 1.12 (ProcessText_android) adds to their selection toolbar every VISIBLE app that declares an ACTION_PROCESS_TEXT action, and any app may, under any name. Removing the for it (audit of 2026-09-26) is not enough: before Android 11 there is no package visibility, every app is listed (minSdk is 24); from 11 on, visibility is granted per whole app, and any app another reaches comes with all its text actions. sansActionsDeTexteDesAutresApps() sets ComposeFoundationFlags.isSmartSelectionEnabled to false before the first composition, on every version: Compose's toolbars keep editing only, as the body does. Lost: reading aloud from the toolbar (TalkBack's action), still reachable through its own accessibility shortcut. Measured with a probe app that declares a PROCESS_TEXT action, see the pull request. Test on the real activity: the flag is off once MainActivity has run. Co-Authored-By: Claude Opus 5.5 --- .../secure/SansClassificationDeTexteTest.kt | 13 ++++++++++ .../filestech/notes_tech/ui/MainActivity.kt | 3 +++ .../ui/secure/SansClassificationDeTexte.kt | 24 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/app/src/androidTest/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexteTest.kt b/app/src/androidTest/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexteTest.kt index 1790b01..26a471d 100644 --- a/app/src/androidTest/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexteTest.kt +++ b/app/src/androidTest/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexteTest.kt @@ -5,6 +5,8 @@ import android.view.textclassifier.TextClassification import android.view.textclassifier.TextClassificationContext import android.view.textclassifier.TextClassificationManager import android.view.textclassifier.TextClassifier +import androidx.compose.foundation.ComposeFoundationFlags +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -91,6 +93,17 @@ class SansClassificationDeTexteTest { assertThat(application.textClassifier).isNotSameInstanceAs(TextClassifier.NO_OP) } + /** + * Compose must list no other app's text action, on any version: package visibility is granted + * per app. See [sansActionsDeTexteDesAutresApps]. + */ + @OptIn(ExperimentalFoundationApi::class) + @Test + fun compose_lists_no_other_app_text_action() { + gestionnaireDeLActivite() // the activity has run its onCreate + assertThat(ComposeFoundationFlags.isSmartSelectionEnabled).isFalse() + } + private fun gestionnaireDeLActivite(): TextClassificationManager { var gestionnaire: TextClassificationManager? = null scene!!.onActivity { gestionnaire = it.getSystemService(TextClassificationManager::class.java) } diff --git a/app/src/main/java/com/filestech/notes_tech/ui/MainActivity.kt b/app/src/main/java/com/filestech/notes_tech/ui/MainActivity.kt index 869ad56..ea01508 100644 --- a/app/src/main/java/com/filestech/notes_tech/ui/MainActivity.kt +++ b/app/src/main/java/com/filestech/notes_tech/ui/MainActivity.kt @@ -46,6 +46,7 @@ import com.filestech.notes_tech.ui.panic.RecouvrementDePanique import com.filestech.notes_tech.ui.panic.activityPanicViewModel import com.filestech.notes_tech.ui.secure.LocalSecureWindow import com.filestech.notes_tech.ui.secure.SecureWindowController +import com.filestech.notes_tech.ui.secure.sansActionsDeTexteDesAutresApps import com.filestech.notes_tech.ui.secure.sansClassificationDeTexte import com.filestech.notes_tech.ui.share.PartagesRecus import com.filestech.notes_tech.ui.splash.SplashScreen @@ -108,6 +109,8 @@ class MainActivity : FragmentActivity() { // 🔴 Before the first field exists: the selection toolbar's "Translate" sent the selected text // of a note to another app (3.1.1) — see `sansClassificationDeTexte`. sansClassificationDeTexte() + // Same toolbar, other door: other apps' text actions — editing entries only. + sansActionsDeTexteDesAutresApps() enableEdgeToEdge() setContent { CompositionLocalProvider( diff --git a/app/src/main/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexte.kt b/app/src/main/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexte.kt index 0b82b2c..5987f08 100644 --- a/app/src/main/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexte.kt +++ b/app/src/main/java/com/filestech/notes_tech/ui/secure/SansClassificationDeTexte.kt @@ -4,6 +4,8 @@ import android.app.Activity import android.os.Build import android.view.textclassifier.TextClassificationManager import android.view.textclassifier.TextClassifier +import androidx.compose.foundation.ComposeFoundationFlags +import androidx.compose.foundation.ExperimentalFoundationApi /** * 🔴 **No text classifier in this app** — 3.1.1, measured on 2026-10-29. @@ -33,3 +35,25 @@ internal fun Activity.sansClassificationDeTexte() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return getSystemService(TextClassificationManager::class.java)?.setTextClassifier(TextClassifier.NO_OP) } + +/** + * 🔴 **No other app's text action in Compose's selection toolbar** — measured on 2026-10-31. + * + * Compose foundation 1.12 adds to that toolbar one entry per app that declares `ACTION_PROCESS_TEXT` + * and that this app can see (`ProcessText_android`, read in the bytecode) — any app may declare one, + * under any name, and one tap hands it the selected text, in the title, search and vault fields + * alike. Removing the `` for it (audit of 2026-09-26) is not enough: before Android 11 there + * is no package visibility, every app is listed, a translator among them; from 11 on, visibility is + * granted per WHOLE app, and any app another `` of the manifest reaches is listed with all + * its text actions. + * + * [ComposeFoundationFlags.isSmartSelectionEnabled] set to `false` takes those entries — and smart + * selection, already off with the classifier — out of every Compose toolbar of the process, on every + * version: Compose's fields keep editing only, as the body's native field already does + * ([com.filestech.notes_tech.ui.editor.MenuDuChampDuCorps]). What is lost: + * reading aloud from the toolbar, still reachable through its own accessibility shortcut. + */ +@OptIn(ExperimentalFoundationApi::class) +internal fun sansActionsDeTexteDesAutresApps() { + ComposeFoundationFlags.isSmartSelectionEnabled = false +}