Skip to content

some chores and bug fixes regarding widgets, shared components, and scroll wheel - #561

Merged
Elektron123 merged 41 commits into
you-apps:mainfrom
RisPNG:clock-widgets-and-pickers
Aug 19, 2026
Merged

some chores and bug fixes regarding widgets, shared components, and scroll wheel#561
Elektron123 merged 41 commits into
you-apps:mainfrom
RisPNG:clock-widgets-and-pickers

Conversation

@RisPNG

@RisPNG RisPNG commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

#557 added the widget manager and the RGB colour picker. while going through it on my phone I found the widgets don't actually open the app properly if you place the widget but don't save (by pressing back), so this started as three bug fixes and grew into a pass over the widget settings screen and the wheel pickers it borrows.

Bug 1: Opening the app through the widget

tapping a widget opens a second copy of the app on top of the first, so you press back twice to leave.

TextWidgetProvider wires a tap on every update, then applyDigitalClockWidgetOptions wires another over it with FLAG_ACTIVITY_CLEAR_TASK. two pending intents that differ only by their launch flags are the same pending intent as far as filterEquals is concerned, so FLAG_UPDATE_CURRENT hands back the first one and the clear-task flags never apply. MainActivity is standard, so the second copy stacks.

the provider no longer wires a tap at all, since both text clock widgets already set or clear it themselves from the open-on-click setting.

the analog widget on the other hand never opened the app at all. its tap is wired in onUpdate, but updateAnalogClockWidget builds fresh RemoteViews without one and pushes them, and that runs the moment you pick a clock face. the tap moved into applyAnalogClockWidgetOptions, which both paths call.

Bug 2: Widget initialization issue

the first configuration screen upon placing the widget answers the host's "do I keep this widget?", and it answered no for every exit (pressing back or pressing cancel) except Save. so backing out of a first placement told the host to throw the widget away, while my launcher (vivo) kept drawing it anyway, so I have a widget placed but the state remains uninitialized. the widget looks fine because the host draws the provider's own layout, which is full of TextClock views that tick on their own, but nothing is wired to it and it ignores every tap, which wouldn't allow me to edit the widget or open the app through the widget anymore, so I have to simply remove the widget and create a new one and actually press "save" to first initialize it properly.

to fix this, first time placement now answers yes at its defaults. this also directly fixes the newly widget list not listing uninitialized widgets.

Standardization 1: Widget settings page

I'm sorry but the widget setting page is like its own thing, it doesn't follow the standard of the rest of the app conventions at all. The settings are now sectioned under General, Time, and Date, the same way #555 grouped the app settings.

image

apart from that, the text size dropdown was built for this widget settings alone, which is so unnecessary, so I just standardize that to use the scroll picker dialog.

image

the shared screen also titled itself "Digital clock widget" for the vertical widget, so now they're title cased and separated to "Digital Clock Widget", and "Vertical Clock Widget" in title case, since it's... a title. the analog one is now titled "Analog Clock Widget" to keep consistent with the other 2 widgets' titles.

the text shadow was its own card written into the screen, and the RGB picker was a dialog filed under the settings components while declaring itself a shared one. both are proper shared components now beside the scrolling one, RadioPickerDialog and ColorPickerDialog, which should come in useful later.

component real call sites
ScrollWheel 7 - alarm, timer, the scroll dialog
ScrollPickerDialog 3 - snooze, alarm timeout, widget text size
RadioPickerDialog 1 - text shadow style
ColorPickerDialog 1 - widget custom colour
image

the digital clock shared a clock face icon with the analog one in the list. the list's own subtitles were hardcoded in English in the screen itself, so those are translatable strings now too.

image

The Scroll Wheel Picker (Bug 3: fix insensitive scroll wheel, which fixes by Standardization 2: standardizing the scroll wheel into a shared component)

two things turned up in the scroll wheel;

  1. the value reported on release is computed differently from the one drawn, which disagree on the last page only, so scrolling snooze or timeout to 120 minutes stored 0, fixed,
  2. and the width reserved for three digits is now pre-decided from the widest value the wheel will draw instead of how high it counts, so the text besides the value doesn't jump left and right anymore.

the snooze, timeout, and size dialogs are now using the same scroll wheel component instead of written out separately, so this dialog, the MinutePickerDialog is generalized into ScrollPickerDialog and all three use it, and added a Cancel beside Save in the dialog, for design consistency.

the AM/PM column was another scroll wheel written out separately, so it never got the fling fix behaviour #536 gave the others. it's now using the shared component, told to name two values instead of numbers.

the alarm is also the only screen where these wheels sit on a page that scrolls the way they do, and nothing said which of the two a drag belonged to, so the page could take a share and a wheel settle back where it started. a drag that lands on the wheels stays with them now.

I also renamed ScrollTimePicker to ScrollWheel and moved it beside the other shared components, as picker is used to define the layouts on how to use the ScrollWheel. TimePickerDial became ScrollTimerPicker to match. so now the component usage is more clear, like the following:

ScrollWheel
the individual scroll wheel column component (now sits under presentation/components/)

ScrollAlarmTimePicker
defines the layout using the ScrollWheel component for hour + minute + AM/PM, with the 12/24-hour mapping
ScrollTimerPicker
defines the layout using the ScrollWheel component for hours + minutes + seconds
ScrollPickerDialog
defines the layout in a dialog using the ScrollWheel component for one ScrollWheel and a unit

the picker style option is called "Scroll" instead of "Wheel", because it sits next to "Clock" under the same heading and Clock is the round one. wheel and number_pad were never translated so nothing orphans there, but retitling the analog screen orphans select_clock_face in 27 locales, which lint flags as ExtraTranslation, so I removed those in the same commit.

image

happy to split any of this out if it's easier to review in pieces.

Related PRs

merge this before #556, #556 can wait, I will push a patch (if needed) to #556 after this one is merged in.

it builds on #557, #536, #534 and #542, and follows the sectioning from #555.

RisPNG added 30 commits August 18, 2026 19:30
…s, since the widget config is its only caller and the file sat among the settings components while naming the shared one, and end it with a newline
…, so applying or cancelling a custom widget colour reads the same as saving or cancelling anywhere else in the app rather than as a pair of flat labels
…ce the time size alone offers eighteen values and a dropdown hung off a chip made the reader scroll a popup to reach the ends of a range

The wheel picks by position in the size list rather than by the value itself, so the sizes stay the curated list the widget already exposes and the picker gains a label for turning a position into the text it shows.
…puted a different way, so the last value of a picker that starts above zero saves as itself instead of as zero

The picker labelled each page as its position within the range plus the starting offset but reported the offset added before the wrap, which agreed everywhere except the final page. A snooze or alarm timeout scrolled to 120 minutes therefore displayed 120 and stored 0. The twelve hour alarm picker escaped it only because its meridiem mapping turns a stray zero into the same hour as twelve.
… runs past ninety nine stops padding its small numbers to two digits while leaving its large ones at three

A duration counts minutes rather than naming a position in a clock, so nothing is owed to a fixed width. The clock and timer wheels keep their padding, where a lone digit beside another would read as the wrong time.
…g names its value where every other setting in the app does instead of parking it in a button off to the right
…reen without keeping the changes is a button the way it is in the alarm editor rather than a guess at the back gesture

Cancelling finishes the activity, which already carries the cancelled result it set on the way in, so a widget being placed for the first time is still dropped and one being edited keeps what it had.
…aded, so the two colour rows carry an icon and a title of the same weight instead of a bare line of body text starting further left than its neighbours
…ens the app, since the provider was wiring a tap of its own that the options then tried to replace

Both text clock widgets already set or clear the tap themselves according to the open on click setting, and a pending intent that only differs by its launch flags is the same pending intent to the system, so the provider's flagless one survived and the options' request to clear the task never took effect.
…the task is not answered with the pending intent a running timer's notification made first

Every widget asked for request code zero against a plain intent for the main activity, which is the same request the timer service makes for its notification, and the system hands back whichever of them was built first along with the launch flags it was built with. Keying the request on the widget being drawn keeps them apart.
… saving the face redrew the widget from views that had never been given one

The tap was wired where the widget is updated by the system but not where it is updated after configuring it, and configuring is the first thing that happens to a widget being placed, so an analog clock has never opened the app from the moment it was added.
…already configured, so a widget whose configuration was cancelled can still be opened and finished from the widget settings

A widget added without saving its configuration has no stored settings, and the list read that as the widget not existing, which left the one case that most needs editing as the one case with no way to reach it. Loading it at its defaults shows it as it is drawn. The set of widget ids to ignore went with it, since nothing ever added an id to that set.
…get whose configuration is never saved still keeps time and opens the app rather than only looking as though it does

A widget host draws the provider's own layout while it waits for a configuration, and that layout carries clocks that tick on their own, so a placed widget looks finished long before the app has drawn it once. Nothing is wired to it until the app does, which left a widget whose configuration was cancelled ticking correctly and ignoring every tap. Writing the defaults on the way in settles it, and cancelling still discards whatever was changed on the screen itself.
…the shadow reads like the vibration pattern it resembles rather than a card of its own with the choice folded away behind an expander

The setting was a card built for itself alone, animating its corners between two radii, rotating a chevron and hiding the five styles behind advanced settings, none of which appears anywhere else in the app. A switch that names the current style and opens a picker says the same thing in the shape every other choice of its kind already takes, and the styles say their own names now rather than carrying English in the enum where no translation can reach them.

The step slider the card drew went with it, since nothing else ever drew one.
…configured, so setting up a vertical clock stops calling itself a digital clock

The screen is shared by both text clock widgets and titled itself with the digital one for either of them, since the title was written into the shared activity rather than asked of the widget that opened it.
…nge, so the three settings that describe its date sit together instead of once each among the switches, the sizes and the colours

The screen listed ten settings in the order they were written, which put showing the date at the top, its size five rows down and its colour two rows after that, and asked the reader to hold the whole list in mind to configure one half of a clock. General keeps what belongs to the widget itself, and Time and Date each keep the whole of their own.
…s left without saving, and go on discarding the changes when a widget already configured is left the same way

A host asks the configuration screen whether to keep the widget it is holding, and the screen answered no for every way out but the save button, so leaving the first configuration told the host to throw the widget away. It is the placement being answered the first time and only the changes on the screen every time after, so the first says yes at its defaults and the rest still say no.
…ngth, an alarm timeout or a widget text size alone is a button rather than a tap outside the dialog

Every one of them already dropped the scrolled value when dismissed, but said so only by having no way to agree, which asked the reader to know that tapping away was safe.
…e dialog, since the three were the same wheel between the same two buttons written out twice

The minutes dialog and the size dialog had already converged on a title, a wheel, a unit beside it and save against cancel, and adding the cancel button meant writing the same pair into both. The shared one takes the range and the words rather than assuming minutes, so a duration passes the minutes it counts and a text size passes positions in the sizes it offers.
…aw, so the unit beside a widget text size stops stepping aside when the sizes reach three digits

The width was reserved by asking how high the wheel counts, which answered for the wheel that counts the minutes it shows but not for the one that counts positions in a list of sizes and draws the size at each. Asking the labels themselves answers for both, and answers the same as before everywhere the two questions agreed.
…and end it with cancel beside save, so all three widgets are set up on a screen that says the same things in the same places

The screen called itself after the one choice it offers rather than the widget it configures, and offered only a way to agree, while the two text clock widgets already named themselves and answered both ways.
…de it do, so the one column of the alarm picker that was left on the default fling stops behaving unlike the two it sits next to

The commit that centred the snap and made the highlighted value the selected one gave the shared wheel a fling that may travel the whole range and gave this one only the centred snap, so it kept the default that settles within a page of where it started.
…go leaves the value that was highlighted rather than the one the page scrolled back to

The alarm is the only screen where these wheels sit on a page that scrolls the way they do, and nothing said which of the two a drag belonged to, so the page took a share of it often enough that a wheel could settle back where it started. The timer and the dialogs never had to answer this because nothing behind their wheels moves.
…ry part of this picker is called what the rest of them are called

The wheel is a scroll time picker, the alarm's row of them a scroll alarm time picker and the dialog around one a scroll picker dialog, while the timer's row alone was a dial, which is the one thing none of them draw.
…e picker built for those two was the shared wheel written out a second time

It kept its own pager over two pages, its own centred snap, its own haptic tick and its own highlight, all of which the shared wheel already does, and the numbers agreed exactly: a wheel of two values starting at the chosen one lands on the same page from either. Naming the two values is all the wheel needed to be told.
…num it came to sit above does not use the API it opts into
…arts, since it picks one value of anything rather than a time and the timer is only one of the four places that draw it

Everything built out of it is a picker, being what a reader picks a time or a length or a size with, while this is the column those pickers are made of, and the app already calls this style a wheel where it offers it. It sat in the timer's own folder from when the timer was its only caller, which left the alarm, the widget settings and the dialog reaching across a feature they have nothing to do with.
…ing picker style what it does, so the widget list stops offering two clock faces for different widgets and the style beside Clock stops sounding round as well

The digital and analog widgets both carried a clock face, which named the analog one and said nothing about the other, while the vertical widget already showed the stacked cards it draws, so a single card says the same thing for the digital one. The scrolling style sat next to the clock style under the same heading while calling itself a wheel, which is the shape of the option beside it rather than the one it offers.
RisPNG added 11 commits August 19, 2026 10:15
…een no longer shows, since a translation without a string to translate is flagged as an extra one

The screen names the widget it configures now, so the twenty seven locales that had translated its old title are left holding a string nothing asks for.
…reens name their widget the way a title names a thing rather than the way a sentence starts
…ow dialog be answered rather than answered by touching an option

The picker dialogs the app already had name the thing they select, while the ones the widget settings opened were titled after the setting behind them or after the colour model they happen to use, so a custom colour, a date size and a time size now say they are being selected the way a snooze length and a vibration pattern do.

The shadow dialog applied a style the moment one was touched, which read as a menu rather than a choice and left its cancel button with nothing to cancel. It holds the touched style now and applies it on save, beside a cancel that leaves the widget as it was.
…ch one it uses, and label the shadow style as a style

The time zone row explained what turning it on would do and went on explaining it after a zone had been chosen, so the one thing it could usefully say, which zone the widget follows, was the one thing it never did. It says the zone once there is one, and keeps the explanation for when there is not.

The shadow row named a style without saying it was one, which reads oddly beside a preset called Off.
…the line under each widget was written into the screen rather than into the strings

The three lines it can show were spelled out in English where no translation could reach them, and named a time zone in a different case and a different spelling from the setting that chooses one.
…ur dialog after the colour it picks, so the widget settings open dialogs the app owns rather than dialogs written into the screen

The list of styles was written into the settings screen, and the colour dialog was named after the three sliders it happens to use and agreed to a colour with Apply while everything else in the app agrees with Save. Both are components now, beside the scrolling one, and both answer with save against cancel.
…to rather than the one it was scrolled from

A release too slow to fling is decided by how far the page was dragged against a threshold, which was half a row, so a drag that had already moved the highlight could still be read as not far enough and settle back where it began. A fifth of a row settles forward instead, which is the value the reader was looking at when they let go.
… left it, while letting a flick coast as far as it ever did

Where a release lands was decided entirely from how fast and how far the finger moved, and a slow flick can work out a resting place a row behind the one under the highlight, which is the value the reader is looking at and the one they mean. The resting place is now held to that row at the least, so a flick still travels the whole range and a release never hands back the row it came from.

The threshold that decides a release with no flick in it goes back to half a row, since it agrees with where the highlight moves, and biasing it lower only traded one wrong row for another.
…el, so a release settles where it is instead of coasting a row back and being pulled forward again

Holding the resting place to the highlighted row fixed which value a release chooses but not what it looks like getting there, because a wheel coasts on the speed it was let go with before it settles anywhere, and a finger that slows to a stop often leaves pointing back the way it came. That coast ran a row backwards and the resting place then pulled it forward, which reads as the wheel choosing wrongly and correcting itself.

A release whose speed points against the way the wheel was just turned is treated as no speed at all, so there is nothing to coast backwards and the wheel simply settles. A release that keeps going the way it was turned still carries every bit of its speed.
…k to coast as it always did

A wheel worked out where to come to rest from how fast and how far the finger moved, which is the right question for a flick and the wrong one for a drag. Someone dragging a wheel a row at a time is reading it, and the row they stop on is the answer they mean, whatever the numbers behind the gesture work out to.

A release too slow to be a flick now carries no speed at all and the wheel simply settles on the row it is showing. A flick still carries its speed and lands wherever it lands, which is all a flick is asked to do.
@Elektron123
Elektron123 merged commit c8055cb into you-apps:main Aug 19, 2026
@Elektron123

Copy link
Copy Markdown
Collaborator

tysm :)

@RisPNG
RisPNG deleted the clock-widgets-and-pickers branch August 19, 2026 20:36
Elektron123 added a commit that referenced this pull request Aug 21, 2026
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