super-productivity/android
Johannes Millan c18055a341
fix(android): stop the widget labelling a stale list as Today (#9098) (#9118)
The widget renders a pre-computed snapshot Angular pushes into `widget_data`;
the blob carried no expiry, so a process that stayed dead across midnight kept
rendering yesterday's tasks under a hardcoded "Today" header, indefinitely.

Native cannot recompute the list. TODAY_TAG membership is virtual, today's
repeat instances do not exist as entities until TaskDueEffects materializes
them on a day change, and overdue carry-over runs there too — so there is no
persisted field a native filter could read that would give the right answer
even in principle. Only running the app can produce today's list.

So make the widget honest instead of wrong: Angular stamps the snapshot with
`validUntil`, the instant it stops being today (start-of-next-day offset
included), and `dayStr` for the label. Native's whole verdict is
`WidgetData.headerFor` → `now >= validUntil`; when stale it renders the
snapshot's own date instead of "Today". The list stays visible and useful, it
just stops claiming to be today's. A stale snapshot whose day cannot be read
says "Outdated" rather than falling back to the very lie this fixes.

Shipping the boundary rather than its inputs keeps the app's calendar rules in
one language. The iOS port (#8950) can consume `validUntil` unchanged instead
of mirroring getDbDateStr semantics into Swift, where a non-Gregorian default
locale would silently misread the day. The verdict is a pure function so it is
unit-testable: this project has no Robolectric, and a decision left inside the
provider could ship inverted and green.

Both refresh paths rebuild the header. A push can change the day the blob
describes; a tap cannot, but it re-renders at a later `now` than the last
verdict was computed at — and it is the one interaction that reaches our code
while the app is dead, so a tap on a new day must not redraw rows under a
"Today" header. Also fixed: onUpdate re-registered the adapter with an
unchanged intent, which does not re-invoke the factory's onDataSetChanged(),
so the periodic update rendered whatever rows the adapter last built.

`v` stays 1: the fields are additive, and parse() returns empty for any other
version, so a bump would blank the widget of every install until next opened.
A pre-#9098 blob has no `validUntil` and is never reported stale — unknown must
not read as expired.

Known bounds, documented in the plan rather than papered over: the label flips
only on a push, a tap, or the inexact Doze-deferred 30-min periodic update, and
the launcher paints cached views on unlock — so the lie is bounded, not
eliminated. Force-stop is not a gap: the system masks a stopped package's
widget entirely, so the reported symptom can only occur in the Doze band. An
exact alarm at `validUntil` would close the rest; deferred on cost/scope, not
because it would not work.

Verified: 25/25 Kotlin across 6 timezones incl. midnight-gap zones (Santiago,
Apia); 203/203 Angular in both timezones CI runs. Boundary math asserted
against the real getDbDateStr rollover. Sabotage-measured, not assumed:
inverting the verdict fails 5 tests, >=→> fails 4, dropping the validUntil
guard fails 3, Locale.US→getDefault fails 1, dropping the day round-trip
fails 1.
2026-07-17 16:06:06 +02:00
..
.idea chore(android): untrack .idea/gradle.xml 2026-04-17 18:13:02 +02:00
app fix(android): stop the widget labelling a stale list as Today (#9098) (#9118) 2026-07-17 16:06:06 +02:00
fastlane/metadata/android/en-US/changelogs 18.14.0 2026-07-10 17:23:35 +02:00
gradle/wrapper feat: migrate to capacitor 8 2026-05-23 20:42:00 +02:00
.gitignore build(android): simplify WebViewRequestHandler 2024-12-14 11:39:20 +01:00
ALWAYS_TAG_RELEASES_FOR_FDROID ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
build.gradle feat: migrate to capacitor 8 2026-05-23 20:42:00 +02:00
capacitor.settings.gradle feat(android): migrate edge-to-edge to built-in SystemBars (#8543) 2026-06-22 16:07:06 +02:00
gradle.properties ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
gradlew ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
gradlew.bat ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
LICENSE ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
README.md build: update links to match our new organization 2026-01-05 14:45:06 +01:00
README_OFFLINE.md build: update links to match our new organization 2026-01-05 14:45:06 +01:00
README_ONLINE.md ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
settings.gradle ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00

super-productivity-android

Android App for Super Productivity (https://super-productivity.com/).

I am not an Android developer, so help would be very welcome!!

New Connectivity-Free Mode is Here!

This feature was added on October 7, 2024. See Pull Request #57.

You can now use the core features of the app without an internet connection, offering a smoother and more reliable experience. We've made several key updates to enhance usability:

  • Connectivity-Free Mode Support: Enjoy uninterrupted access to the app's main features without needing a network connection. You can still sync with WebDAV, Dropbox, or choose to work entirely offline without any network access.
  • Online-Only Mode (Compatibility Mode): For users who prefer or need the traditional experience, the app still supports the original mode, which requires an internet connection for functionality.
  • CORS Issues Resolved: Fixed cross-origin resource sharing (CORS) problems, especially for WebDAV sync, ensuring secure and smooth synchronisation with local or hosted resources.
  • Enhanced Security: Strengthened data protection to keep your information secure, even when offline.
  • Seamless Upgrade: Existing users can continue using the app in Online-Only Mode (Compatibility Mode) without any disruptions, while new users can immediately enjoy the benefits of Connectivity-Free Mode. Future updates will also include a smooth migration plan for everyone.

Update now to enjoy these exciting new features and improvements!

Launch Modes

The app supports two launch modes:

  1. Connectivity-Free Mode (Recommended) Use the app without an internet connection.
  2. Online-Only Mode (Compatibility Mode) Requires an internet connection to connect to production, local development, or self-hosted servers.

Configuring Launch Mode

To configure the launch mode, adjust the LAUNCH_MODE setting in the app_config.properties file:

  • 0: Default behaviour (read from SharedPreferences)
  • 1: Force Online-Only Mode (Compatibility Mode)
  • 2: Force Connectivity-Free Mode (Recommended)

Recommendation: Set LAUNCH_MODE to 2 for Connectivity-Free Mode.

How to Adjust LAUNCH_MODE

  1. Locate the app_config.properties file in the project's root directory.
  2. Open the file in a text editor.
  3. Find the LAUNCH_MODE setting and set it to your desired mode (0, 1, or 2).
LAUNCH_MODE=2

Important: The app_config.properties file is intended for local modifications only. DO NOT COMMIT this file unless you are sure of what you are doing.

Detailed Configuration Guides