Skip to content

Redesign the widget setup, widget, and app screens - #69

Merged
hameno merged 15 commits into
masterfrom
feature/ux_redesign
Oct 10, 2026
Merged

hameno merged 15 commits into
masterfrom
feature/ux_redesign

Conversation

@hameno

@hameno hameno commented Oct 10, 2026

Copy link
Copy Markdown
Member

Implements the UX proposals from the design canvas, including moving the home-screen widget to Jetpack Glance. Nine commits, each self-contained with its tests:

  1. Data layer: Widget gets a curated header_color and a per-widget sort_order. The migration from 3 to 4 maps black and dark-grey widgets to the dark header, and has a migration test. App matching and sorting are shared in apps/AppMatching.kt.
  2. Glance widget: DDWidgetProvider keeps its name and becomes a GlanceAppWidgetReceiver. The widget gets system corners, a tonal header, app name before package, and an empty state with "Choose apps". The RemoteViews service, factory and layouts are removed.
  3. Setup on placement: "Which apps should this widget show?" offers one of your apps (signature filter), a package pattern, or an existing profile.
  4. Widgets list and editor: cards show the header colour, profile and app count. The editor has a live preview, the palette, profiles as radio rows with counts, and a per-widget sort order.
  5. Profiles: tap opens a profile, and an overflow menu offers Duplicate and Delete (with Undo). Swipe and long-press delete are removed. Cards show counts and the widgets using each profile.
  6. Profile editor: filters in plain language, a live "Matching apps (N)" list, and one "Add filter" sheet.
  7. Settings: plain-language wording, in English and German.
  8. Cleanup: removes orphaned resources and updates CLAUDE.md.
  9. Code review fixes:
    • Glance refresh while a session runs, via a refresh token in widget state.
    • A row and icon size cap that keeps the widget under the Binder limit.
    • Invalid regex patterns no longer crash.
    • Setup double-tap and back behaviour.
    • Editors no longer reload app icons on every keystroke.

Bugs fixed along the way

  • Placing a widget from the launcher's picker was broken. The widget was never saved, and the configure activity never returned RESULT_OK.
  • Settings sort labels were swapped. They were mapped by enum position, so "Last updated" and "First installed" were reversed.
  • An invalid package pattern crashed every screen that matches apps.

Testing

  • 73 unit tests pass, including Robolectric Compose, Glance (runGlanceAppWidgetUnitTest) and Room migration tests. Lint reports no errors, and assembleDebug succeeds.
  • Not yet run on a device or emulator. Please check these by hand:
    • Placing a widget from the picker, then backing out of setup.
    • Reload and refresh after installing an app.
    • A profile with more than 40 apps.
    • The first refresh after upgrading from the RemoteViews version.

🤖 Generated with Claude Code

hameno and others added 9 commits October 10, 2026 15:50
- Widget gains header_color (curated palette: amber, neutral, dark,
  dynamic) and a nullable per-widget sort_order. Migration 3 to 4 maps
  black and dark-grey widgets to the dark header, the rest to amber.
- matching() and SortOrder.comparator() extract the profile matching
  and sorting from the RemoteViews factory, so the widget and the app
  screens can share them.
- room-testing plus the schema exports in debug assets let
  MigrationTestHelper run under Robolectric.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- DDWidgetProvider keeps its name (placed widgets depend on it) and is
  now a GlanceAppWidgetReceiver; WidgetService, the RemoteViews factory
  and its layouts are gone.
- The widget has system corner radii, a tonal header from the curated
  palette (or the wallpaper colours), "7 apps · updated 15:12", app name
  before package, and quieter uninstall/app-details actions.
- A widget whose profile matches nothing explains it and offers
  "Choose apps" instead of showing a blank list.
- WidgetContentLoader assembles a widget's apps from the shared
  matching, honouring a per-widget sort order over the Settings one.
  App icons are scaled to the 40 dp they are drawn at.
- widget_info: Glance loading layout, picker description, target size,
  reconfigurable; drops the unused autoAdvanceViewId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Placing a widget from the launcher's picker opened the widget editor
for a widget that was never saved (only the pin flow saved one), and
the configure activity never returned RESULT_OK.

Now the configure intent opens a setup screen: "Which apps should this
widget show?", answered with one of your apps (signature filter), a
package pattern, or an existing profile, with a live count of the apps
that will appear. Done creates the profile and filter, saves the
widget named after the profile, returns RESULT_OK and closes. Leaving
without finishing keeps the result cancelled.

The widget's empty state opens the same setup for an existing widget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Widgets list: each card shows the widget's header colour, its profile
and how many apps it lists, instead of the internal "ID: 5". A widget
whose profile matches nothing says "No apps match · Choose apps" and
opens setup.

Widget editor: a live preview of the widget, the curated header palette
(wallpaper colours on Android 12+) instead of the 11 raw colours,
profiles as radio rows with app counts so the selection is visible, and
a per-widget sort order ("Default (…)" keeps the Settings value).

Also fixes Settings' sort labels: they were mapped by enum position, so
"Last updated" and "First installed" were swapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Today a tap edits a profile, a long press deletes it, and swiping does
either, with nothing on screen showing how to delete. Now a tap opens
the profile and each card's overflow menu offers Duplicate and Delete.
Deleting is immediate with Undo in a snackbar (the profile's filters
come back too); a profile that widgets still use keeps the in-use
dialog. Cards show "1 filter · 7 apps" and which widgets use the
profile.

The delete confirmation dialog and its strings are gone, replaced by
undo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profile editor lists filters in plain language ("Signed like
DevDrawer2", "Package matches com.example.*") with each filter's own
app count, and a live "Matching apps (N)" list of what the profile
shows, updated as filters are added or removed before saving.

One "Add filter" button opens a sheet that explains both filter types
in a sentence each. Tapping a filter shows its details; the close icon
removes it (nothing is saved until Save, so no confirmation).

The per-filter preview dialog and the delete-filter confirmation are
gone, with their now-unused strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "Show activity choice on launch" becomes "Ask which screen to open",
  with a summary explaining when it applies.
- "Widget Sorting Options" becomes "Default sort order": it now only
  applies to widgets without their own order.
- The "User Interface" category becomes "Appearance" (sentence case).

German follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Remove resources the redesign orphaned: the old widget-row icons and
  their tint colour, and eight strings no screen uses any more.
- Read the Profiles snackbar's strings through LocalResources, which
  follows configuration changes (lint error).
- "Show all" in setup reuses the profile editor's string instead of a
  string lint wants as a plural.
- CLAUDE.md: Glance widget, setup route and configure result, shared
  app matching, database version 4, and the Robolectric-based UI,
  Glance and migration tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Widget: a running Glance session only recomposes on update(), so
  Reload and app-triggered updates could show stale apps. A refresh
  token in the widget's Glance state now makes the session reload;
  UpdateReceiver and RefreshAction bump it.
- Widget: list at most 40 apps (with "+N more apps") and store icons
  at 72 px at most, keeping the RemoteViews under the Binder limit.
- An invalid package pattern ("com.(") now matches nothing instead of
  throwing in every screen that matches apps.
- Setup: Done can't create a second profile while saving.
- Back from setup opened by the launcher or the widget closes the app
  (a widget being placed is then removed, as the launcher expects);
  in-app setup still goes back.
- Editors only reload app labels and icons when filters, profile, sort
  order or installed apps change, not on every name keystroke; the
  Widgets list reads installed apps once.
- The profile editor's matching apps reuse AppInfoItem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Test Results

90 tests  +43   90 ✅ +43   28s ⏱️ -1s
25 suites +15    0 💤 ± 0 
25 files   +15    0 ❌ ± 0 

Results for commit de8ed6e. ± Comparison against base commit 21f22eb.

This pull request removes 20 and adds 63 tests. Note that renamed tests count towards both.
de.psdev.devdrawer.DialogStringResourcesTest ‑ given a German locale, when showing the analytics opt-in dialog, then it reads German
de.psdev.devdrawer.DialogStringResourcesTest ‑ given a German locale, when showing the color selection dialog, then it reads German
de.psdev.devdrawer.DialogStringResourcesTest ‑ given a German locale, when showing the delete profile dialog, then it reads German
de.psdev.devdrawer.GermanTranslationTest ‑ given a German locale, when reading yes, then it is translated
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given a profile that is in use by widgets, when deletion is prepared, then InUseError state is returned
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given a profile with no widgets, when deletion is prepared, then Showing state is returned
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given no profiles, when a new profile is created, then it is added and the callback is invoked
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given profiles exist, when viewState is collected, then profiles are emitted
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given two existing profiles, when a new profile is created, then it is named Profile 3
de.psdev.devdrawer.profiles.WidgetProfilesViewModelTest ‑ given two profiles, when one is deleted, then only the other remains in viewState
…
de.psdev.devdrawer.DialogStringResourcesTest ‑ given a German locale, when showing the analytics opt-in card, then it reads German
de.psdev.devdrawer.GermanTranslationTest ‑ given a German locale, when reading undo, then it is translated
de.psdev.devdrawer.WidgetLaunchTest ‑ given no widget id, when resolving, then nothing widget-specific opens
de.psdev.devdrawer.WidgetLaunchTest ‑ given setup opened from outside the app, when leaving it, then the activity closes
de.psdev.devdrawer.WidgetLaunchTest ‑ given setup opened inside the app, when leaving it, then it goes back
de.psdev.devdrawer.WidgetLaunchTest ‑ given the launcher configures a new widget, when resolving, then setup opens and a result is expected
de.psdev.devdrawer.WidgetLaunchTest ‑ given the widget's empty state, when resolving, then setup opens without a result
de.psdev.devdrawer.WidgetLaunchTest ‑ given the widget's settings button, when resolving, then the editor opens
de.psdev.devdrawer.analytics.AnalyticsOptInCardTest ‑ given a pending opt-in, when on the Widgets tab, then the card is shown
de.psdev.devdrawer.analytics.AnalyticsOptInCardTest ‑ given a pending opt-in, when setting up a widget or elsewhere, then nothing interrupts
…

♻️ This comment has been updated with latest results.

hameno and others added 6 commits October 10, 2026 16:45
The app-signature filter picker was a dialog with an unlabelled icon
toggle for system apps. It is now a bottom sheet with a one-line
explanation, a search field (name or package), an "Include system
apps" chip, and recently updated apps first. Tapping an app adds it;
swiping the sheet away cancels.

App icons in Compose now go through one AppIcon composable that
renders at a fixed size: converting at the drawable's own size failed
for drawables without one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Legacy widget colours now migrate to their closest header colour instead of
falling back to amber, and the editor's swatches wrap onto a second row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The blocking opt-in dialog could cover a new widget's setup and kept its
buttons disabled for 2.5 seconds. It is now a dismissable card that only
appears on the Widgets tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Widgets placed with the app's "Add widget" button were saved straight away
with the first profile (or a new empty "Default" one) and never went through
setup. The launcher's confirmation now opens setup for the new widget once the
app is back in front; until then the widget shows its "Choose apps" state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The widget editor lost unsaved changes on back without asking. Changes now
save immediately; the name saves once typing pauses or the editor closes, and
a blank name keeps the saved one.

"Delete widget" only wiped the settings of a widget that stayed on the home
screen. It is replaced by a hint on how to remove the widget there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profile editor needed an explicit Save and asked "Discard changes?" on
back, unlike the widget editor. Filters now save when added or removed, with
an Undo snackbar for removals, and the name saves once typing pauses or the
editor closes. The filter list and its matching apps are now derived from the
same emission so they never disagree.

The app bar actions plumbing had no users left and is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hameno hameno mentioned this pull request Oct 10, 2026
@hameno
hameno added this pull request to stack #71 October 10, 2026 16:59
@hameno
hameno merged commit c81ed96 into master Oct 10, 2026
2 checks passed
@hameno
hameno deleted the feature/ux_redesign branch October 10, 2026 17:00
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.

1 participant