Project 4: One-Tap Pocket Control Panel
Add a deliberate home-screen Widget surface and a native Termux:GUI dashboard while keeping every operation, permission decision, and durable state transition in the existing PocketOps core.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 2 - Intermediate |
| Time Estimate | 12-20 hours |
| Language | Python with small shell launchers (alternatives: Bash, C/C++, supported community bindings) |
| Prerequisites | Projects 1-3, event loops, state machines, Android activity vocabulary, accessibility basics |
| Key Topics | Termux:Widget, foreground versus task launch, Termux:GUI IPC, view/domain state, operation IDs, lifecycle reconstruction, duplicate prevention, confirmations |
1. Learning Objectives
By completing this project, you will:
- Distinguish Widget foreground shortcuts from background tasks and choose each intentionally.
- Explain why a home-screen launcher is an entry surface rather than a reliable service or application core.
- Build a native Termux:GUI view hierarchy through a narrow IPC adapter.
- Separate ephemeral view state from durable operation and domain state.
- Reconstruct the dashboard after close, focus loss, configuration change, or process restart.
- Prevent repeated taps and ambiguous retries from duplicating logical work.
- Require scope-specific confirmation for destructive operations.
- Design status, focus, labels, touch targets, contrast, and non-color cues for accessibility.
- Test the same core contracts from CLI, Widget, fake GUI, and a physical Android device.
2. Theoretical Foundation
2.1 Core Concepts
Projects 1-3 already define the application: typed operations, durable state, capability policy, schemas, and observable outcomes. Project 4 adds two adapters. The Widget adapter translates a user tap into one fixed operation. The GUI adapter translates durable PocketOps state into Android-native controls and translates UI events back into typed requests.
Neither adapter owns business decisions. If the GUI implements a different import policy from the CLI, or a widget directly invokes a raw Termux:API command, the system now has multiple applications that only appear related.
Android launcher native control panel shell/tests
Widget Termux:GUI CLI
| | |
+---------- thin input adapters +------------------------+
|
versioned PocketOps operations
|
+----------------------+----------------------+
| runtime | vault | telemetry | queue | service|
+----------------------+----------------------+
|
private durable state
UI can disappear. Accepted operations and authoritative state cannot.
The useful architectural test is simple: can the operation be completed and inspected without the GUI? If not, application logic has leaked into the view.
Termux:Widget execution modes
In the classic reference track, the official Termux:Widget plug-in discovers scripts from two private HOME locations:
~/.shortcuts/for commands launched in foreground terminal sessions.~/.shortcuts/tasks/for commands launched as background tasks shown through the Termux task notification.
The official documentation requires suitable private directory permissions and validates that shortcut paths resolve beneath allowed roots. Widget contents may need a refresh after script changes or app/process updates. A widget does not run code itself; it sends an execution request to the Termux app.
Foreground and background are product choices:
| Mode | Appropriate Use | Inappropriate Use |
|---|---|---|
| Foreground session | Status details, interactive recovery, opening control panel | Silent periodic work |
| Background task | Quick capture/enqueue with notification evidence | Long unjournaled work or hidden destructive action |
A task is still governed by Android lifecycle and Termux process life. It does not become durable because it came from the launcher. The task should validate, enqueue or commit a short operation, report an operation ID, and exit. Projects 5 and 8 provide queues and supervision for longer work.
Distribution track and signing caveat
The reference implementation uses classic F-Droid/GitHub Termux plus compatible integration applications. All classic Termux apps and plug-ins used together must come from the same official source/signing family. Never mix a GitHub add-on with an F-Droid main app, or vice versa. Verify source and compatibility through Project 1 before installing Widget, GUI, API, or later plug-ins.
The current Google Play track merges Widget functionality into the main application; a separate Widget APK is not required there. Its API and plug-in surface is distinct, and this guide does not assume Termux:GUI is available or equivalent on Play. Treat Widget and GUI as two separately negotiated capabilities. A Play implementation may complete the Widget portion and report GUI as unavailable, or substitute a deliberately scoped local web interface as an extension. Do not install classic plug-in APKs into the Play family to force parity.
Termux:GUI and Android UI lifecycle
Termux:GUI lets a Termux process create Android-native activities and views through IPC. Official development documentation describes activities, tasks/back stacks, lifecycle events, view groups, text views, buttons, inputs, and language bindings/protocols. The visible Android controls live across a process boundary from the CLI core.
This creates two categories of state:
Ephemeral view state
- Current scroll position.
- Which card is expanded.
- Temporary focus.
- In-progress text not yet submitted.
- Animation and pressed state.
Durable domain/operation state
- Snapshot ID and committed readings.
- Import plan and committed import ID.
- Queued/running/succeeded/failed operation.
- Service desired state.
- Confirmation decision and audit record for a destructive request.
An activity can stop, close, or be recreated while a core operation continues. The GUI must query authoritative state by operation ID and render it again. It cannot keep the only job handle inside a button callback.
Operation state machines and duplicate taps
A one-tap interface makes duplicates more likely. Users double-tap. Launchers retry. A button appears unresponsive during IPC latency, so the user taps again. Model mutating actions as durable operations:
+---------+
tap ----->| QUEUED |<---- safe replay returns same operation
+----+----+
|
v
+---------+
| RUNNING |
+----+----+
|
+----------+----------+
v v
+---------+ +----------+
|SUCCEEDED| | FAILED |
+---------+ +----+-----+
|
retryable? new attempt under
same logical idempotency key
The UI can disable the initiating button immediately, but visual disabling is not the correctness mechanism. The core still enforces an idempotency key or uniqueness rule. After recreation, the UI queries the operation and restores the correct disabled/progress/result state.
Render models and event loops
Do not expose raw database rows or command output directly to views. Map a core result into a small render model:
- stable component ID;
- visible label;
- state enum;
- concise detail;
- non-color cue/icon description;
- allowed action set;
- accessibility label/hint;
- last-updated timestamp;
- linked operation ID.
The GUI event loop receives lifecycle and control events. Validate event type and component identity, then dispatch a typed application request. IPC can fail, events can arrive after a view is gone, and long work can outlive the visible activity. Handle late or unknown events safely rather than assuming a synchronous desktop GUI.
Confirmations and least surprise
Read-only Status is safe for one tap. Capture can be one tap when it creates a bounded snapshot and visible audit record. Import should show a plan before mutation because the user needs to see candidate and disposition. Stop Jobs is destructive and must name scope:
- Which queue or service?
- Does “stop” cancel running work, prevent new work, or only change desired state?
- Is committed work preserved?
- Can the action be reversed?
Generic “Are you sure?” dialogs train users to tap through. A useful confirmation states the exact effect and makes the safe cancel action obvious.
Accessibility and small-screen feedback
Color is supplementary. Every READY, DEGRADED, BLOCKED, running, succeeded, and failed state needs visible text and an accessible semantic label. Touch targets should be large and separated. Focus order follows screen order. The dashboard must work with increased font size, screen reader traversal, hardware keyboard/focus navigation where supported, rotation, narrow screens, and dark/light contrast.
Progress must state what is happening. An indefinite spinner with no operation ID or recovery path is not observability. Show “Capture queued,” the ID, elapsed state, and a way to reopen the result.
2.2 Why This Matters
Users judge an operational tool by how quickly they can understand and safely act on current state. Thin visual adapters add accessibility and one-tap utility without sacrificing the testability and composability of the CLI. The same boundary pattern governs web frontends, desktop clients, mobile apps, and operator consoles over durable backend operations.
2.3 Historical Context / Background
Termux began as a terminal-first experience and gained add-ons for launcher shortcuts and Android-native GUI controls. Android also strengthened lifecycle, background-launch, and signing constraints. The current Play track merges Widget into the main app while classic installations retain companion plug-ins, so visual surfaces must now be negotiated like every other capability.
2.4 Common Misconceptions
- “A widget is a tiny always-running app.” It is an Android entry surface that requests Termux execution.
- “Background task means reliable daemon.” Android can still stop the Termux process tree.
- “Disabled button prevents duplicates.” Correctness belongs in core idempotency.
- “GUI state is application state.” Activities and the GUI process can disappear.
- “The Play Widget is installed like classic Widget.” Current Play merges Widget into the main app.
- “A pretty dashboard can parse CLI prose.” It needs typed contracts and render models.
3. Project Specification
3.1 What You Will Build
Build two visual entry surfaces over Projects 1-3:
PocketOps home-screen controls
- Status: foreground session or panel launch.
- Capture Reading: short background enqueue/capture operation.
- Import Inbox: open plan/review flow rather than mutating blindly.
- Stop Jobs: open a scope-specific confirmation flow.
Native control panel
- Top app/title bar and last-refresh status.
- Runtime, Storage, Telemetry, Queue, and Services cards.
- Capture progress and committed snapshot receipt.
- Import plan with candidate, validation, and disposition.
- Destructive confirmation dialog.
- Bounded event log using safe structured summaries.
- Lifecycle restore by durable state and operation ID.
3.2 Functional Requirements
- Explicit Widget mode: At least one foreground shortcut and one background task are intentional and documented.
- Fixed launchers: Each shortcut invokes one reviewed PocketOps operation; no user-controlled shell fragments.
- Shared contracts: GUI and Widget call the same typed operations used by CLI tests.
- Durable operation before feedback: Mutating work is accepted durably before the UI claims success.
- State reconstruction: Reopen/rotation reloads cards and operation status from core state.
- Duplicate prevention: Repeated taps map to one logical operation where required.
- Confirmation: Stop/import effects are scoped and explicit.
- Accessibility: Every state has text and semantics independent of color.
- Bounded log: UI event list is capped, redacted, and recoverable from safe audit data.
- Capability fallback: Missing GUI does not disable CLI/Widget operations or corrupt state.
3.3 Non-Functional Requirements
- Responsiveness: Tap acknowledgement appears promptly without pretending the operation completed.
- Reliability: UI process loss cannot lose accepted work or final receipts.
- Security: Launchers are private fixed files; event inputs are typed; destructive scope is validated by core.
- Usability: Primary states and recovery actions fit a phone screen without terminal knowledge.
- Accessibility: Labels, focus order, touch targets, scaling, contrast, and non-color cues are reviewed.
3.4 Example Usage / Output
StatusCard:
card_id
title
state = READY | DEGRADED | BLOCKED | RUNNING
state_text
summary
last_updated
primary_action?
accessibility_label
UiAction:
schema = pocketops.ui-action.v1
action = REFRESH | CAPTURE | PLAN_IMPORT | CONFIRM_STOP | CANCEL
component_id
operation_id?
idempotency_key?
bounded_parameters
OperationReceipt:
operation_id
logical_action
state
accepted_at
committed_result_id?
safe_error?
3.5 Real World Outcome
On the classic reference track, install compatible Widget and GUI components only from the same official signing/source family as the main Termux installation, following each project’s official installation instructions. On the Play track, use the Widget capability merged into the current main app and treat GUI as separately negotiable rather than mixing classic APKs.
Add the PocketOps widget to the Android launcher. It shows four clearly labeled actions with distinct icons or text: Status, Capture Reading, Import Inbox, and Stop Jobs. Status is a deliberate foreground action. Capture Reading is a short background task that durably accepts an operation and returns. Refresh the widget after changing its private launchers.
Tap Status. A native control panel appears. The title bar reads “PocketOps,” and beneath it a line shows last refresh and overall DEGRADED/READY text. Five stacked or grid cards show Runtime, Storage, Telemetry, Queue, and Services. Each card has a state word, non-color symbol/description, concise evidence, and one relevant action. Increased font size reflows without clipping.
Tap Capture Reading. The button disables immediately and announces “Capture queued.” A progress row displays the operation ID. When Project 3 commits the snapshot, the row changes to “Succeeded” and shows the snapshot ID. Tap twice rapidly: both UI events resolve to the same logical operation. Close the GUI during RUNNING, reopen it, and watch the same operation transition to its final result.
Tap Import Inbox. The panel displays Project 2’s read-only plan: candidate name, size, type, digest status, target class, and whether the original will be retained. No import occurs until the user confirms that exact plan.
Tap Stop Jobs. A dialog says which service/queue is affected, what happens to running and committed work, and whether the action is reversible. Cancel leaves state unchanged. Confirm creates an audited operation rather than killing an arbitrary process from UI code.
┌────────────────────────────────────────────┐
│ PocketOps DEGRADED │
│ Updated 14:32 · Refresh │
├────────────────────────────────────────────┤
│ ✓ Runtime READY │
│ classic-fdroid · aarch64 │
├────────────────────────────────────────────┤
│ ✓ Storage READY │
│ private vault · inbox 1 candidate │
├────────────────────────────────────────────┤
│ ! Telemetry DEGRADED │
│ location permission denied │
│ [ Capture Reading ] │
├────────────────────────────────────────────┤
│ ✓ Queue READY · depth 0 │
│ ✓ Services READY │
├────────────────────────────────────────────┤
│ Recent: capture 01JZ8K4YQ3 SUCCEEDED │
└────────────────────────────────────────────┘
The observable result exists in the launcher, native panel, CLI operation query, and private audit log. All four agree because they consume the same contracts.
3.6 Edge Cases
- Widget is stale after script/app update and needs explicit refresh.
- Shortcut parent directory has wrong permissions or canonical path escapes allowed root.
- Foreground shortcut is accidentally placed in the task directory, or reverse.
- Widget tap arrives twice or after a long launcher delay.
- GUI capability unavailable on the active track.
- GUI IPC disconnects while a core operation continues.
- Activity closes or configuration changes during confirmation or progress.
- Result arrives for a component no longer present.
- Operation succeeds after UI displayed a transient timeout.
- Large font, narrow width, rotation, dark mode, and screen reader traversal.
- Import plan becomes stale because candidate changes before confirmation.
- Stop confirmation refers to a service whose desired state changed concurrently.
- Event log grows without bound or contains telemetry canaries.
4. Solution Architecture
4.1 High-Level Design
Android launcher Termux:GUI activity
+-------------------+ +-----------------------+
| Widget shortcuts | | view tree + event loop|
| foreground / task | +-----------+-----------+
+---------+---------+ |
| typed UI actions / render models
fixed private launchers |
+-------------------+----------------------+
v
+------------------------+
| UI application adapter |
| validation + receipts |
+-----------+------------+
|
versioned core operations
+--------------------+--------------------+
| doctor | vault | telemetry | queue/state|
+--------------------+--------------------+
|
private durable store
|
reload by operation/card IDs
4.2 Key Components
| Component | Responsibility | Key Decision |
|---|---|---|
| Widget launchers | Translate fixed taps into operation requests | One launcher, one reviewed operation |
| Surface capability resolver | Select classic Widget/GUI, Play merged Widget, or unavailable state | Never cross-install to emulate parity |
| UI controller | Validate UI events and dispatch requests | No direct database or API command logic |
| Operation service | Accept/idempotently query durable work | Receipt exists before UI success feedback |
| Query service | Produce current card and operation state | Read-only and safe to refresh repeatedly |
| View-model mapper | Convert typed domain results into cards/actions | Views never parse human CLI output |
| GUI adapter | Create/update native views and receive lifecycle/events | IPC failure remains an adapter failure |
| Confirmation coordinator | Bind user decision to an exact current plan | Reject stale plan or changed scope |
| Accessibility metadata | Supply labels, hints, focus, and non-color state | Treated as part of render contract |
4.3 Data Structures
Use Section 3.4 contracts plus:
ConfirmationPlan:
plan_id
action
target_scope
effect_summary
reversible
generated_from_state_version
expires_at
DashboardModel:
schema
overall_state
cards[]
active_operations[]
recent_safe_events[]
generated_at
source_state_version
4.4 Algorithm Overview
Widget action
- Termux receives a fixed private launcher request.
- Launcher bootstraps the minimal explicit environment.
- It submits or queries one typed operation with an idempotency identity.
- Core durably accepts work or returns a committed result.
- Launcher renders a bounded receipt and exits.
GUI refresh and action
- GUI capability starts and creates an activity/view hierarchy.
- Controller queries one
DashboardModelfrom core state. - Adapter renders cards and active operations.
- UI event is validated against component/action allowlists.
- Mutating request returns an operation receipt or confirmation plan.
- GUI observes operation by ID and refreshes render model.
- On recreation/reopen, repeat from step 2; no job depends on old view objects.
Complexity analysis
- Dashboard query: O(C + O + E), for bounded cards, active operations, and recent events.
- UI memory: bounded by current view tree and capped render models, never full logs/history.
5. Implementation Guide
5.1 Development Environment Setup
Complete Projects 1-3 and verify their CLI contracts before installing visual integrations. On classic, source the main Termux app and all compatible plug-ins from one official F-Droid or GitHub signing family. Do not mix sources. Follow official Widget and GUI installation/version guidance and open required applications once where their setup requires it.
On the current Play track, use merged Widget functionality and record GUI as unsupported unless the active official capability surface proves otherwise. The project may still complete its Widget and architecture goals with a fake GUI adapter; a local web UI is an optional alternative, not a silent replacement.
For classic Widget, use the documented private foreground and task roots and restrictive directory permissions. Store launchers in private HOME, never shared storage.
5.2 Project Structure
one-tap-control-panel/
├── docs/
│ ├── surface-capabilities.md
│ ├── screen-specification.md
│ ├── accessibility-review.md
│ └── lifecycle-state-table.md
├── shortcuts/
│ ├── foreground-launchers/
│ └── background-task-launchers/
├── schemas/
│ ├── ui-action-v1.schema.json
│ └── dashboard-v1.schema.json
├── src/
│ ├── controller/
│ ├── operation-client/
│ ├── view-models/
│ ├── gui-adapter/
│ └── confirmation/
├── fixtures/
├── tests/
└── README.md
5.3 The Core Question You’re Answering
“How can a command-line application gain one-tap and native visual surfaces without creating a second, inconsistent application?”
Before creating a button, list the exact existing operation it invokes and the typed state it renders. If either is missing, return to the core first.
5.4 Concepts You Must Understand First
- Thin adapters: UI translates intent and results; core owns policy and state.
- Widget modes: Foreground session and background task have different user/lifecycle contracts.
- Android activity lifecycle: Visible UI can stop or disappear independently of jobs.
- IPC: GUI events/results can be late, disconnected, bounded, or version-mismatched.
- Idempotency: Repeated taps must not create repeated logical effects.
- Confirmation binding: A decision must refer to an exact plan/state version.
- Accessibility semantics: Text, focus, labels, and non-color cues are part of correctness.
- Signing/source compatibility: Classic plug-ins require the same Termux source family; Play differs.
5.5 Questions to Guide Your Design
- Which actions are read-only, one-tap safe, confirmable, or forbidden from Widget?
- Which launcher should be foreground and which should be a task?
- What is the maximum work a Widget task performs before enqueue-and-exit?
- Which state belongs only in the visible view?
- How does a reopened panel discover an in-progress operation?
- What happens when a confirmation plan becomes stale?
- Can every icon and color be understood by text/screen reader alone?
- How does the UI report missing GUI capability without disabling CLI operations?
5.6 Thinking Exercise
Draw the Capture operation states: idle, submitting, queued, running, succeeded, retryable failure, permanent failure. At every state:
- close the GUI;
- tap the Widget again;
- rotate or recreate the activity;
- lose GUI IPC;
- restore the GUI.
Write which durable IDs exist and what the reopened screen must show. Repeat for an Import plan whose source file changes before confirmation.
5.7 The Interview Questions They’ll Ask
- “Why should a GUI be a thin adapter?”
- “What is the difference between view state and domain state?”
- “How do you prevent duplicate submissions from repeated taps?”
- “What does Android activity recreation imply for long operations?”
- “How do foreground Widget shortcuts differ from background tasks?”
- “How would you test a native GUI backed by a CLI?”
5.8 Hints in Layers
Hint 1: Read-only Status first
Render one DashboardModel from fixed fixtures before creating any mutating button.
Hint 2: Capture returns an ID
The button submits one operation and observes its receipt. It never waits on raw Termux:API output.
Hint 3: Rebuild from queries
Treat every GUI start as if all prior view objects were lost. Query cards and active operations again.
Hint 4: Test without GUI
Drive the controller using fake UI events and snapshot render models. Reserve the phone for IPC, lifecycle, and accessibility evidence.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Interface patterns | “Designing Interfaces,” Tidwell, Brewer, and Valencia | Status, feedback, mobile, and input patterns |
| Boundary architecture | “Clean Architecture,” Robert C. Martin | Ch. 20-22 |
| Android lifecycle | “Android Programming: The Big Nerd Ranch Guide” | Activity lifecycle chapters for your edition |
| State models | “Designing Data-Intensive Applications,” Martin Kleppmann | Ch. 4 and 11 for durable events/state |
5.10 Implementation Phases
Phase 1: Read-Only Dashboard Core (3-5 hours)
Goals
- Define render/action/receipt contracts.
- Render Status from Projects 1-3 without a physical GUI.
Tasks
- Create dashboard fixtures for ready, degraded, blocked, and running states.
- Build the view-model mapper and accessibility metadata.
- Test bounded recent events and unknown fields.
- Implement a read-only GUI adapter or fake protocol harness.
Checkpoint: One typed state renders identically in tests and the initial native panel.
Phase 2: Widget and Durable Capture (4-7 hours)
Goals
- Add one foreground and one background action deliberately.
- Prove duplicate-safe capture and lifecycle reconstruction.
Tasks
- Add private fixed Widget launchers in documented roots.
- Refresh and test foreground Status versus background Capture.
- Submit Capture through the operation service with idempotency.
- Close/reopen during every operation state.
- Test rapid repeated taps and IPC loss.
Checkpoint: Two rapid taps create one logical snapshot, and reopening shows its final receipt.
Phase 3: Import, Stop, and Accessibility (4-8 hours)
Goals
- Add plan-bound confirmation flows.
- Complete real-device lifecycle and accessibility review.
Tasks
- Render Project 2 import plan and validate freshness on confirm.
- Define exact Stop Jobs scope and reversible behavior.
- Add focus order, labels, larger text, non-color cues, and narrow layouts.
- Test rotation, app close, background/foreground, and stale widget refresh.
- Save redacted screen/state acceptance evidence.
Checkpoint: Accessibility checklist passes, stale plans are rejected, and no destructive action runs from an ambiguous tap.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Widget Capture | Foreground; background task | Short background enqueue/task | Immediate one-tap behavior with visible receipt |
| Long work | Widget process; durable operation | Durable operation | Survives surface loss and enables observation |
| GUI data | Parse CLI text; typed query | Typed query/render model | Stable and accessible state |
| Duplicate control | Disable button only; core idempotency | Both, with core authoritative | UX feedback plus correctness |
| Confirmation | Generic dialog; exact plan | Exact expiring plan/state version | Prevents stale or ambiguous destructive action |
| Missing GUI | Block product; degrade surface | Degrade surface | Core and Widget remain useful |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit | Validate view-model mapping | Card states, labels, allowed actions |
| Contract | Protect UI action and dashboard schemas | Unknown fields, version mismatch, bounded strings |
| Controller | Test events without Android GUI | Duplicate tap, stale plan, unknown component |
| Integration | Connect durable operations | Capture receipt, import plan, stop audit |
| Widget | Verify mode/path/environment | Foreground session, background task, refresh |
| Lifecycle | Prove reconstruction | Close/reopen, configuration change, IPC loss |
| Accessibility | Validate nonvisual and scaled use | Labels, focus, touch target, large text, contrast |
| Track | Prevent installation confusion | Classic plug-ins versus Play merged Widget |
6.2 Critical Test Cases
- Status read-only: Dashboard query causes no mutation.
- Foreground shortcut: Opens the intended session/panel with explicit environment.
- Background capture: Accepts operation, shows task evidence, and exits promptly.
- Rapid double tap: One logical operation/receipt.
- Close during running: Reopen resolves the same operation ID.
- IPC disconnect: Core work continues; UI reports adapter loss and recovers.
- Stale import plan: Changed candidate causes re-plan, not stale confirm.
- Stop cancel: No desired state or job changes.
- Stop confirm: Exact scope changes and audit record commits.
- Widget stale after update: Refresh restores current launchers.
- Classic lineage mismatch fixture: Setup state is blocked without mixing-source advice.
- Play track: Merged Widget is selected; separate classic Widget/GUI installation is not suggested.
- Large text/rotation: Cards remain readable and actions reachable.
- Screen reader: Every icon/state/action has a meaningful label and order.
- Log canary: Telemetry secrets never appear in UI events or recent-log cards.
6.3 Test Data
dashboard-ready: five READY cards, no operations
dashboard-degraded: location permission failure, other cards valid
dashboard-running: capture operation QUEUED then RUNNING
capture-duplicate: two UI events, same idempotency key
import-plan-stale: candidate digest changes before confirmation
stop-plan: exact worker/queue scope and state version
gui-disconnect: adapter closes between event and render
accessibility-long: expanded labels and 200% font scale
event-canary: SECRET_CANARY must be redacted before rendering
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Wrong Widget directory | Action opens wrong mode or does not appear | Use documented foreground/task roots and refresh |
| Launcher depends on shell profile | Works in terminal only | Bootstrap minimal explicit HOME/PREFIX/PATH |
| Raw API call in button handler | UI and CLI behavior diverge | Invoke existing typed core operation |
| Job state held in GUI | Closing loses progress | Persist first and reload by operation ID |
| Button disabling as duplicate defense | Retry creates duplicate | Enforce core idempotency/uniqueness |
| Generic destructive dialog | User cannot predict effect | Bind confirmation to exact scope and state version |
| Color-only state | Screen-reader/color-vision failure | Add state text, semantics, and non-color cues |
| Classic add-on advice on Play | Unsupported mixed installation | Negotiate track and surface separately |
7.2 Debugging Strategies
- Run the exact core operation directly before debugging Widget/GUI.
- Compare redacted environment manifests for terminal and Widget launches.
- Inspect Widget root, permissions, canonical path, and refresh state.
- Trace one correlation/operation ID from tap through core and back to render model.
- Separate GUI IPC errors from application operation failures.
- Inspect stdout/stderr/tool log before enabling verbose Android logs.
- Restore normal log levels after debugging to reduce private-data exposure.
7.3 Performance Traps
- Rebuilding the entire native view tree for every small progress update.
- Polling durable state at an aggressive fixed interval while backgrounded.
- Rendering unbounded event history.
- Doing file import or telemetry collection inside the UI event callback.
- Sending large payloads repeatedly over IPC instead of small IDs and render models.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add a Refresh shortcut and visible stale-data age.
- Add a compact landscape layout.
- Add a “copy safe operation ID” action without copying sensitive detail.
8.2 Intermediate Extensions
- Add notification deep links to a specific operation receipt.
- Add a settings view that edits validated profile data through core contracts.
- Add localization and test long translated labels.
8.3 Advanced Extensions
- Build a local loopback web UI fallback with the same render/action schemas and CSRF protection.
- Add push-based operation updates with backpressure instead of polling.
- Conduct a screen-reader usability session and revise the information architecture from observed evidence.
9. Real-World Connections
9.1 Industry Applications
- Operator consoles: Thin dashboards render durable backend state and submit audited operations.
- Mobile job control: UI surfaces accept work quickly and observe by operation ID.
- Progressive enhancement: CLI remains functional while richer surfaces are optional capabilities.
- Accessibility architecture: Semantic render models make nonvisual behavior testable.
- Distributed UI state: Activity recreation resembles browser refresh or reconnecting clients.
9.2 Related Open Source Projects
- Termux:Widget: Official classic home-screen shortcut and task adapter.
- Termux:GUI: Native Android view/activity bridge for Termux applications.
- Termux app: Receives execution requests and owns the child process environment.
- Android architecture guidance: Lifecycle and state-restoration concepts parallel conventional Android applications.
9.3 Interview Relevance
This project demonstrates clean boundaries, UI/domain state separation, lifecycle recovery, idempotent submission, event-loop design, IPC failure, confirmations, and accessibility. It is relevant to frontend, mobile, desktop, distributed systems, and platform-tooling interviews.
10. Resources
10.1 Essential Reading
- Termux:Widget official project - Shortcut roots, foreground/tasks, permissions, refresh, and debugging.
- Termux:GUI official project - Native Android GUI integration.
- Developing with Termux:GUI - Tasks, activities, lifecycle, views, layouts, and bindings.
- Termux:GUI protocol - IPC contract details for binding authors.
- Termux app installation - Classic same-source/signing-lineage rule.
- Termux Google Play track - Current merged Widget and distinct integration surface.
10.2 Video Resources
- Official Android Developers material on activity lifecycle, state restoration, accessibility, and adaptive layouts.
- UI architecture talks focused on unidirectional state and operation receipts.
10.3 Tools and Documentation
- Android activity lifecycle - Platform lifecycle model.
- Android accessibility principles - Labels, focus, touch targets, and alternative cues.
- Android adaptive layouts - Conceptual guidance for changing screen/window sizes.
- Command Line Interface Guidelines - The core interface remains a stable CLI contract.
10.4 Related Projects in This Series
- Project 1: Pocket Runtime Cartographer provides surface/track capability truth.
- Project 2: Scoped Storage Inbox and Safe File Vault provides import plans and receipts.
- Project 3: Phone Telemetry and Action Broker provides snapshots and notification actions.
- Project 8 later gives long-running work a supervised lifecycle; this Widget does not replace it.
11. Self-Assessment Checklist
11.1 Understanding
- I can explain why Widget and GUI are adapters rather than the core application.
- I can distinguish classic foreground shortcuts and background tasks.
- I can explain current Play merged Widget behavior without suggesting mixed APKs.
- I can separate view, operation, and domain state.
- I can explain why duplicate prevention belongs in the core.
- I can describe how every screen reconstructs after lifecycle loss.
- I can justify every confirmation and accessibility semantic.
11.2 Implementation
- At least one foreground and one background Widget action are intentional.
- Every launcher calls one fixed reviewed operation.
- GUI renders typed view models, not CLI prose.
- Accepted operations persist before success feedback.
- Close/reopen and IPC-loss tests preserve operation truth.
- Rapid taps do not duplicate logical effects.
- Stale plans cannot be confirmed.
- Labels, focus, scaling, contrast, and non-color cues pass review.
11.3 Growth
- I removed one piece of business logic from the UI adapter.
- I documented one lifecycle failure that a desktop-only test missed.
- I can explain this system to both a mobile engineer and a backend engineer.
12. Submission / Completion Criteria
Minimum Viable Completion
- Status Widget/shortcut and native or fake GUI render the same typed health state.
- One foreground and one background action are correctly placed and documented.
- Capture submits a durable operation and returns a receipt.
- Closing/reopening shows the same final operation.
- States include text and do not rely on color alone.
Full Completion
- All minimum criteria plus four launcher actions, classic/Play capability handling, native GUI cards, import and stop plan confirmations, duplicate prevention, bounded event log, IPC/lifecycle tests, and a complete accessibility review.
Excellence (Going Above and Beyond)
- Push-based updates, secure local web fallback, localization/adaptive layouts, notification deep links, and observed screen-reader usability testing.
This guide was expanded from TERMUX_ANDROID_APPS_TOOLS_MASTERY.md. See the expanded project index for the complete learning path.