Skip to content

feat: system widgets foundation + price widget#895

Open
jvsena42 wants to merge 68 commits intomasterfrom
feat/system-widgets-foundation
Open

feat: system widgets foundation + price widget#895
jvsena42 wants to merge 68 commits intomasterfrom
feat/system-widgets-foundation

Conversation

@jvsena42
Copy link
Copy Markdown
Member

@jvsena42 jvsena42 commented Apr 9, 2026

This PR:

  1. Adds a Bitcoin Price home screen widget using Jetpack Glance
  2. Implements independent widget preferences stored in a separate DataStore
  3. Uses WorkManager for periodic background data refresh (15-minute intervals)
  4. Includes a configuration Activity for widget settings on click
  5. Renders a sparkline chart via Bitmap using Android Canvas API

Description

Introduces the foundation for Android home screen widgets (AppWidgets) using Jetpack Glance, starting with the Price widget. The widget displays enabled trading pairs with price, change percentage, and a line chart — matching the in-app Price widget layout.

The architecture reuses the existing PriceService for data fetching (via a new fetchData(period) overload) while keeping widget preferences completely independent from the in-app widget system through a dedicated AppWidgetPreferencesStore backed by its own DataStore file.

A Glance design system (GlanceTextStyles, GlanceColors) ports the app's typography and color tokens to Glance equivalents, and shared components (GlanceWidgetScaffold, GlanceDataRow) provide consistent widget layouts.

Preview

Screen_recording_20260421_153032.webm

QA Notes

  1. Long-press the home screen and select "Widgets"
    • Find "Bitcoin Price" in the widget picker
    • Verify the preview shows a price layout with mock data
  2. Add the widget to the home screen
    • Verify the configuration screen opens with trading pair and period options
    • Toggle pairs and period, tap Save
    • Verify the widget updates to show selected pairs with price, change %, and sparkline chart
  3. Wait 15+ minutes or force-stop and reopen
    • Verify the widget refreshes data via WorkManager
  4. Tap the widget
    • Verify it opens the widget settings screen
    • Verify the widget update after saving new preferences
  5. Long-press the widget
    • Verify the widget is resizable
    • Verify the chat is hidden if not enough vertical space
  6. Verify the OS widget settings are independent from internal app widgets

@jvsena42 jvsena42 changed the title feat: add price home screen widget feat: System widgets foundation + price widget Apr 9, 2026
@jvsena42 jvsena42 self-assigned this Apr 10, 2026
@jvsena42 jvsena42 marked this pull request as ready for review April 22, 2026 13:08
Comment thread app/src/main/java/to/bitkit/appwidget/ui/price/PriceGlanceWidget.kt Outdated
Comment thread app/src/main/java/to/bitkit/appwidget/AppWidgetRefreshWorker.kt Outdated
@jvsena42 jvsena42 marked this pull request as draft April 22, 2026 14:29
@jvsena42 jvsena42 marked this pull request as ready for review April 22, 2026 14:39
@jvsena42 jvsena42 requested review from ovitrif and pwltr April 22, 2026 17:07
Comment thread app/src/main/java/to/bitkit/data/widgets/PriceService.kt Outdated
Comment thread app/src/main/java/to/bitkit/appwidget/ui/components/GlanceText.kt Outdated
@jvsena42 jvsena42 marked this pull request as draft April 27, 2026 16:08
@jvsena42 jvsena42 marked this pull request as ready for review April 27, 2026 16:17
@jvsena42
Copy link
Copy Markdown
Member Author

jvsena42 commented Apr 28, 2026

some improvements like constant extractions implemented in #914

@claude

This comment has been minimized.

@pwltr
Copy link
Copy Markdown
Contributor

pwltr commented Apr 30, 2026

I tested on device, works as described, except it did not seem to update price for me automatically (waited ~30minutes). Only after I tapped the widget did it reload. Still don't know anything about Kotlin so won't review code.

EDIT: have seen it update now

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Found 2 issues: 1 CLAUDE.md violation and 1 bug.

}

@Composable
private fun PriceConfigContent(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLAUDE.md violation — The inner private composable must be named Content per CLAUDE.md:

ALWAYS split screen composables into parent accepting viewmodel + inner private child accepting state and callbacks Content()

The codebase consistently uses the literal name Content for the private inner child (e.g. ContactsScreen.kt, ProfileScreen.kt). This should be renamed from PriceConfigContent to Content.

Suggested change
private fun PriceConfigContent(
private fun Content(

val type = typeName?.let { runCatching { AppWidgetType.valueOf(it) }.getOrNull() }
?: AppWidgetType.PRICE

viewModel.init(appWidgetId, type)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BugviewModel.init() is called unconditionally in onCreate, which runs again on any configuration change (locale switch, font size adjustment, dark/light mode toggle) — not just rotation. At that point the widget hasn't been saved to the DataStore yet, so preferencesStore.getEntry(appWidgetId) returns null and the user's in-progress selections are silently reset to PricePreferences().

Fix: guard with savedInstanceState == null so init only runs on the first launch:

Suggested change
viewModel.init(appWidgetId, type)
if (savedInstanceState == null) viewModel.init(appWidgetId, type)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants