neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Let the wallpaper through the widgets
4cfa437 · Gabriel Morell · 2026-09-14 02:32
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