Intent overlay: cancel/back support + roasting excuse chips - #3
Merged
Merged
Conversation
…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.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
KEYCODE_BACKbefore this, so pressing back silently did nothing.cancel().Cancelling mirrors the existing "left without granting" path (
SessionStateMachine.onAppLeft'sINTENT_PENDING -> IDLEcase — no session Room row exists yet at this point sincegrant()is what creates it) and also sends the user home via the samegoToHomeScreen()patternRoastOverlayControlleralready 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 newonCancel: () -> Unit; added the "← BACK" text affordance, aKeyEvent.KEYCODE_BACKlistener on the (now focus-requesting) root view, and agoToHomeScreen()helper.EXCUSE_CHIPS+ a horizontal chip row wired to the excuseEditText(tap fills the field; aTextWatcherkeeps chip highlight in sync if the user edits manually afterward).EXCUSE_KEYWORD_ROASTS/EXCUSE_GENERIC_ROASTS+ a live caption reacting to custom-typed text, wired into the sameTextWatcher.WatcherService.kt:maybeShowIntentOverlaywiresonCanceltosessionStateMachine.onAppLeft(pkg, now)— the same call the poll loop already makes when the user switches away from the overlay without granting.Test plan
https://claude.ai/code/session_01APiUERJLZddCPpd8gJ94R4