Files
IchWillHeim/README.md
T

365 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IchWillHeim
A dementia-friendly Android home screen launcher. The patient sees a locked-down, simple screen. Caregivers configure everything remotely via a web app.
---
## Product architecture
The product is split into two distinct parts:
**1. Android launcher (this app)**
The only thing installed on the patient's device. It fetches its configuration from NocoBase on startup and renders a simple, locked-down home screen. No workflow configuration happens on the device.
**2. Caregiver web app**
Where caregivers manage everything: patients, devices, contacts, and workflows. Built in two phases:
- **Near term:** NocoBase's interface builder — Table, Form, Grid Card, and Details blocks are sufficient to build a functional caregiver dashboard with no frontend code. Caregivers can add/edit/remove workflows and contacts directly.
- **Long term:** a custom web app (and eventually mobile) with a proper onboarding questionnaire (ask what the patient needs → generate the first workflows automatically) plus a manual editor for ongoing changes.
---
## Settings on the device
### Patient-accessible settings
Simple, non-technical controls the patient can reach directly from the home screen:
- Font / button size
- Screen brightness
### PIN-locked advanced settings
Accessible via long-press (PIN-protected). The access screen is labelled **Settings Access** and the menu **Settings Menu**:
- **Device ID** — the identifier used to fetch this device's config from NocoBase
- **Language** — in-app language picker
- **Phone Settings** — opens the Android system settings (for Wi-Fi, accessibility, etc.)
Workflow configuration, home screen grid layout, and help screen editing are never done on the device — all managed via the web app.
---
## How the app works
### From the patient's perspective
The patient sees one screen with large buttons driven by workflows configured remotely. Pressing Back does nothing. Pressing Home always returns to this screen. No other apps are accessible.
### From the caregiver's perspective
Caregivers manage the patient's experience entirely through the web app (NocoBase or future custom app). On the device, they can access PIN-locked settings for device ID and language. They never need to touch workflow config on the device.
---
## Home screen grid
The home screen displays workflow buttons in a fixed 2-column grid. The caregiver sets `homepage_position` (1-indexed, left-to-right, top-to-bottom) and `button_span` (1 = one cell, 2 = wide / full row) for each workflow via the web app.
**Grid rendering rules (same logic in Android and web preview):**
1. Build a flat array of cells (size = effective grid size).
2. For each workflow sorted by position: place it at `position - 1`. If `button_span == 2` AND the button starts in the left column (even index) AND there is a next cell, mark the next cell as "consumed" so nothing else renders there.
3. If a `span-2` button starts in the right column, it is silently downgraded to `span-1` (would overflow the row).
4. **Auto-extend**: the configured `gridSize` (6 or 8, set in caregiver settings) is a minimum. If a workflow's position+span exceeds it, the grid grows to fit — `effectiveGridSize = max(configuredRows, rowsNeededByContent) × 2`.
The same `buildGridCells` algorithm runs in `HomeScreen.kt` (Android) and `Workflows.tsx` (web preview), so what the caregiver sees in the browser is exactly what the patient sees on their phone.
---
Every button on the home screen triggers a **workflow** — a sequence of steps. Each step calls one function (show a screen, place a call, etc.) and returns a result code. Steps execute in `sort_order` ascending; the engine automatically advances to the next step unless an `if` step redirects the flow.
```
Step 1 (sort=1) → Step 2 (sort=2) → if step → yes: Step 3 / no: Step 4 → …
```
Everything is data-driven. New buttons, screens, and branching logic are configured in the web app with no code changes.
### Step types
| function_id | behaviour |
|---|---|
| `show_speaker_choice` | Shows speakerphone vs normal choice. Emits `speakerphone` or `normal`. |
| `call_contact` | Dials `phone` from `parameter_json`. Reads `speakerphone` from `parameter_json` (bool), falls back to `lastResultCode == "speakerphone"`. |
| `open_app` | Launches `package_name` from `parameter_json` via `packageManager.getLaunchIntentForPackage`. Advances with `null`. Shows a toast if the app isn't installed. |
| `show_content` | Renders `ContentScreen` with the step's `content_block` list (heading/text/image/button). A button block advances with its own `result_code`; `result_code == "cancel"` always ends the workflow regardless of step type. With no button block, a default "Next" button advances with `null`. |
| `show_app_list` | Renders `AppListScreen` — a scrolling grid of every `workflow_type == "app"` workflow for the device (the equivalent of a stock launcher's swipe-up app drawer). Tapping a tile starts that workflow directly, same as a home-grid button. |
| `if` | Checks `lastResultCode == if_result_code`. Jumps to `then_step` if true, `else_step` if false. Falls back to next by sort_order if targets not set. |
| `label` | No-op. Used as a named jump target for `if` steps. Advances to next step by sort_order. |
`lastResultCode` is preserved across all steps, so a value set in step 2 is still readable by step 5.
### Result codes
| result_code | emitted by |
|---|---|
| `cancel` | any `show_content` button tagged as cancel, or `show_speaker_choice` (user tapped cancel) — always ends the workflow |
| `speakerphone` | `show_speaker_choice` |
| `normal` | `show_speaker_choice` |
---
## NocoBase schema
**Server:** `https://nocobase.rucki.ch`
**API notes:**
- Filter only on direct fields of the collection — nested relation filters (e.g. `device.device_id`) are not supported. To filter workflows by device, do a two-step lookup: resolve `device_id` → device PK via `/api/devices:list`, then filter workflows by `device_key`.
- Appends use `appends[]` query parameter (array format).
- When creating relations, always specify the existing FK explicitly in advanced options. If you let NocoBase auto-generate, it creates a second FK column and rows added later won't appear in API responses.
- Create belongsTo relations first (they create the FK column), then hasMany reusing the same FK.
---
### Tables
| Table | Purpose |
|---|---|
| `device_users` | One row per patient (renamed from `beneficiaries`) |
| `devices` | One row per physical device; linked to a device user |
| `contacts` | Contacts; linked to device user (not device — shared across all their devices) |
| `workflow_function` | Global catalog of executable functions (defined by the app) |
| `workflow` | One row per home screen button per device |
| `workflow_step` | Steps within a workflow; each calls one function |
| `content_block` | Ordered UI content (heading / text / button) for screen steps |
| `event_log` | One row per completed workflow step — used by the caregiver dashboard |
---
### `device_users`
| field | type |
|---|---|
| `name` | string |
| `date_of_birth` | date |
| `notes` | text |
---
### `devices`
| field | type | notes |
|---|---|---|
| `device_id` | string | unique; used by the app to fetch its config (underscores, e.g. `redmi_test`) |
| `label` | string | human-readable name e.g. "Renate's tablet" |
| `beneficiary` | belongsTo → beneficiaries | |
---
### `contacts`
| field | type | notes |
|---|---|---|
| `name` | string | |
| `phone` | string | |
| `photo_url` | text (url) | |
| `device_user` | belongsTo → device_users | linked to the person, shared across all their devices |
---
### `workflow_function` — global catalog, defined by the app
| field | type | notes |
|---|---|---|
| `function_id` | string | unique identifier used by the app |
| `name` | string | displayed in the web app step editor |
| `description` | text | |
**Current entries:**
| function_id | name |
|---|---|
| `show_speaker_choice` | Show Speaker Choice |
| `call_contact` | Call Contact |
| `open_app` | Open App |
| `show_content` | Show Content |
| `show_app_list` | Show App List |
| `if` | If / Branch |
| `label` | Label |
---
### `workflow` — one per home screen button per device, or one per auto-synced installed app
| field | type | notes |
|---|---|---|
| `name` | string | e.g. "Call Sandro", or the app's label for `workflow_type == "app"` |
| `homepage_position` | integer / null | 1-indexed position in the 2-column grid; null = not on home screen |
| `button_span` | integer | 1 = normal (1 cell), 2 = wide (full row); default 1 |
| `workflow_type` | select | `custom` (default, caregiver-authored) or `app` (auto-synced from an installed app) |
| `package_name` | string / null | set only for `workflow_type == "app"`; matched against on re-sync so a caregiver's added tutorial step or homepage_position always survives |
| `is_active` | boolean | for `app` workflows: set to `false` on uninstall instead of deleting, so a reinstall reactivates the same row rather than duplicating it |
| `device` | belongsTo → devices | |
| `workflow_steps` | hasMany → workflow_step | fetched via `workflow_steps` append |
---
### `workflow_step` — steps execute in `sort_order` ascending; lowest = start step
| field | type | notes |
|---|---|---|
| `name` | string | |
| `sort_order` | integer | lowest value = start step |
| `parameter_json` | text | static inputs e.g. `{"phone": "+41768224400"}` |
| `function` | belongsTo → workflow_function | app reads `function_id` from the related record |
| `if_result_code` | string / null | `if` steps only: the `lastResultCode` value to match |
| `then_step` | belongsTo → workflow_step / null | `if` steps only: jump target when condition is true |
| `else_step` | belongsTo → workflow_step / null | `if` steps only: jump target when condition is false |
| `content_block` | hasMany → content_block | fetched via `workflow_steps.content_block` append |
> **Unused DB fields** (can be deleted from NocoBase): `output_id`, `input_mapping_json`, `is_active`
---
### `event_log` — one row per completed workflow step
Written by the Android app fire-and-forget after each step. No UI impact.
| field | type | notes |
|---|---|---|
| `device_id` | string | matches `devices.device_id` |
| `workflow_name` | string | name of the running workflow |
| `function_id` | string | step type that completed |
| `result_code` | string / null | outcome (`confirmed`, `cancelled`, `speakerphone`, `normal`, or null for automatic steps) |
| `duration_seconds` | number | time the patient spent on this step |
| `created_at` | datetime | auto-set by NocoBase |
The caregiver web app dashboard groups these rows by device user → workflow → function+outcome to show usage statistics.
---
### `content_block` — ordered content elements for screen steps
| field | type | notes |
|---|---|---|
| `type` | select | `heading`, `text`, `image`, `button` |
| `value` | text | display text, image URL, or button label |
| `sort_order` | integer | render order |
| `result_code` | string | `button` type only — emitted when tapped |
| `workflow_step` | belongsTo → workflow_step | |
---
### Relation map
| Relation | Type | FK column |
|---|---|---|
| `devices.device_user` | belongsTo → device_users | `device_users_key` on `devices` |
| `contacts.device_user` | belongsTo → device_users | `device_users_key` on `contacts` |
| `workflow.device` | belongsTo → devices | `device_key` on `workflow` |
| `workflow.workflow_steps` | hasMany → workflow_step | `workflow_key` on `workflow_step` |
| `workflow_step.function` | belongsTo → workflow_function | `function_key` on `workflow_step` |
| `workflow_step.then_step` | belongsTo → workflow_step | `then_step_key` on `workflow_step` |
| `workflow_step.else_step` | belongsTo → workflow_step | `else_step_key` on `workflow_step` |
| `workflow_step.content_block` | hasMany → content_block | `content_block_key` on `content_block` |
---
## Example: Call Sandro (simple — no speaker choice)
| sort_order | function_id | parameter_json |
|---|---|---|
| 1 | `call_contact` | `{"phone": "+41768224400"}` |
Steps run in order. No transitions, no if steps needed.
---
## Example: Call Sandro (with speaker choice via if step)
| sort_order | function_id | parameter_json | if_result_code | then_step | else_step |
|---|---|---|---|---|---|
| 1 | `show_speaker_choice` | — | — | — | — |
| 2 | `if` | — | `speakerphone` | → step 3 | → step 4 |
| 3 | `label` | — | — | — | — |
| 4 | `call_contact` | `{"phone":"…","speakerphone":true}` | — | — | — |
| 5 | `label` | — | — | — | — |
| 6 | `call_contact` | `{"phone":"…","speakerphone":false}` | — | — | — |
Step 2 reads `lastResultCode` from step 1. If it equals `"speakerphone"`, jumps to step 3 (label) → step 4 (call with speaker). Otherwise jumps to step 5 (label) → step 6 (normal call).
---
## File structure
```
app/src/main/java/com/example/ichwillheim/
├── MainActivity.kt — entry point, step dispatcher (the `when` block), call utilities
├── ui/
│ ├── HomeScreen.kt — patient home screen (clock, workflow buttons)
│ ├── ContentScreen.kt — data-driven content renderer for show_content steps
│ ├── SpeakerChoiceScreen.kt — WorkflowSpeakerChoiceScreen, SpeakerOptionHalf
│ ├── PinScreen.kt — PIN entry screen
│ ├── LauncherSettingsScreen.kt — SettingsMenuScreen, LauncherSettingsScreen, LanguagePicker
│ └── theme/ — Color.kt, Theme.kt, Type.kt
├── viewmodel/
│ └── LauncherViewModel.kt — screen state + navigation flags, survives config changes
├── domain/
│ ├── WorkflowModels.kt — Workflow, WorkflowStep, ContentBlock
│ └── WorkflowEngine.kt — WorkflowRunState: advance(), param(), start()
├── data/
│ ├── SettingsRepository.kt — AppSettings + SharedPreferences
│ ├── RemoteConfigRepository.kt — Ktor HTTP client; fetches workflows from NocoBase
│ └── InstalledApps.kt — LauncherApps enumeration of launchable apps
└── service/
└── CallOverlayService.kt — foreground service for call overlay
```
---
## Internationalisation
All user-facing strings live in `res/values/strings.xml` (English default).
| Folder | Language |
|---|---|
| `res/values/` | English (default) |
| `res/values-de/` | German |
| `res/values-es/` | Spanish |
---
## Permissions
| Permission | Why |
|---|---|
| `CALL_PHONE` | Place calls directly without the dialer |
| `READ_PHONE_STATE` | Detect call state (for speakerphone timing) |
| `ANSWER_PHONE_CALLS` | Answer incoming calls from the overlay |
| `MODIFY_AUDIO_SETTINGS` | Switch to speakerphone |
| `SYSTEM_ALERT_WINDOW` | Draw call overlay — must be granted manually |
| `FOREGROUND_SERVICE` + `FOREGROUND_SERVICE_SPECIAL_USE` | Run CallOverlayService |
| `POST_NOTIFICATIONS` | Required on Android 13+ for foreground service notification |
---
## Security
The app authenticates to NocoBase via a `Bearer` token in every request. The token is stored in `local.properties` (never committed) and injected at build time via `BuildConfig`.
**Before any public release:** replace with a proper auth layer — caregiver accounts, server-side token exchange, per-user data isolation in NocoBase. The database schema does not need to change; only the authentication mechanism on top of it.
---
## Color palette
Both the Android launcher and the caregiver web app use the same three-color system:
| Role | Hex | Usage |
|---|---|---|
| Background / surface | `#FFF6E5` | Screen background, sidebar, cards |
| Primary | `#A6CDB2` | Buttons, active nav items, focus rings |
| Accent | `#FCAB7E` | Cancel buttons, secondary actions, highlights |
| Text | `#2D2D2D` | All foreground text on the above backgrounds |
All three background colors pass WCAG AA contrast with `#2D2D2D` text (≥ 7:1).
---
## Ideas backlog
- **Custom caregiver web app** — onboarding questionnaire (generates first workflows) + manual editor; eventually iOS/Android caregiver app
- **Unified inbox** — SMS + WhatsApp in one patient-facing screen
- **Photo slideshow management** — upload/manage slideshow photos remotely via NocoBase
- **Message effectiveness tracking** — log whether reminders led to calls or cancellations
- **Caregiver accounts** — multiple caregivers per patient, each with their own login
- **Community platform** — caregivers share workflow presets, tips, and experiences