From b1da3c216154919b2ee435327eccceeadfb2f847 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 15:52:21 +0000 Subject: [PATCH] fix: move ML Kit translation off the UI dispatcher LaunchedEffect runs on Dispatchers.Main by default. The first call to LanguageTranslatorService triggers its class init, which loads a Properties file from inside the play-services AAR via ZipFile/RandomAccessFile and trips StrictMode's DiskReadViolation on the UI thread. Wrap the translateAndCache call in withContext(Dispatchers.IO) so MLKit's first-touch init happens off the UI dispatcher. --- .../ui/components/TranslatableRichTextViewer.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 58b3c6232c..76b6265417 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -39,8 +39,10 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.MaxWidthPaddingTop5dp import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext import kotlin.coroutines.coroutineContext @Composable @@ -101,7 +103,13 @@ fun TranslatableRichTextViewer( LaunchedEffect(content, translateTo, dontTranslateFrom) { try { - translatedTextState.value = translateAndCache(content, translateTo, dontTranslateFrom) + // ML Kit's first-touch class init reads a Properties file out of the play-services AAR + // via ZipFile/RandomAccessFile, which trips StrictMode if it runs on the UI dispatcher + // that LaunchedEffect uses by default. + translatedTextState.value = + withContext(Dispatchers.IO) { + translateAndCache(content, translateTo, dontTranslateFrom) + } } catch (e: CancellationException) { throw e } catch (_: Exception) {