Feature/widgets#16
Merged
Merged
Conversation
Owner
Markel15
commented
May 19, 2026
- Fix a bug with the dates and time zones not calculating correctly: Some tasks will be moved now in the calendar 1 day (in the past if your UTC time is negative and in the future if your UTC time is positive). This will only happen if you created a task from the calendar module. Bug Timezone issue #13
- Fixed a bug which caused some overlaps in different devices depending on the notch location. Bug Flow tab overlaps the Android status bar (and camera notch) #15
- Added a widget for basic habits. Resizable and dynamic color compatible. (Tested on Android 12,15,16 and 17 other versions should work too).
- Improved micro interaction with the numeric habit cards (the button has movement now to improve the experience) .
- Recovered empty state view when there's nothing to show in the flow module.
New widget for the boolean habits, first version, still improvements to do and bug fixes. The main important thing is the approach, looks a bit odd having the preferences and Room at the same time, but Glance widgets need to have the information in those preferences to be updated instantly. I tried an approach having Room as a single source of truth (preferences only to store the glance ID) but wouldn't work properly. I think the main problem was that updating the database wasn't instant and when the re-draw of the widget had to occur, it wasn't updated yet to show it.
This is mainly to solve the problem of the widget not updating at midnight. With this approach, the widget should be updated after some time if the day passed (new day, and new state ).
Make it compatible with dynamic color, and improve overall usability and looks. Add a guardrail for a possible race condition when creating a widget and the glance id is not set up yet, just added a try catch block, the solution will be automatic in the next onUpdate.
Single source of truth Room DB and flows, update the widget when updating the DB. This refactor allows the synchronization when updating the from the app.
This is mainly for the edge case when the widget session is inactive, an update from the app is made and the widget isn't updated automatically. The update will be every 30 minutes. If you see the widget before the update, won't be updated visually. I think this case the trade-off is worth it a solution could be to update the widget every time the habit is updated, but I doubt that is worth for this case. If you have a widget like this you'll normally update it from the widget, and if you update it from the app you probably won't mind that the widget is not updated visually until 30 minutes or so. I may be wrong but this is my point of view.
Centralize all the logic of the Icon Mapper in a single file in the design system module. Centralized the drawable icons too, this deletes duplicated code and drawables. For future updates when adding more icons this eases the process.
The code was very confusing using in some parts local time to store the dates in the DB and in other parts UTC time. This was added to other problems that the reference time to calculate due dates used local time. Now every date is normalized with UTC time and uses UTC time to check as well the due dates and reminder dates. Local time is only used to compare due dates, reminder times, etc. This way, everything is stored in UTC time. The only problem is that the tasks created from the calendar screen used local time to store the due date, I could add a migration but, I think very few people creates dates this way and probably won't be a big problem, some due dates will be moved depending on your local time to match UTC time, but this will cause a movement of 1 day maximum. From now on every task should be normalized at that time and working well (I hope).
The tests of the calendar module used local time to check dates. Solved normalizing the conversion to use UTC time too
I changed the hardcoded value of 30 dp of padding to use the status Bar padding, this should change between different devices, but even if it makes every app padding different should be better because some devices need special paddings.
I recovered the EmptyStateView.kt which showed some placeholder when the flow module was empty. I thought this was in use, but I think it disappeared when I put together the tasks with the ideas and checklists.
This updates the widget allowing it to be resizable and now should work well independent of the launcher. Before this update, the launchers decided the widget size and added extra padding. Now the exact size of the widget is used to calculate everything related and maintain the proportions.
The main problem was that when the widget session died, making a click on it forced and update of the DB and re-drawing of the widget itself. In that process collecting the data from the DB during a brief period of time the initial value was null and that caused to show the placeholder before updating the value obtained form the DB (flickering). Now this should be fixed by adding the latest recorded values adn showing them avoiding the flickering (because now the habit isn't null)
Since I am using a custom shape, the ripple wasn't matching the shape, I tried some things and after investigating and not finding anything native, I found that the API offered the rippleOverride parameter expecting a drawable resource. So I created a transparent drawable and looks goog. This is the only thing useful I found in the documentation regarding the ripple,so I hope this is the best solution.
…ions after process restart The previous fix for the flickering issue replaced the reactive currentState<Preferences>() call inside provideContent with a one-shot getAppWidgetState() call outside of it. While this successfully eliminated the flickering on devices where the DataStore was already available, it introduced a regression on Android 12 (I could only test it in that version, Android 16 and Android 17 (preview) worked well). Root cause: On Android 12, the system is more aggressive about killing widget processes (That is my conclusion, but I couldn't find anything in the documentation about it). When the process is restarted (e.g. user taps the widget), getAppWidgetState() may return empty preferences because the underlying DataStore has not been initialized yet. Since the habitId was captured as an immutable parameter (-1), the widget permanently showed the \"?\" placeholder with no way to recover. Fix: Restore the reactive currentState<Preferences>() observation inside HabitWidgetContent (which runs in a @composable context) while keeping the preloading logic in provideGlance for anti-flickering. The habitId is now resolved as: val habitId = currentState<Preferences>()[KEY_HABIT_ID] ?: preloadedHabitId This provides two recovery paths: - Normal case (DataStore available): preloadedHabitId is correct on the first frame, so there is no flickering. - Android 12 recovery case: preloadedHabitId is -1, but currentState reactively updates when the DataStore initializes, triggering a recomposition with the correct habitId. When habitId differs from preloadedHabitId (reactive recovery), the stale preloaded initial values are discarded so the Flow can emit fresh data for the correct habit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.