From 0c7c553ed48045c56fba201e4e7c0dd148d3cf23 Mon Sep 17 00:00:00 2001 From: birdbird <6892457-bird.bird@users.noreply.gitlab.com> Date: Sun, 4 Oct 2026 17:45:02 +0000 Subject: [PATCH] Fix MaterialAlertDialogBuilder crash with unthemed application context --- .../org/moire/ultrasonic/util/Dialogs.kt | 20 ++++++++++++++++--- .../org/moire/ultrasonic/util/UiUtil.kt | 3 ++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Dialogs.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Dialogs.kt index 7988dc3c..dc19c855 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Dialogs.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Dialogs.kt @@ -9,11 +9,22 @@ package org.moire.ultrasonic.util import android.app.Activity import android.content.Context +import android.view.ContextThemeWrapper import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.lang.ref.WeakReference import org.moire.ultrasonic.R import timber.log.Timber +/** + * MaterialAlertDialogBuilder requires a context whose theme inherits from Theme.AppCompat + * (it resolves the colorPrimary attribute in ThemeEnforcement). An application or service + * context carries no app theme, so it must be wrapped in a ContextThemeWrapper. + */ +private fun themedContext(context: Context): Context { + if (context is Activity || context is ContextThemeWrapper) return context + return ContextThemeWrapper(context, UiUtil.getStyleFromSettings(context)) +} + /* * InfoDialog can be used to show some information to the user. Typically it cannot be cancelled, * only dismissed via OK. @@ -25,7 +36,8 @@ open class InfoDialog( private val finishActivityOnClose: Boolean = false ) { private val activityRef: WeakReference = WeakReference(activity) - open var builder: MaterialAlertDialogBuilder = Builder(activityRef.get() ?: context, message) + open var builder: MaterialAlertDialogBuilder = + Builder(themedContext(activityRef.get() ?: context), message) fun show() { builder.setOnCancelListener { @@ -74,7 +86,8 @@ class ErrorDialog( finishActivityOnClose: Boolean = false ) : InfoDialog(context, message, activity, finishActivityOnClose) { - override var builder: MaterialAlertDialogBuilder = Builder(activity ?: context, message) + override var builder: MaterialAlertDialogBuilder = + Builder(themedContext(activity ?: context), message) class Builder(context: Context) : MaterialAlertDialogBuilder(context) { constructor(context: Context, message: CharSequence?) : this(context) { @@ -101,7 +114,8 @@ class ConfirmationDialog( activity: Activity? = null, finishActivityOnClose: Boolean = false ) : InfoDialog(context, message, activity, finishActivityOnClose) { - override var builder: MaterialAlertDialogBuilder = Builder(activity ?: context, message) + override var builder: MaterialAlertDialogBuilder = + Builder(themedContext(activity ?: context), message) class Builder(context: Context) : MaterialAlertDialogBuilder(context) { constructor(context: Context, message: CharSequence?) : this(context) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/UiUtil.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/UiUtil.kt index fce055a2..d22e8559 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/UiUtil.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/UiUtil.kt @@ -43,7 +43,8 @@ object UiUtil { context.setTheme(R.style.UltrasonicTheme_Base) } - private fun getStyleFromSettings(context: Context): Int = when (Settings.theme.lowercase()) { + @JvmStatic + fun getStyleFromSettings(context: Context): Int = when (Settings.theme.lowercase()) { context.getString(R.string.setting_key_theme_dark) -> { R.style.UltrasonicTheme_Dark }