Tags: astraedus/nudge
Tags
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>
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>
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>
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>
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>
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>
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>
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>
PreviousNext