feat: headlines widget v61 + OS widget#919
feat: headlines widget v61 + OS widget#919jvsena42 wants to merge 25 commits intofeat/price-widget-v61from
Conversation
|
The OS widget is taller than the in-app because it has to fit in two cell. Tested with one cell height, it got too short |
…/headlines-v61 # Conflicts: # app/src/main/java/to/bitkit/ui/screens/widgets/price/PricePreviewScreen.kt
# Conflicts: # CHANGELOG.md
| state: AppWidgetConfigUiState, | ||
| onToggleSource: () -> Unit, | ||
| onToggleTime: () -> Unit, | ||
| onReset: () -> Unit, | ||
| onSave: () -> Unit, |
There was a problem hiding this comment.
These are hardcoded English strings in the live, user-facing widget configuration UI. HeadlinesConfigContent is called from the production AppWidgetConfigScreen dispatch (not from a @Preview-annotated composable), so real users will see these strings.
Per the CLAUDE.md rule:
NEVER hardcode strings and always preserve string resources
The exception ("NEVER add string resources for strings used only in dev settings screens and previews") does not apply here.
These three strings (title, timeAgo, publisher) should be added as entries in strings.xml (in alphabetical order) and referenced via stringResource(R.string.…) at the call site.
Reference:
| ?: AppWidgetEntry(appWidgetId = appWidgetId, type = AppWidgetType.HEADLINES) | ||
| val article = remember(data.cachedArticles, data.articleRotationTick) { | ||
| data.cachedArticles.randomOrNull()?.toArticleModel() |
There was a problem hiding this comment.
The articleRotationTick is used only as a remember() invalidation key — it is never used to index into cachedArticles. Article selection is purely random (randomOrNull()), so:
- The same article can appear on consecutive refreshes.
- Articles can be skipped entirely.
The rotation counter built in AppWidgetPreferencesStore.bumpArticleRotationTick() and wired up in AppWidgetRefreshWorker has no effect on which article is displayed.
To make rotation deterministic as intended, use the tick as an index:
val article = remember(data.cachedArticles, data.articleRotationTick) {
val articles = data.cachedArticles
articles.getOrNull(data.articleRotationTick.toInt() % articles.size.coerceAtLeast(1))
?.toArticleModel()
}Reference:
FIGMA
Stacks on top of #914. This PR:
Description
The Headlines widget now ships as a system widget.
HeadlinesGlanceWidgetis registered viaHeadlinesGlanceReceiverin the manifest, usesSizeMode.Responsiveto switch between a Wide layout (343x152dp, source/time row under the headline) and a Compact layout (163x192dp, headline-only with optional time pinned bottom-right), and reuses the foundation'sGlanceWidgetScaffold+ tap-to-edit intent. Tapping the widget opens the article URL when present, falling back toAppWidgetConfigActivityfor unconfigured/no-data instances. The Glance content reads cached articles fromAppWidgetPreferencesStoreand renders a random one.The shared
AppWidgetConfigViewModelandAppWidgetConfigScreenwere extended to handle Headlines: aHeadlinePreferencesfield (in-app) mapped to/fromHomeHeadlinePreferences(datastore),toggleShowTime/toggleShowSourceactions, branchedresetPreferences/saveAndFinishperAppWidgetType, and a CONTENT section listing Title / Source / Time toggles. Save now persists the entry, registers the widget, and warms the article cache viaAppWidgetDataRepository.fetchArticles().AppWidgetRefreshWorkerwas updated to refresh Headlines instances alongside Price.In-app,
HeadlineCardis split into wide and compact variants. Wide: full-width card with a 4-lineTitle, plus a Brand-colored source / White64 time row. Compact: 163x192dp card with a 4-line title and optional time at the bottom. The widget title row, newspaper icon, and "Source" label have all been removed in favor of the v61 layout.HeadlinesPreviewScreenmirrors the price preview — top-bar title, description, divider, Widget Settings row showing Default/Custom,WidgetSizeCarouselbetween the small and wide cards, and Save/Delete buttons.HeadlinesEditScreenis restructured into a CONTENT section usingCaption13Upheaders and toggle rows (Title is locked-on, Source and Time are user-toggleable) with dividers, replacing the previous flat list.Manifest, XML widget info (
appwidget_info_headlines.xml), and a static preview layout (appwidget_preview_headlines.xml) are added to register the new provider with a 4x2 default placement and 2x2 minimum, matching the price widget. Three new strings (widgets__widget__content,widgets__widget__save,widgets__widget__settings) were added tostrings.xml.Preview
os-widget.webm
app-widget.webm
QA Notes
System widget
In-app widget
Build & lint
./gradlew compileDevDebugKotlinclean./gradlew testDevDebugUnitTestclean (existingHeadlineCardTest/HeadlinesPreviewContentTestupdated for the new layout)./gradlew detektclean