357 lines
15 KiB
Markdown
357 lines
15 KiB
Markdown
# 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_call_confirm` | Shows a confirm/cancel screen. Emits `confirmed`. Cancel always ends the workflow. |
|
||
| `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"`. |
|
||
| `show_reminder` | Renders `ContentScreen` with the step's `content_block` list. Advances with `null` when the patient taps the button. |
|
||
| `show_tutorial` | Same as `show_reminder` — mechanically identical. Both render `ContentScreen`. |
|
||
| `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 |
|
||
|---|---|
|
||
| `confirmed` | `show_call_confirm` (user tapped confirm) |
|
||
| `cancelled` | `show_call_confirm` or `show_speaker_choice` (user tapped cancel) |
|
||
| `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_call_confirm` | Show Call Confirm |
|
||
| `show_speaker_choice` | Show Speaker Choice |
|
||
| `call_contact` | Call Contact |
|
||
| `if` | If / Branch |
|
||
| `label` | Label |
|
||
|
||
---
|
||
|
||
### `workflow` — one per home screen button per device
|
||
|
||
| field | type | notes |
|
||
|---|---|---|
|
||
| `name` | string | e.g. "Call Sandro" |
|
||
| `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 |
|
||
| `is_active` | boolean | |
|
||
| `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 | `show_call_confirm` | — |
|
||
| 2 | `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_call_confirm` | — | — | — | — |
|
||
| 2 | `show_speaker_choice` | — | — | — | — |
|
||
| 3 | `if` | — | `speakerphone` | → step 4 | → step 5 |
|
||
| 4 | `label` | — | — | — | — |
|
||
| 5 | `call_contact` | `{"phone":"…","speakerphone":true}` | — | — | — |
|
||
| 6 | `label` | — | — | — | — |
|
||
| 7 | `call_contact` | `{"phone":"…","speakerphone":false}` | — | — | — |
|
||
|
||
Step 3 reads `lastResultCode` from step 2. If it equals `"speakerphone"`, jumps to step 4 (label) → step 5 (call with speaker). Otherwise jumps to step 6 (label) → step 7 (normal call).
|
||
|
||
---
|
||
|
||
## File structure
|
||
|
||
```
|
||
app/src/main/java/com/example/ichwillheim/
|
||
│
|
||
├── MainActivity.kt — entry point, navigation state, workflow runner, call utilities
|
||
├── HomeScreen.kt — patient home screen (clock, workflow buttons)
|
||
├── ContentScreen.kt — data-driven content renderer for show_reminder / show_tutorial steps
|
||
├── PreCallStepsScreen.kt — WorkflowConfirmScreen (show_call_confirm)
|
||
├── SpeakerChoiceScreen.kt — WorkflowSpeakerChoiceScreen, SpeakerOptionHalf
|
||
├── PinScreen.kt — PIN entry screen
|
||
├── LauncherSettingsScreen.kt — SettingsMenuScreen, LauncherSettingsScreen, LanguagePicker
|
||
├── CallOverlayService.kt — foreground service for call overlay
|
||
├── WorkflowModels.kt — Workflow, WorkflowStep, ContentBlock
|
||
├── WorkflowEngine.kt — WorkflowRunState: advance(), param(), start()
|
||
├── SettingsRepository.kt — AppSettings + SharedPreferences
|
||
└── RemoteConfigRepository.kt — Ktor HTTP client; fetches workflows from NocoBase
|
||
```
|
||
|
||
---
|
||
|
||
## 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
|