Skip to content

feat: added widget#1018

Merged
spacecowboy merged 4 commits intospacecowboy:masterfrom
MatthewTighe:add-widget
Mar 5, 2026
Merged

feat: added widget#1018
spacecowboy merged 4 commits intospacecowboy:masterfrom
MatthewTighe:add-widget

Conversation

@MatthewTighe
Copy link
Contributor

@MatthewTighe MatthewTighe commented Jan 7, 2026

This PR adds a newsfeed widget using Jetpack Glance for #384. It:

  • establishes a separate "current feed and tag" for the widget
  • allows customization of the display feed and tag through a settings activity
  • allows refreshing the widget through a "refresh button"

There are a couple areas that could potentially use more polish, but I wanted to get this up to at least start a conversation as to whether it is worth waiting for them.

Areas of potential improvement:

  1. the widget preview never renders, just loads forever
  2. lack of tests - Glance is allegedly unit testable, but because this access LocalContext, it would need to provided by something like Robolectric, which I did not think was worth importing for that alone. there's otherwise very little logic here, but happy to eventually add tests for the Repository etc if preferred
  3. I can't get the "Refreshing" text to actually display for some reason - though it does if I make that the only content. I'll probably keep messing with this at some point
  4. widget isn't resizable

LMK your thoughts on whether any of these are worth baking longer - happy to keep trucking on this but it's obviously been slow going. Assume we will just squash these commits as well?

Screen_recording_20260106_222913.webm

Fixes #384

Copy link
Owner

@spacecowboy spacecowboy left a comment

Choose a reason for hiding this comment

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

Thank you so much for doing this. Apologies for the delay in review, I've been busy IRL.

When trying this I got the following error messages spamming my log and the widget wouldn't show anything:

Error in Glance App Widget
java.lang.IllegalArgumentException: RemoteViews for widget update exceeds maximum bitmap memory usage (used: 22240000, max: 15552000)

Once I commented out the bitmap images of the items it displayed.

So I've added some suggestions to make bitmaps optional to start with. Obviously it wont' fix the crash I saw but at least items without images will be rendered now.

I think overall it's a great start.

Before merging I think it's important to ensure all items are displayed. If it's easier, just disable images to start with maybe? Up to you.

And it needs to be resizable, because I think that is an overall guideline requirement by Google these days?

Comment on lines +395 to +454
@OptIn(ExperimentalCoroutinesApi::class)
fun getCurrentWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentWidgetFeedAndTag,
feedListFilter,
search,
) { feedAndTag, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime = Instant.EPOCH,
newestFirst = true,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}

@OptIn(ExperimentalCoroutinesApi::class)
fun getWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentFeedAndTag,
minReadTime,
currentSorting,
feedListFilter,
search,
) { feedAndTag, minReadTime, currentSorting, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime =
when (feedId) {
ID_SAVED_ARTICLES -> Instant.EPOCH
else -> minReadTime
},
newestFirst = currentSorting == SortingOptions.NEWEST_FIRST,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}

Copy link
Owner

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but there is no search functionality for the widget right?

So no need to include the search query?

Suggested change
@OptIn(ExperimentalCoroutinesApi::class)
fun getCurrentWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentWidgetFeedAndTag,
feedListFilter,
search,
) { feedAndTag, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime = Instant.EPOCH,
newestFirst = true,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}
@OptIn(ExperimentalCoroutinesApi::class)
fun getWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentFeedAndTag,
minReadTime,
currentSorting,
feedListFilter,
search,
) { feedAndTag, minReadTime, currentSorting, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime =
when (feedId) {
ID_SAVED_ARTICLES -> Instant.EPOCH
else -> minReadTime
},
newestFirst = currentSorting == SortingOptions.NEWEST_FIRST,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}
@OptIn(ExperimentalCoroutinesApi::class)
fun getCurrentWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentWidgetFeedAndTag,
feedListFilter,
) { feedAndTag, feedListFilter ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime = Instant.EPOCH,
newestFirst = true,
filter = feedListFilter,
search = "",
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}
@OptIn(ExperimentalCoroutinesApi::class)
fun getWidgetFeedListItems(): Flow<PagingData<FeedListItem>> =
combine(
currentFeedAndTag,
minReadTime,
currentSorting,
feedListFilter,
) { feedAndTag, minReadTime, currentSorting, feedListFilter ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
tag = tag,
minReadTime =
when (feedId) {
ID_SAVED_ARTICLES -> Instant.EPOCH
else -> minReadTime
},
newestFirst = currentSorting == SortingOptions.NEWEST_FIRST,
filter = feedListFilter,
search = "",
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}

@MatthewTighe
Copy link
Contributor Author

MatthewTighe commented Feb 12, 2026

@spacecowboy thanks so much for taking a look! I've applied your patch feedback.

For the memory crash, I've tried a couple things:

  1. setting a target size to the coil ImageRequest. I doubt this did much, but I'm wondering if perhaps oversized bitmaps were being loaded and then just visually scaled down.
  2. I changed the format used for loading the image to the lowest density one.

That said, I haven't run into the crash in my local testing. Could you either see if you can still reproduce, or perhaps pass along a repro OPML export?

I've kept the patch applying your feedback separate for now, assuming we will squash and merge eventually

@spacecowboy
Copy link
Owner

spacecowboy commented Feb 16, 2026

@spacecowboy thanks so much for taking a look! I've applied your patch feedback.

For the memory crash, I've tried a couple things:

  1. setting a target size to the coil ImageRequest. I doubt this did much, but I'm wondering if perhaps oversized bitmaps were being loaded and then just visually scaled down.
  2. I changed the format used for loading the image to the lowest density one.

That said, I haven't run into the crash in my local testing. Could you either see if you can still reproduce, or perhaps pass along a repro OPML export?

The feed I tried with was Feeder's default feed for the app's changelog which automatically gets added on fresh installs: https://news.nononsenseapps.com/

I've kept the patch applying your feedback separate for now, assuming we will squash and merge eventually

Yes it will get squashed on merge. You forgot to remove the search for getWidgetFeedListItems though

I will try the new version soon

@MatthewTighe
Copy link
Contributor Author

Whoops, looks like getWidgetFeedListItems had become unused anyway so I removed it. I'm still not able to reproduce the crash you're seeing unfortunately

Screen_recording_20260301_151249.mp4

@spacecowboy
Copy link
Owner

I'll take another look.

@MatthewTighe do you feel it is ready now from your perspective and testing?

@spacecowboy
Copy link
Owner

It seems to work nicely now, and the resizing works smoothly 👍

One thing I noted is that the margins are un-even. There is a margin on the left but not on the right. But this is not a blocker by any means. This can be fixed later.

@spacecowboy spacecowboy changed the title feat: add newsfeed widget (#384) feat: added widget (#384) Mar 2, 2026
@spacecowboy spacecowboy changed the title feat: added widget (#384) feat: added widget Mar 2, 2026
@MatthewTighe
Copy link
Contributor Author

Ha, I am not the most skilled when it comes to UX 😅 I often have to rely on the professionals for that piece of sign-off. Happy to take a look if you'd like. If it's not a blocker, is there anything else you need from me before this is merged?

@spacecowboy spacecowboy merged commit 8da9aac into spacecowboy:master Mar 5, 2026
9 of 12 checks passed
@spacecowboy
Copy link
Owner

Then I think we should just merge. Not letting perfect be the enemy of good.

Thanks for the contribution @MatthewTighe !

@MatthewTighe
Copy link
Contributor Author

🙌 awesome, thanks for helping me get it over the finish line! looking forward to using it in the wild 😁

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.

Newsfeed Widget

2 participants