Skip to content

Tags: astraedus/nudge

Tags

v1.9.3

Toggle v1.9.3's commit message
fix: keyboard/paste-popup no longer re-triggers the delay (#5)

After completing a delay, PassthroughManager shields the app from
re-blocking until a genuine app switch. But a soft keyboard NOT in the
hardcoded SYSTEM_PACKAGES list (e.g. the reporter's FUTO keyboard), or
the `android` framework package that hosts the paste / long-press popup
toolbar and toasts, surfaced a different package on a window event.
That reached evaluateForegroundPackage -> clearIfAppChanged, which wiped
the passthrough, so tapping back into the app re-triggered the delay.
Gboard/Samsung keyboards were only shielded by being hardcoded; any
other keyboard hit the bug.

Recognise transient non-app windows generically and ignore their events:
- Match the ACTIVE keyboard dynamically via Settings.Secure
  DEFAULT_INPUT_METHOD (cached, kept fresh by a ContentObserver), so
  every keyboard is covered, not a hardcoded few. Static IME_PACKAGES
  kept as fallback; the 3 IME entries moved out of SYSTEM_PACKAGES.
- Add isTransientNonAppPackage(pkg, currentImePackage) covering the
  active IME, IME_PACKAGES, and the `android` FRAMEWORK_PACKAGE.
- onAccessibilityEvent early-returns for these (no clearOverlays, no
  passthrough clear, no lastPackage move), the app underneath is
  unchanged. isOverlayBypassedByForeground also excludes transients.

Tests: TransientWindowTest (FUTO-as-active-IME, hardcoded IME, android,
real-app-never-transient, overlay-bypass regression guard). Full JVM
suite green. Device-verified on Pixel 3: keyboard + paste-popup no
longer re-block; genuine app switches still re-block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

main-latest

Toggle main-latest's commit message
fix: keyboard/paste-popup no longer re-triggers the delay (#5)

After completing a delay, PassthroughManager shields the app from
re-blocking until a genuine app switch. But a soft keyboard NOT in the
hardcoded SYSTEM_PACKAGES list (e.g. the reporter's FUTO keyboard), or
the `android` framework package that hosts the paste / long-press popup
toolbar and toasts, surfaced a different package on a window event.
That reached evaluateForegroundPackage -> clearIfAppChanged, which wiped
the passthrough, so tapping back into the app re-triggered the delay.
Gboard/Samsung keyboards were only shielded by being hardcoded; any
other keyboard hit the bug.

Recognise transient non-app windows generically and ignore their events:
- Match the ACTIVE keyboard dynamically via Settings.Secure
  DEFAULT_INPUT_METHOD (cached, kept fresh by a ContentObserver), so
  every keyboard is covered, not a hardcoded few. Static IME_PACKAGES
  kept as fallback; the 3 IME entries moved out of SYSTEM_PACKAGES.
- Add isTransientNonAppPackage(pkg, currentImePackage) covering the
  active IME, IME_PACKAGES, and the `android` FRAMEWORK_PACKAGE.
- onAccessibilityEvent early-returns for these (no clearOverlays, no
  passthrough clear, no lastPackage move), the app underneath is
  unchanged. isOverlayBypassedByForeground also excludes transients.

Tests: TransientWindowTest (FUTO-as-active-IME, hardcoded IME, android,
real-app-never-transient, overlay-bypass regression guard). Full JVM
suite green. Device-verified on Pixel 3: keyboard + paste-popup no
longer re-block; genuine app switches still re-block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.9.2

Toggle v1.9.2's commit message
fix: unblock Reddit, make daily pass global 2-min, gate all enforceme…

…nt on master toggle (v1.9.2)

Three user-reported fixes shipped as a patch:

Bug 1, Reddit wrongly blocked by the content filter. Removed reddit.com +
tumblr.com from the bundled blocklist asset, and added a code-level ALLOWLIST
(reddit/redd.it/x/twitter/imgur/discord/tumblr/wikipedia + reddit media) checked
before the domain match in ContentFilterMatcher, so a regenerated blocklist can
never re-introduce them. Domain match only, keyword matching on the raw URL is
unaffected (reddit.com/r/porn still blocks via keyword).

Bug 2, Daily emergency pass is now ONE 2-minute window per rolling 24h across
ALL apps (was per-app, 1 minute). EmergencyPass rewritten to global semantics
(canUseGlobal/nextAvailableGlobalMs/recordGlobal, globalLastUsed = MAX over the
ledger for migration from the old per-app data). PASS_DURATION_MS=120_000. The
active free window stays scoped to the app it was granted on. The locked-out
control renders as a visibly disabled/grey "Daily pass used" button on every
overlay. All user-facing "1 minute" strings updated to "2 minutes".

Bug 3, Master toggle OFF now suppresses ALL enforcement. Root cause: the
auto-kick cooldown block ran before the async globalEnabled check, so a
cooled-down/over-limit app still kicked the user after they toggled Nudge off.
Added a cached globalEnabledCached flag (Strict-Mode cached-flags pattern) and a
single synchronous gate in onAccessibilityEvent before the enforcement dispatch,
covering rule eval, auto-kick, cooldown overlay, counter/time-remaining overlays,
web-domain, content filter, and in-app detection. Toggle-off also clears all
cooldowns, cancels emergency-pass windows/kicks, and hides overlays; the pass
expiry kick is gated on globalEnabled. Strict Mode's own gates are unchanged.

Tests: full suite 816 passing (added allowlist regression tests, rewrote
EmergencyPassTest for global semantics, added clearAllCooldowns test).
Device-verified on Pixel 3 (all three fixes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

v1.9.1

Toggle v1.9.1's commit message
v1.9.1, fix block-overlay tab-out/tab-in bypass

v1.9.0

Toggle v1.9.0's commit message
feat: daily 1-minute pass (emergency escape hatch) (v1.9.0)

Per-app 60s escape window on block overlays, then kick home + rolling
24h lockout. Hidden under Strict Mode; Settings master toggle (default
on). Pure-domain ledger + EmergencyPassManager + shared overlay action.
20 new unit tests. Device-verified on Pixel 3 (button, grant, 59.8s
auto-kick, lockout, toggle, Strict Mode all pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.8.0

Toggle v1.8.0's commit message
feat(content-filter): query-aware keyword matching + expanded coverag…

…e (v1.8.0)

Content filter now matches flagged terms inside search queries (not just
whole domains), so restricted results are blocked at the search stage across
supported browsers including Firefox. Adds an opt-in "Strict keyword matching"
sub-toggle (default off) that whole-word-matches ambiguous shorthand terms in
the search query only, legitimate same-named sites stay reachable. Expands the
built-in keyword list with additional low-collision tokens.

- ContentFilterMatcher: extractSearchQuery() + matchesQueryKeyword(),
  AMBIGUOUS_QUERY_KEYWORDS, expanded DEFAULT_KEYWORDS (pure JVM, unit-tested)
- NudgePreferences: contentFilterStrictKeywords (DataStore, default false)
- ContentFilter.isBlocked(url, strictKeywords); wired through EvaluateBlockUseCase
- Settings: "Strict keyword matching" sub-toggle
- +22 unit tests; device-QA verified on Firefox (Pixel 3)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.7.0

Toggle v1.7.0's commit message
docs: ship autonomously after QA gate (no permission step)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.6.0

Toggle v1.6.0's commit message
feat: editable block messages + fix Firefox web blocking (v1.6.0)

Editable messages: new Settings > Personalize > "Edit block messages"
screen lets users customize the delay title, delay subtitle, and
hard-block overlay messages (one per line; empty = built-in defaults).
Stored in DataStore; resolved into the overlays at block time. Pure,
unit-tested resolver.

Firefox fix: modern Firefox uses a Compose toolbar whose URL bar is a
bare testTag (ADDRESSBAR_URL_BOX, no package prefix) with the URL in the
accessibility contentDescription, not text. findAccessibilityNodeInfosBy
ViewId does not match bare testTags at runtime, so web blocking silently
did nothing in Firefox. Added a bounded tree-traversal fallback that
matches viewIdResourceName directly and reads text->contentDescription,
cleaning the localized "Search or enter address" hint. Chrome and other
Chromium browsers use the unchanged fast path (no regression).

Verified on-device (Pixel 3, Firefox 152): custom messages render on all
overlay modes; github.com blocked in Firefox; Chrome un-regressed; no
crashes. 297 unit tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.5.8

Toggle v1.5.8's commit message
perf: load installed apps off the main thread + cache (faster navigat…

…ion)

getInstalledApps()/resolveAppName() did heavy PackageManager work (app
labels + icons) on the main thread and recomputed on every navigation,
causing visible lag opening Active Rules, Manage Apps, app config, and
stats screens. Now they run on Dispatchers.IO and cache results in the
@singleton repo (Mutex-guarded first-load), so repeat visits are instant.
ActiveRulesViewModel no longer re-fetches the app list inside the rules
flow collect. Adds 5 unit tests for the caching behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.5.7

Toggle v1.5.7's commit message
fix: web domain HARD_BLOCK now re-blocks on return to Chrome

Previously, visiting a hard-blocked domain in Chrome showed the overlay
once but silently let the user through on subsequent visits. The
lastBlockedDomain passthrough was incorrectly set for HARD_BLOCK, which
has no "completed" state. Now only DELAY/BREATHING set the passthrough
after the user finishes the exercise.

Bump to v1.5.7 (versionCode 28).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>