Commit graph

324 commits

Author SHA1 Message Date
Johannes Millan
2ae4666955 fix(android): handle WebView init failures safely 2026-05-11 20:32:12 +02:00
Johannes Millan
348dc47e3e fix(android): handle foreground service failures 2026-05-11 14:10:51 +02:00
Johannes Millan
2b06d0b886 18.5.0 2026-05-09 20:27:05 +02:00
Johannes Millan
eb1131048f feat(android): surface WebView block detection source for triage
Show which detection path produced the BLOCK (USER_AGENT, PACKAGE,
UNKNOWN, or INIT_FAILURE) on the WebView block screen so user-submitted
screenshots for issue #7518 are actionable. Tag forced non-version
blocks with INIT_FAILURE so they no longer falsely report a
version-detection source. Adds tests for parseMajorVersion(String?).
2026-05-08 22:30:59 +02:00
Johannes Millan
faa45fd280 18.4.4 2026-05-02 20:20:10 +02:00
johannesjo
30d7e7d717 18.4.3 2026-05-02 00:03:46 +02:00
johannesjo
1b4d0c1710 18.4.2 2026-05-01 23:07:19 +02:00
Johannes Millan
ff0f6d2f82 18.4.1 2026-05-01 21:59:17 +02:00
Johannes Millan
4f15e66f9d 18.4.0 2026-05-01 20:58:26 +02:00
Johannes Millan
7c5b58ecf6 fix(android): add WebView block recovery paths
Builds on the prior authoritative-vs-fallback fix with three layered
recovery mechanisms so users hit by a false BLOCK are never locked out
of their data:

- Last-known-good auto-recovery: persist the highest WebView version
  that has ever loaded the app on this device. A later transient
  mis-read that drops below MIN_CHROMIUM_VERSION is downgraded to WARN.
- "Try anyway" override: third button on the block screen opens an
  AlertDialog with an explicit risk acknowledgment (crashes, render
  failures, possible data loss). Confirming persists an override and
  relaunches the app. Hardened against tapjacking via
  filterTouchesWhenObscured on both the activity and dialog window.
- Override auto-clears once a healthy version is detected, so a future
  genuine block is not silently bypassed.

Also tightens the UA regex (drops the misleading Safari Version/X
fallback that always reads "4.0" and would falsely block) and adds
diagnostic logging gated by Log.isLoggable for field debugging.

Tests: 12 unit tests covering statusForVersion branches, all
applyOverrides paths, and parseMajorVersion edge cases.

Refs #7229
2026-04-29 16:17:55 +02:00
Johannes Millan
10257fc21a fix(android): avoid false WebView version lockout 2026-04-29 16:17:55 +02:00
Johannes Millan
81560f97af 18.3.0 2026-04-25 22:42:18 +02:00
Johannes Millan
3d40f56aae 18.2.8 2026-04-22 16:19:14 +02:00
Johannes Millan
6e6fef101c 18.2.7 2026-04-21 22:37:49 +02:00
Johannes Millan
54a0931f10 18.2.6 2026-04-21 22:27:37 +02:00
Johannes Millan
ff0314e52d 18.2.5 2026-04-20 18:39:43 +02:00
Johannes Millan
01e30b9c7e
Feat/to me it looks like there are lots of 60dd04 (#7280)
* fix(issue): prevent crash from orphan issueProviderId (#7135)

The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.

Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.

* fix(focus-mode): sync tray countdown with in-app timer during breaks

Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.

Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.

Fixes #7278

* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68

Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.

Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3

* fix: restore i18n, UI, and docs work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.

Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior

* fix(electron): restore electron + e2e work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)

Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
  (re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
  (setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
  (electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
  (SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
  (this is literally the fix the bad commit's message claimed to add)

Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
  checks from d64014d086 (later than c558bcab5e) while restoring the
  goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
  clearStaleLevelDbLocks from theirs, fs from ours).

* fix(sync): restore sync-core work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.

This PR restores only the pieces that are genuinely still missing:

- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
  operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
  stale-idb-locks.ts piece was restored in the prior commit)

Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
  comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)

Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
2026-04-20 12:04:38 +02:00
Johannes Millan
e113cd8d21 18.2.4 2026-04-19 19:21:50 +02:00
Johannes Millan
404c68127a 18.2.3 2026-04-18 23:24:19 +02:00
Johannes Millan
47c52dfa87 18.2.2 2026-04-17 22:51:53 +02:00
Johannes Millan
b9d6dc6d86
test(e2e): update Show/hide task panel button label (#7260)
* test(e2e): update Show/hide task panel button label

The TOGGLE_DETAIL_PANEL translation was renamed from "Show/Hide
additional info" to "Show/hide task panel" in dd789d2, but e2e
selectors still referenced the old string, causing every test that
opens the task detail panel to time out.

https://claude.ai/code/session_011mX4Pr24S42svmA5j7fRux

* 18.2.1

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 22:19:22 +02:00
Johannes Millan
9b7d34100e 18.2.0 2026-04-17 19:48:55 +02:00
Johannes Millan
6171a98646 refactor(android): route specialUse FGS through ServiceCompat helper
Both FocusModeForegroundService and TrackingForegroundService declare
android:foregroundServiceType="specialUse" in the manifest. The 2-arg
startForeground() overload already falls back to the manifest type on
Android 14+, but passing the type explicitly via ServiceCompat is
Google's documented best practice and removes OEM-dependent fallback
ambiguity.

Extract the Build-gated call into a Service.startForegroundSpecialUse
extension in ForegroundServiceExt.kt so a future third FGS picks up
the API-version gating automatically. Callers shrink to one-liners.
2026-04-17 19:45:20 +02:00
Johannes Millan
f81858b488 fix(android): guard broken WebView package probe
WebView.getCurrentWebViewPackage() is documented to return null on
devices without an installed provider, but in practice it can throw
AndroidRuntimeException, MissingWebViewPackageException, or NPE on
devices with a corrupt or updating provider. An unhandled throw there
short-circuits WebViewCompatibilityChecker.evaluate() before
FullscreenActivity's recovery path (added in 5a5c3bca89) has a chance
to run, re-crashing the app on launch.

Wrap the call in a narrow catch for RuntimeException so the documented
throwables fall through to the PackageManager lookup while Error
subclasses still propagate. Log at debug level so broken-provider
telemetry survives.
2026-04-17 19:45:10 +02:00
Johannes Millan
a2f45b0d69 refactor(android): narrow broad catch clauses so unknown failures surface in Play Console
The recent WebView, FGS, and JS-dialog guards each caught Exception,
which hid every thrown type behind a log line. Play Console only
surfaces uncaught throws, so a future regression inside any of these
paths would be silently swallowed.

Narrow to the known recoverable types:
- FullscreenActivity.initWebView: AndroidRuntimeException (incl.
  MissingWebViewPackageException) + IllegalStateException.
- FullscreenActivity.onJsAlert/onJsConfirm: WindowManager.BadTokenException
  + IllegalStateException; JsResult.cancel() still fires via the
  handled flag so the WebView JS thread never deadlocks.
- TrackingForegroundService / FocusModeForegroundService
  ensureForegroundNotification: IllegalArgumentException (Samsung One UI
  / Android 14 blank-title case) + SecurityException. Anything else,
  including ForegroundServiceStartNotAllowedException, now propagates.
2026-04-17 18:13:49 +02:00
Johannes Millan
5a5c3bca89 fix(android): recover from WebView init crash via WebViewBlockActivity
Observed on Play Console: AndroidRuntimeException thrown from
WebViewFactory.getProvider during FullscreenActivity.initWebView on
devices where the system WebView package itself is broken (seen with a
WebView Beta build throwing "Already registered a list of actions in
this process" from WebViewChromiumFactoryProvider.<init>). The activity
had no recovery - the constructor threw and the app died at launch.

Mirror the existing CapacitorMainActivity pattern: catch around the
WebHelper().instanceView(this) call, and on failure present the existing
WebViewBlockActivity using compatibility.copy(status = BLOCK) so the
block screen keeps the detected provider/version, then finish().

Also harden lifecycle callbacks that dereference the lateinit webView /
javaScriptInterface fields so the new failure path can't trigger an
UninitializedPropertyAccessException via OEM-specific lifecycle quirks:
::isInitialized guards in onSaveInstanceState, onRestoreInstanceState,
onBackPressed, and callJSInterfaceFunctionIfExists (the last matching
the existing guard in CapacitorMainActivity).
2026-04-17 18:13:02 +02:00
Johannes Millan
b9f91a5327 fix(android): guard FGS contract unconditionally in onStartCommand
Every Context.startForegroundService() call creates a per-call FGS token
that must be answered with startForeground() within the system timeout,
even if the service is already a foreground service. Earlier fixes closed
specific branches one at a time and left gaps — most notably ACTION_START
with null taskId while already tracking, which returned without calling
startForeground().

Satisfy the contract once at the top of onStartCommand in both
TrackingForegroundService and FocusModeForegroundService, so every branch
below (known and future) already has the token answered. Add
stopForegroundAndSelf() on the previously silent no-op branches
(ACTION_UPDATE / ACTION_STOP when idle) so the guard does not leak the
service as foreground with a placeholder notification.

The placeholder notification carries a content title — Samsung One UI can
render title-less notifications as blank and a few Android 14 builds throw
IllegalArgumentException from startForeground() without one, which would
be swallowed by the guard's try/catch and re-trigger the very timeout we
are preventing. Matches the crash's heavy Samsung/Lenovo-tablet bias.
2026-04-17 18:13:02 +02:00
Johannes Millan
367342edd6 fix(android): guard webchrome dialogs against activity teardown
Prevents WindowManager$BadTokenException in FullscreenActivity.onJsConfirm
and onJsAlert when JS fires alert()/confirm() after the activity starts
finishing. Also ensures JsResult is always released so the WebView JS
thread never deadlocks on lifecycle-driven dismissal.
2026-04-17 18:13:02 +02:00
Johannes Millan
5787437a9a
fix(reminder): restore Android alarms after app update (#7254)
Android clears all AlarmManager alarms (and may cancel WorkManager jobs)
whenever the app's APK is replaced — i.e. on every Play Store auto-update
or manual reinstall. BootReceiver already knows how to re-register
everything from ReminderAlarmStore and re-enqueue SyncReminderWorker, but
its intent-filter only listened for BOOT_COMPLETED, so after an update
reminders stayed silently unregistered until the user next opened the app.

Add MY_PACKAGE_REPLACED to the manifest and broaden the receiver's action
guard so alarm restoration runs on both events. No new permissions or
helpers required — MY_PACKAGE_REPLACED is a protected broadcast delivered
only by the system to the owning app.

Refs #6832

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 17:39:59 +02:00
Johannes Millan
0e9218bd68 fix(sync): handle data validation errors and improve client ID management
- Separate JsonParseError and SyncDataCorruptedError handlers in sync wrapper
- Handle corrupted remote data gracefully instead of throwing
- Add getOrGenerateClientId() as unified entry point, eliminating dual injection
  of ClientIdService + CLIENT_ID_PROVIDER in snapshot-upload, file-based-encryption,
  and sync-hydration services
- Use crypto.getRandomValues() instead of Math.random() for client ID generation
- Warn user when stored client ID is invalid and must be regenerated
- Fix flaky e2e supersync tests (premature waitForURL match on daily-summary URL)
2026-04-16 17:41:39 +02:00
Johannes Millan
ea1ef16307 fix(android): support session-only SAF permissions on OEM devices
Some OEM ROMs throw SecurityException when takePersistableUriPermission is
called, even with FLAG_GRANT_PERSISTABLE_URI_PERMISSION requested. Instead of
rejecting the call, fall through and return the URI — it remains valid for the
session. Also fix checkUriPermission to use context.checkUriPermission() which
detects both persistent grants and active session grants, so isReady() no longer
clears a working (session-only) SAF URI on those devices.
2026-04-14 22:09:02 +02:00
Johannes Millan
1f22fe685d 18.1.3 2026-04-01 21:27:44 +02:00
Johannes Millan
3a216b88e1 18.1.2 2026-04-01 21:25:48 +02:00
Johannes Millan
3e0f26aa5d 18.1.1 2026-04-01 21:17:20 +02:00
Johannes Millan
d32f7037a3 fix(sync): preserve own vector clock counter across full-state op resets
When a full-state op (SYNC_IMPORT/BACKUP_IMPORT) arrived from another
client, mergeRemoteOpClocks reset the local clock to a "minimal" form
but only preserved the current client's counter from the incoming op's
clock. If the current client had issued ops (e.g. GLOBAL_CONFIG) not
reflected in the incoming full-state op's clock, its counter was dropped,
causing subsequent ops to reuse the same counter value. Downstream clients
then saw these ops as EQUAL (duplicate) and skipped them silently.

Fix: take max(mergedClock[clientId], currentClock[clientId]) when
rebuilding the clock after a full-state op reset.

Also add __SP_E2E_BLOCK_WS_DOWNLOAD flag to WsTriggeredDownloadService
to allow E2E tests to block automatic WS-triggered downloads during
concurrent conflict scenarios.

Fix archive conflict test by blocking WS downloads on Client A during
the concurrent edit phase so it doesn't auto-receive B's rename via
WebSocket before archiving (restoring the intended conflict scenario).

Fix LWW singleton test to assert convergence rather than specific winner.
Fix renameTask helper to avoid Playwright/Angular re-render races.
Fix shepherd.js import paths that broke the Angular dev server build.
2026-04-01 15:41:13 +02:00
Johannes Millan
db3eaa6f81 18.1.2 2026-03-31 22:31:59 +02:00
Johannes Millan
49f63e8174 18.1.1 2026-03-31 22:31:40 +02:00
Johannes Millan
edb102534e
feat(android): improve task title from share intent using EXTRA_SUBJECT (#7052)
* feat(android): improve task title from share intent using EXTRA_SUBJECT

Browsers and many apps send the page title via Intent.EXTRA_SUBJECT, which
was previously ignored. Now the share handler uses subject > title > content
to build a meaningful task title instead of the generic "Check: Shared Content".
For links without metadata, a readable URL (domain + path) is used instead.

https://claude.ai/code/session_01LzFepWFjLKQSWKZn4iXEpS

* chore: update package-lock.json after npm install

https://claude.ai/code/session_01LzFepWFjLKQSWKZn4iXEpS

* refactor(android): simplify _buildTaskTitle and fix double spaces in URL

Deduplicate the subject/title preference logic and collapse multiple
spaces in _readableUrl output caused by leading slashes in pathnames.

https://claude.ai/code/session_01LzFepWFjLKQSWKZn4iXEpS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-31 13:12:34 +02:00
Johannes Millan
85a8426213 18.1.0 2026-03-29 20:31:02 +02:00
Johannes Millan
136bb51ea0 fix(android): prevent crashes from null bridge and foreground service contract violations
Guard against null bridge/webView after onCreate by showing WebViewBlockActivity
with actionable guidance instead of crashing. Satisfy Android's foreground service
contract in TrackingForegroundService and FocusModeForegroundService by posting a
minimal notification before stopping when started without a valid action. Handle
null taskId on ACTION_START without killing an active tracking session.
2026-03-29 19:57:10 +02:00
Johannes Millan
9f8cf0a3d0 fix(oauth): use platform-specific redirect URIs and iOS client ID
Google rejects custom scheme redirect URIs that don't match the
platform's app identifier. Use the Android applicationId
(com.superproductivity.superproductivity) on Android and the iOS
bundle ID (com.super-productivity.app) on iOS, with single-slash
URI format per Google's documentation.

- Add iosClientId to OAuthFlowConfig and plugin API types
- Add iOS OAuth client ID to Google Calendar plugin
- Select client ID per platform (Android/iOS/Desktop) in bridge service
- Add Android package name intent filter in AndroidManifest
- Accept both URI schemes in OAuth callback handler
- Fix redirect handler to use IS_NATIVE_PLATFORM consistently
2026-03-27 17:33:38 +01:00
johannesjo
4826bc265c 18.0.2 2026-03-26 21:24:24 +01:00
johannesjo
25e31656d1 18.0.1 2026-03-26 21:09:48 +01:00
Johannes Millan
4eca106653 18.0.0 2026-03-26 19:58:29 +01:00
Johannes Millan
142107b12e build: update capacitor and package-lock.json 2026-03-26 17:43:04 +01:00
Johannes Millan
fe0edaacd0 18.0.0-rc.0 2026-03-24 21:04:33 +01:00
Johannes Millan
bbf3c741a7 feat(tasks): lock drag-and-drop Y-axis on small screens and throttle tree drag
Lock cdkDragLockAxis to Y on small screens (<600px) for task-list and
planner components, matching the breakpoint where planner switches to
single-column and bottom nav appears. Nav menu tree always locks Y.
Uses reactive LayoutService.isXs() so orientation changes work correctly.

Throttle tree-dnd onDragMoved with requestAnimationFrame to reduce
layout thrashing from getBoundingClientRect/elementFromPoint calls.
2026-03-24 16:25:42 +01:00
Johannes Millan
f20ebea583 fix(android): prevent ForegroundServiceDidNotStartInTimeException and bridge NPE
Add safeStopSelf() to TrackingForegroundService and FocusModeForegroundService
that calls startForeground() before stopSelf() in fallback branches (missing
task ID, stop when not running, null/unknown action). Android requires
startForeground() within 10s of startForegroundService().

Replace ambiguous NullPointerException in CapacitorMainActivity.onCreate with
a descriptive IllegalStateException when Capacitor bridge fails to initialize.
Add safe-call operators in onSaveInstanceState/onRestoreInstanceState.
2026-03-24 16:25:42 +01:00
Johannes Millan
02bc3e88e3 feat(two-way-sync): add plugin OAuth, two-way field sync, and remote issue deletion
Add OAuth support for plugins with PKCE, token persistence in local-only
IndexedDB, and Electron/web redirect handling. Extend two-way sync with
dueDay, dueWithTime, and timeEstimate field mappings including mutually
exclusive field clearing. Support remote issue deletion on task delete
and remote deletion detection during polling.

Add Google Calendar plugin (disabled from bundled builds pending legal
review) as a development reference for the plugin OAuth and two-way
sync APIs.

Additional fixes:
- Restore accidentally deleted focus-mode translation keys
- Remove full Task[] from deleteTasks action to prevent op-log bloat
- Add dueDay/deadlineDay format validation guards (#6908)
- Fix Android reminder alarm cancel intent matching
- Validate dueDay format to prevent false overdue from corrupted data
- Fix PKCE test expectation after utility consolidation
2026-03-22 13:02:41 +01:00
Johannes Millan
da41ad7603 fix(android): match cancel intent action to schedule intent for reminder alarms
cancelReminder() created a PendingIntent without setting the action,
while scheduleReminder() set ACTION_SHOW_REMINDER. Android's
Intent.filterEquals() treats null != "action" so alarmManager.cancel()
silently failed to find the scheduled alarm.
2026-03-22 10:10:19 +01:00