compose-markdown icon indicating copy to clipboard operation
compose-markdown copied to clipboard

Missing context when using in app widget glance

Open YukiGasai opened this issue 11 months ago • 2 comments

Hi, I am trying to use the MarkdownText component inside my widget. The editor is not showing any errors until I add the widget. I get the following error.

Work [ id=72ffacc4-21eb-4b3a-98db-3968e91cbd5f, tags={ androidx.glance.session.SessionWorker } ] failed because it threw an exception/error
                                                                                                    java.util.concurrent.ExecutionException: java.lang.IllegalStateException: CompositionLocal LocalContext not present
                                                                                                    	at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:516)
                                                                                                    	at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
                                                                                                    	at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311)
                                                                                                    	at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
                                                                                                    	at java.lang.Thread.run(Thread.java:1012)
                                                                                                    Caused by: java.lang.IllegalStateException: CompositionLocal LocalContext not present
                                                                                                    	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.noLocalProvidedFor(AndroidCompositionLocals.android.kt:168)
                                                                                                    	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.access$noLocalProvidedFor(AndroidCompositionLocals.android.kt:1)
                                                                                                    	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$LocalContext$1.invoke(AndroidCompositionLocals.android.kt:54)
                                                                                                    	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$LocalContext$1.invoke(AndroidCompositionLocals.android.kt:53)
                                                                                                    	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getCurrent(ValueHolders.kt:29)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getValue(ValueHolders.kt:31)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.resolveCompositionLocal(Composer.kt:2069)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:2037)
                                                                                                    	at dev.jeziellago.compose.markdowntext.MarkdownTextKt.MarkdownText-Jo_eG7I(MarkdownText.kt:232)
                                                                                                    	at de.yukigasai.obsidiantodowidget.ComposableSingletons$TodoWidgetKt$lambda-2$1.invoke(TodoWidget.kt:135)
                                                                                                    	at de.yukigasai.obsidiantodowidget.ComposableSingletons$TodoWidgetKt$lambda-2$1.invoke(TodoWidget.kt:134)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                                                                                                    	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                                                                                                    	at de.yukigasai.obsidiantodowidget.TodoWidgetKt.LocalContextProvider(TodoWidget.kt:120)

I am using the minimal example:

override suspend fun provideGlance(context: Context, id: GlanceId) {
    refreshTodos(context, id)
    provideContent { Content() }
}


@Composable
fun Content() {
    val markdownContent = """  
            # Sample  
            * Markdown  
            * [Link](https://example.com)  
            ![Image](https://example.com/img.png)  
            <a href="https://www.google.com/">Google</a>  
        """.trimIndent()
    MarkdownText(markdown = markdownContent)
}

I already tried wrapping it in a LocalContextProvider, but that didn't seem to change anything.

override suspend fun provideGlance(context: Context, id: GlanceId) {
    refreshTodos(context, id)
    provideContent { Content() }
}

@Composable
fun LocalContextProvider(context: Context, content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalContext provides context, content = content)
}

@Composable
fun Content() {
    val markdownContent = """  
            # Sample  
            * Markdown  
            * [Link](https://example.com)  
            ![Image](https://example.com/img.png)  
            <a href="https://www.google.com/">Google</a>  
        """.trimIndent()
    val context = LocalContext.current
    LocalContextProvider(context) {
        MarkdownText(markdown = markdownContent)
    }
}

Thank you for creating this library, any help would be appreciated ^^.

YukiGasai avatar Sep 23 '23 16:09 YukiGasai

Hi, @YukiGasai! Thanks for reporting! Would be great if you could share more details or provide a sample app just to debugging better the issue and collect more data.

jeziellago avatar Sep 28 '23 20:09 jeziellago

The reason this isn't working is that Jetpack Glance isn't like regular Compose. It has its own composition system and you can't use custom components like this unfortunately.

You also cannot use custom Spannables, so even with the Glance Interoperability with XML declared RemoteViews, it's not possible to send Markwon parsed content to a widget.

See also this issue on Markwon: https://github.com/noties/Markwon/issues/403

KieronQuinn avatar Mar 20 '24 16:03 KieronQuinn