Let the wallpaper through the widgets

4cfa437 · Gabriel Morell · 2026-09-14 02:32

18 files +88 -9
Message
{commit_body(@commit)}

Files changed

modified clients/android/app/src/main/java/io/neiam/apolloscrib/data/Settings.kt
+15 −0
@@ -91,6 +91,20 @@ class Settings(context: Context) {
91 91 get() = prefs.getString(KEY_THEME, "after-dark").orEmpty()
92 92 set(value) = prefs.edit { putString(KEY_THEME, value) }
93 93
94 + /**
95 + * Whether the home screen widgets drop their own background and let the
96 + * wallpaper through.
97 + *
98 + * The board's colours stay the theme's -- this is not a second palette,
99 + * it is the same one with the ground taken out. The cards keep a faint
100 + * wash of their card colour rather than going fully clear: a card with
101 + * nothing behind it is unreadable over a photograph, and the wash is
102 + * enough to sit the text on without being a panel.
103 + */
104 + var transparentWidgets: Boolean
105 + get() = prefs.getBoolean(KEY_TRANSPARENT_WIDGETS, false)
106 + set(value) = prefs.edit { putBoolean(KEY_TRANSPARENT_WIDGETS, value) }
107 +
94 108 var useTls: Boolean
95 109 get() = prefs.getBoolean(KEY_TLS, false)
96 110 set(value) = prefs.edit { putBoolean(KEY_TLS, value) }
@@ -159,6 +173,7 @@ class Settings(context: Context) {
159 173 private const val KEY_PUBLISH_LOCATION = "publish_location"
160 174 private const val KEY_RICH_CARDS = "rich_cards"
161 175 private const val KEY_ROTATION_MS = "rotation_ms"
176 + private const val KEY_TRANSPARENT_WIDGETS = "transparent_widgets"
162 177
163 178 /**
164 179 * The turns on offer, quickest first. Twelve seconds is the default
modified clients/android/app/src/main/java/io/neiam/apolloscrib/ui/MainActivity.kt
+29 −1
@@ -44,9 +44,9 @@ import androidx.compose.ui.platform.LocalContext
44 44 import io.neiam.apolloscrib.data.Pairing
45 45 import io.neiam.apolloscrib.feed.FeedProvider
46 46 import kotlinx.coroutines.delay
47 +import io.neiam.apolloscrib.data.Settings
47 48 import io.neiam.apolloscrib.widget.BoardWidget
48 49 import io.neiam.apolloscrib.widget.RichBoardWidget
49 −import io.neiam.apolloscrib.data.Settings
50 50 import io.neiam.apolloscrib.ui.theme.ALL_THEMES
51 51 import io.neiam.apolloscrib.ui.theme.CribTheme
52 52 import io.neiam.apolloscrib.ui.theme.LocalAppTheme
@@ -240,6 +240,34 @@ private fun ConnectionScreen(
240 240 Switch(checked = tls, onCheckedChange = { tls = it })
241 241 }
242 242
243 + // The widgets only: the app's own screens keep their ground, which is
244 + // what makes the board readable in the hand. A home screen already
245 + // has a picture behind it.
246 + var transparentWidgets by remember(pairingKey) {
247 + mutableStateOf(settings.transparentWidgets)
248 + }
249 + Column {
250 + Text("Transparent widgets", style = MaterialTheme.typography.bodyLarge)
251 + Text(
252 + "Drops the home screen widgets' background so the wallpaper " +
253 + "shows through. The cards keep a faint wash to sit the text on.",
254 + style = MaterialTheme.typography.bodySmall,
255 + color = LocalAppTheme.current.dim
256 + )
257 + Switch(
258 + checked = transparentWidgets,
259 + onCheckedChange = {
260 + settings.transparentWidgets = it
261 + transparentWidgets = it
262 + // Both widgets redraw now rather than at the next board:
263 + // a setting whose effect waits for a bus is a setting the
264 + // user thinks did nothing.
265 + BoardWidget.refresh(context)
266 + RichBoardWidget.refresh(context)
267 + }
268 + )
269 + }
270 +
243 271 // Opt-in, and the permission is asked for only when it is switched
244 272 // on: a client on a wall showing a fixed vision has no business
245 273 // holding one.
modified clients/android/app/src/main/java/io/neiam/apolloscrib/widget/BoardWidget.kt
+10 −2
@@ -37,10 +37,18 @@ class BoardWidget : AppWidgetProvider() {
37 37 }
38 38
39 39 private fun build(context: Context, widgetId: Int): RemoteViews {
40 − val palette = appThemeByKey(Settings(context).themeKey)
40 + val settings = Settings(context)
41 + val palette = appThemeByKey(settings.themeKey)
41 42
42 43 return RemoteViews(context.packageName, R.layout.widget_board).apply {
43 − setInt(R.id.board_root, "setBackgroundColor", palette.bg.toArgb())
44 + // Transparent takes the ground out entirely: the widget frame is
45 + // the launcher's, and the cards carry their own wash.
46 + setInt(
47 + R.id.board_root,
48 + "setBackgroundColor",
49 + if (settings.transparentWidgets) android.graphics.Color.TRANSPARENT
50 + else palette.bg.toArgb()
51 + )
44 52 setTextColor(R.id.board_empty, palette.dim.toArgb())
45 53
46 54 // The factory needs the widget id, and an Intent's extras are not
modified clients/android/app/src/main/java/io/neiam/apolloscrib/widget/BoardWidgetService.kt
+12 −2
@@ -23,6 +23,7 @@ private class BoardFactory(private val context: Context) : RemoteViewsService.Re
23 23 private val store = VisionStore(context)
24 24 private var cards: List<Preview> = emptyList()
25 25 private var palette: AppTheme = appThemeByKey(Settings(context).themeKey)
26 + private var transparent: Boolean = Settings(context).transparentWidgets
26 27
27 28 override fun onCreate() = Unit
28 29
@@ -32,7 +33,9 @@ private class BoardFactory(private val context: Context) : RemoteViewsService.Re
32 33 * parsed once for the whole cycle.
33 34 */
34 35 override fun onDataSetChanged() {
35 − palette = appThemeByKey(Settings(context).themeKey)
36 + val settings = Settings(context)
37 + palette = appThemeByKey(settings.themeKey)
38 + transparent = settings.transparentWidgets
36 39 // The Plus board where there is one: it carries the extended reading
37 40 // for transit and Basic's own answer for every type Plus does not
38 41 // write, so it is the same board with more on it rather than a
@@ -53,7 +56,14 @@ private class BoardFactory(private val context: Context) : RemoteViewsService.Re
53 56 val card = cards.getOrNull(position) ?: return loading()
54 57
55 58 return RemoteViews(context.packageName, R.layout.widget_card).apply {
56 − setInt(R.id.card_root, "setBackgroundColor", palette.cardBg.toArgb())
59 + // A wash rather than nothing when transparent: enough to sit text on
60 + // over a photograph, not enough to read as a panel.
61 + setInt(
62 + R.id.card_root,
63 + "setBackgroundColor",
64 + if (transparent) palette.cardBg.copy(alpha = 0.35f).toArgb()
65 + else palette.cardBg.toArgb()
66 + )
57 67
58 68 setImageViewResource(R.id.card_icon, card.iconRes)
59 69 setInt(R.id.card_icon, "setColorFilter", palette.primary.toArgb())
modified clients/android/app/src/main/java/io/neiam/apolloscrib/widget/RichBoardWidget.kt
+10 −2
@@ -37,10 +37,18 @@ class RichBoardWidget : AppWidgetProvider() {
37 37 }
38 38
39 39 private fun build(context: Context, widgetId: Int): RemoteViews {
40 − val palette = appThemeByKey(Settings(context).themeKey)
40 + val settings = Settings(context)
41 + val palette = appThemeByKey(settings.themeKey)
41 42
42 43 return RemoteViews(context.packageName, R.layout.widget_rich_board).apply {
43 − setInt(R.id.rich_board_root, "setBackgroundColor", palette.bg.toArgb())
44 + // Transparent takes the ground out entirely: the widget frame is
45 + // the launcher's, and the cards carry their own wash.
46 + setInt(
47 + R.id.rich_board_root,
48 + "setBackgroundColor",
49 + if (settings.transparentWidgets) android.graphics.Color.TRANSPARENT
50 + else palette.bg.toArgb()
51 + )
44 52 setTextColor(R.id.rich_board_empty, palette.dim.toArgb())
45 53
46 54 // The factory needs the widget id, and an Intent's extras are not
modified clients/android/app/src/main/java/io/neiam/apolloscrib/widget/RichBoardWidgetService.kt
+12 −2
@@ -32,11 +32,14 @@ class RichBoardWidgetService : RemoteViewsService() {
32 32 private val store = VisionStore(context)
33 33 private var cards: List<RichFacts> = emptyList()
34 34 private var palette: AppTheme = appThemeByKey(Settings(context).themeKey)
35 + private var transparent: Boolean = Settings(context).transparentWidgets
35 36
36 37 override fun onCreate() = Unit
37 38
38 39 override fun onDataSetChanged() {
39 − palette = appThemeByKey(Settings(context).themeKey)
40 + val settings = Settings(context)
41 + palette = appThemeByKey(settings.themeKey)
42 + transparent = settings.transparentWidgets
40 43
41 44 // The Plus board where there is one, as the compact widget does:
42 45 // the same board with more on it, and Basic's own answer for every
@@ -63,7 +66,14 @@ class RichBoardWidgetService : RemoteViewsService() {
63 66 val card = cards.getOrNull(position) ?: return loading()
64 67
65 68 return RemoteViews(context.packageName, R.layout.widget_rich_card).apply {
66 − setInt(R.id.rich_root, "setBackgroundColor", palette.cardBg.toArgb())
69 + // A wash rather than nothing when transparent: enough to sit text on
70 + // over a photograph, not enough to read as a panel.
71 + setInt(
72 + R.id.rich_root,
73 + "setBackgroundColor",
74 + if (transparent) palette.cardBg.copy(alpha = 0.35f).toArgb()
75 + else palette.cardBg.toArgb()
76 + )
67 77
68 78 setImageViewResource(R.id.rich_icon, card.iconRes)
69 79 setInt(R.id.rich_icon, "setColorFilter", palette.primary.toArgb())

Parents: de5c6b0