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
S
Description
No description provided
Readme 264 KiB
Languages
Kotlin 100%