Skip to content

Intent overlay: cancel/back support + roasting excuse chips - #3

Merged
arkariz merged 9 commits into
mainfrom
claude/intent-overlay-back-cancel
Aug 31, 2026
Merged

arkariz merged 9 commits into
mainfrom
claude/intent-overlay-back-cancel

Conversation

@arkariz

@arkariz arkariz commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Three related additions to the "HOW LONG THIS TIME?" intent-capture overlay (shown when opening a blocked app):

1. Cancel / back support

Previously the only way out was granting a session — no way to back out and leave the blocked app alone.

  • Added a visible "← BACK" affordance in the top-left of the overlay.
  • Added hardware/gesture back-key handling: the window is focusable (the excuse field needs real keyboard input), but nothing consumed KEYCODE_BACK before this, so pressing back silently did nothing.
  • Both paths call the same cancel().

Cancelling mirrors the existing "left without granting" path (SessionStateMachine.onAppLeft's INTENT_PENDING -> IDLE case — no session Room row exists yet at this point since grant() is what creates it) and also sends the user home via the same goToHomeScreen() pattern RoastOverlayController already uses, since dismissing the overlay alone would just reveal the still-blocked app sitting underneath with nothing granted.

2. Roasting-toned excuse chips

The free-text "your excuse" field now has a row of pre-written self-roasting excuse chips above it (EXCUSE_CHIPS). Tapping a chip fills the field (still editable after); typing a custom excuse works exactly as before. Neither path is a hard requirement — the field stays optional — but whichever one the user takes, what ends up in the field is the user roasting themselves on the record before the session starts.

3. Live roast reaction to a custom-typed excuse

Once the field's text stops matching a chip (the user is writing their own), a red caption appears below it reacting to what they typed — first-match keyword pattern-matching against common excuse tropes (EXCUSE_KEYWORD_ROASTS: habit/muscle-memory, "waiting for a download," music/audio, notifications, link-diving, video binging, group chats, "resting"), falling back to a generic reaction line (EXCUSE_GENERIC_ROASTS, picked once per typing session, not re-rolled every keystroke) when nothing matches. There's no on-device LLM/NLP in this app, so this is pattern matching against a fixed word list, not real content understanding.

All three copy sets (EXCUSE_CHIPS / EXCUSE_KEYWORD_ROASTS / EXCUSE_GENERIC_ROASTS) now carry the finished copy pack, not placeholders.

Changes

  • IntentOverlayController.kt:
    • show()/showOnMainThread() take a new onCancel: () -> Unit; added the "← BACK" text affordance, a KeyEvent.KEYCODE_BACK listener on the (now focus-requesting) root view, and a goToHomeScreen() helper.
    • Added EXCUSE_CHIPS + a horizontal chip row wired to the excuse EditText (tap fills the field; a TextWatcher keeps chip highlight in sync if the user edits manually afterward).
    • Added EXCUSE_KEYWORD_ROASTS / EXCUSE_GENERIC_ROASTS + a live caption reacting to custom-typed text, wired into the same TextWatcher.
  • WatcherService.kt: maybeShowIntentOverlay wires onCancel to sessionStateMachine.onAppLeft(pkg, now) — the same call the poll loop already makes when the user switches away from the overlay without granting.

Test plan

  • Manual device check: open a watched app, tap "← BACK" — overlay closes, device returns home, no session started (re-opening shows the overlay again).
  • Manual device check: same flow via hardware/gesture back — same result.
  • Manual device check: tap an excuse chip — field fills with that text; edit it afterward — chip un-highlights, and if the edited text no longer matches any chip, the roast caption appears.
  • Manual device check: type a custom excuse containing a keyword like "video" or "notification" — caption reacts with the matching keyword line; type something with no keyword match — a generic line appears and stays stable while continuing to type (doesn't flicker per keystroke); clear the field — caption disappears.
  • Manual device check: confirm "START THE CLOCK" still grants normally with whatever's in the field.
  • Could not run a Kotlin/Gradle build in this environment (no Android SDK) — reviewed the diff manually; brace/paren balance checked.

https://claude.ai/code/session_01APiUERJLZddCPpd8gJ94R4

claude added 2 commits August 31, 2026 13:50
…ware back)

Previously the only way out of the 'HOW LONG THIS TIME?' overlay was
granting a session — there was no way to back out and leave the
blocked app alone. Added a visible '\xe2\x86\x90 BACK' affordance plus
hardware/gesture back-key handling (the window is focusable for the
excuse field, but nothing consumed KEYCODE_BACK before this).

Cancelling mirrors the existing 'left without granting' path
(SessionStateMachine.onAppLeft's INTENT_PENDING -> IDLE case) and also
sends the user home, since dismissing the overlay alone would just
reveal the still-blocked app underneath with nothing granted.
Adds a horizontal row of pre-written self-roasting excuse chips above
the free-text excuse field. Tapping a chip fills the field (still
editable after); typing a custom excuse works as before. Either path
means whatever ends up in the field is the user roasting themselves
before the session even starts, without making the field a hard
requirement.

Chip copy is placeholder pending a proper copywriting pass — see the
prompt in the PR description.
@arkariz arkariz changed the title Let the intent-capture overlay be cancelled (back button + hardware back) Intent overlay: cancel/back support + roasting excuse chips Aug 31, 2026
claude and others added 7 commits August 31, 2026 14:02
When the excuse field's text stops matching a chip (the user is
writing their own), a red caption below the field now reacts to it —
first-match keyword pattern-matching against common excuse tropes
(work/boss, 'quick', boredom, blaming a friend, etc.), falling back to
a generic 'writing your own?' line picked once per typing session
(not re-rolled every keystroke, to avoid flicker) when nothing
matches. No on-device LLM/NLP exists here, so this is pattern
matching, not real understanding — same placeholder-copy caveat as
EXCUSE_CHIPS.
Swaps EXCUSE_CHIPS, EXCUSE_KEYWORD_ROASTS, and EXCUSE_GENERIC_ROASTS
placeholder copy for the finished set.
10 varying-width chips scrolled off-screen sideways with no visual
hint there was more to see. Added a small self-contained FlowLayout
(left-to-right, wraps at the available width) and swapped it in for
the HorizontalScrollView + LinearLayout row — no new dependency, same
plain-Android-views approach the rest of this overlay already uses.
The excuse field now opens pre-filled with 'My hands did this
automatically.' (DEFAULT_EXCUSE), with that chip highlighted. There's
still no 'uncheck' affordance on a chip — same as before — so the only
ways to change it are picking a different chip or overwriting the
text by hand; the field just no longer starts blank.
The field can still be backspaced to empty mid-edit, but that now
shows a roast caption reacting to the attempt (EXCUSE_EMPTY_ROASTS)
instead of quietly doing nothing. Two fallbacks then guarantee it
never actually stays blank: losing focus while empty snaps the text
back to DEFAULT_EXCUSE, and START's click handler falls back to the
same default if it's ever tapped while the field is blank (e.g. via
the IME action button, without a focus-loss event firing first).
…ts behind a confirm tap

1. Picking a chip no longer fills the free-text field with its text —
   it just selects/highlights the chip and clears the field back to its
   hint, so chip and free text read as two separate, mutually exclusive
   excuse sources instead of one prefilling the other. Typing anything
   in the field deselects whatever chip was active (including the
   DEFAULT_EXCUSE chip, which starts pre-selected on open).

2. Reworked the never-blank enforcement from last commit: instead of
   silently snapping back to DEFAULT_EXCUSE on focus loss / submit, an
   empty free-text field is now allowed, but tapping START with it
   active and empty shows a roast and refuses to submit on the first
   tap (emptyConfirmPending) — a second START tap with that roast still
   showing proceeds with no excuse. A selected chip never hits this
   path, since it's already a real excuse.
@arkariz
arkariz merged commit 33ec59a into main Aug 31, 2026
1 check passed
@arkariz
arkariz deleted the claude/intent-overlay-back-cancel branch September 2, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants