# Architecture: Telefon (Android, ohne Google) Same conventions as the other apps. **No network permission.** The app owns almost no data: it reads the system call log and contacts. ## Stack | Schicht | Wahl | warum | | --- | --- | --- | | UI | Kotlin + Compose, M3 Expressive, dynamic colour | same as the others | | Telefonie | `RoleManager.ROLE_DIALER` + own `InCallService` (BIND_INCALL_SERVICE, IN_CALL_SERVICE_UI) | the only official way to show the call UI and control calls | | Anrufen | `TelecomManager.placeCall` | uses the default SIM/phone account, works for emergency numbers through the system | | Daten | `CallLog.Calls` and `ContactsContract` via ContentResolver, observed with ContentObserver → Flow | no copy of personal data in our database | | Eigene Daten | DataStore (quick replies, theme) | – | | Audio | `InCallService.setMuted / setAudioRoute`, `CallEndpoint` on API 34+ | speaker, earpiece, Bluetooth | | Näherungssensor | `PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK` while the earpiece is active | screen off at the ear | ## Permissions READ_CALL_LOG, WRITE_CALL_LOG (delete entries), READ_CONTACTS, CALL_PHONE, READ_PHONE_STATE, MANAGE_OWN_CALLS no, USE_FULL_SCREEN_INTENT, POST_NOTIFICATIONS, WAKE_LOCK, SEND_SMS no (quick replies hand off to the SMS app with `smsto:`). The dialer role grants most of these automatically once the user accepts it. ## Data No schema. Own state in DataStore only. Access: single user; the system enforces the permissions. ## Interne API | Aufruf | macht | Flow | | --- | --- | --- | | `CallLogRepo.observe(filter)` | query CallLog, join contact name/photo by number (PhoneLookup), group by day | F01 | | `ContactsRepo.favorites()` / `search(q)` / `t9(digits)` | starred contacts; name/number search; T9 digit match on names and numbers | F02, F03, F06 | | `Dialer.call(number)` | `placeCall(tel:)` | F01–F03 | | `CallManager` (in `InCallService`) | StateFlow of calls; answer, reject (with SMS), hold, unhold, mute, route, DTMF, disconnect | F04, F05, F07 | | `Role.request()` | role request intent | F08 | ## Was beißt - **Wrong default dialer state**: without the role, calls still work through the system dialer; the app shows a banner instead of failing. - **Lock screen**: incoming UI via full-screen-intent notification; `showWhenLocked` + `turnScreenOn` on the activity. - **Ringtone**: the system rings for the default dialer? No: the InCallService app must play the ringtone itself only when `Call.Details` say so; on modern Android the platform `Ringer` plays it for the default dialer. We do not play our own. - **Process death mid-call**: the InCallService is bound by the system as long as calls exist; UI state is rebuilt from `calls`. - **Emergency numbers**: always via `placeCall`, never blocked by our UI. - **Privacy**: no network, no analytics; call log stays in the system. ## Build-Reihenfolge 1. Slice: role request, InCallService, incoming + in-call screens, place a call from the keypad. 2. Must: call log with grouping and call back, favourites, search, notifications, mute/speaker/DTMF, proximity. 3. Should: filters, T9, hold, call waiting, quick reply SMS, details screen, voicemail, Bluetooth.