Skip to content

Astradial Android — Native UI Specification (Samsung One UI)

Goal: a user who picks up the app must believe it is the Samsung Galaxy Phone app. This page is the screen-by-screen contract. The canonical reference is the stock Samsung Phone / Contacts app on the founder's Galaxy device (One UI 6/7) — when this text and the device disagree, the device wins.

Hard rule: native One UI, no cross-platform UI

The UI is native Android — Kotlin + XML layouts / Material Components themed to One UI (same toolkit Linphone already uses; no Compose rewrite required, no Flutter, no React Native). The acceptance bar is Samsung One UI, not stock Pixel/Material You. The three Figma kits referenced below are Material-3 component sources — re-theme them to One UI, don't ship them as-is.

This is the Android counterpart to the iOS UI Spec; the strategy (Linphone engine, native dialer skin) is identical — see Overview. The engine and platform plumbing come from linphone-android (Kotlin, MVVM, XML layouts + data binding, package org.linphone); we keep the view-models and replace the views.

Design references

Design-system ground rules

These apply to every screen. They are what makes the app feel like One UI; correct layouts with these wrong still read as fake.

Aspect Rule
Toolkit Android Views + data binding + Material Components for Android, themed to One UI. No custom-drawn lookalikes where a platform/Material control exists.
One UI layout law Top half = title/empty "viewing area", bottom half = controls (one-handed reach). One UI uses a large collapsing CollapsingToolbarLayout/AppBarLayout header that shrinks on scroll; primary actions sit low. Match this — it is the single most recognisable One UI trait.
Typography System sans-serif. On Galaxy hardware the system font is SamsungOne/SamsungSharpSans, so using system text styles gives One UI typography for free — never bundle a lookalike font, never hard-code sizes.
Icons Material Symbols / the One UI line-icon style (rounded, medium weight). No Linphone iconography survives.
Colors One UI tokens via theme attributes (?attr/colorPrimary, ?attr/colorSurface, ?android:attr/colorBackground)—never hard-code hex. Dynamic color (Material You / One UI palette) on Android 12+; system green for the call FAB, system red for missed/decline. Dark mode automatic via -night resources.
Bottom navigation One UI BottomNavigationView (replaces Linphone's bottom_nav_bar.xml). Large touch targets, label under icon, One UI selected-pill.
Lists One UI rounded list cells / MaterialCardView grouping where stock does; large headers; section index where stock Contacts shows the A–Z rail.
Ripples & motion System ripple + One UI's spring/over-scroll (EdgeEffect) defaults. No custom transition curves.
Haptics One UI keypad haptics: HapticFeedbackConstants on digit press + system keypad tone (match stock dialer).
Accessibility TalkBack labels on every control, large-font/Dynamic-type support — stock One UI supports these, so must we.

Tabs

OSS dialer (public repo) ships the three pure-dialer tabs; the private overlay adds the two MD tabs (see Overview → clean split and MD Analytics). Full app, in One UI bottom-nav order:

Keypad · Recents · Contacts · Analytics¹ · Tickets¹ (¹ private overlay)

Replaces Linphone's MainActivity bottom-nav set (Contacts / Calls / Conversations / Meetings). Conversations & Meetings are removed from the UI for v1 (engine support stays).

Tab — Keypad (dialer)

Linphone source: ui/main/history/fragment/StartCallFragment.kt + NumpadModel.kt + StartCallViewModel.kt, layouts start_call_fragment.xml / call_numpad_* (note: Linphone already draws a squircle numpad — shape_squircle_numpad_background.xml — which is close to One UI; restyle, don't rebuild from scratch). Keep the VM, replace the layout.

One UI keypad, top → bottom: 1. SIP-line/identity chip (top): selected outbound account; tap → account picker bottom sheet. Shown even with one line (match stock dual-SIM chip). 2. Number display: large, centred, shrinks as it grows; matched-contact / "Add to contacts" hint below; backspace appears once non-empty. 3. Numpad grid: 4×3 One UI squircle keys; digit large, letters small below (ABC…); long-press 0+; One UI press haptic + DTMF tone. 4. Green call FAB centred below the grid; empty-number tap recalls the last dialled number (stock behaviour).

Function mapping: digits feed liblinphone's outgoing-address builder; during a call the same pad sends DTMF (RFC 4733).

Tab — Recents (call logs)

Linphone source: ui/main/history/fragment/HistoryListFragment.kt + HistoryListViewModel.kt + HistoryListAdapter.kt. Keep VM/adapter data, replace the row + header layouts.

  1. Large One UI header Recents, collapsing on scroll; search affordance.
  2. All / Missed filter (One UI segmented/tab strip).
  3. Rows: avatar (photo / initials / generic), name or number bold (missed in red), secondary line = direction glyph (in/out/missed) + SIP-line badge
  4. number label; right = relative time + info chevron → call detail. Consecutive same-party calls collapse with a (N) count.
  5. Swipe / long-press: delete one, clear all, call back.

Data source: liblinphone CallLog history (core.callLogs); missed filter = missed/declined/aborted statuses.

Tab — Contacts

Linphone source: ui/main/contacts/fragment/ContactsListFragment.kt + ContactsListViewModel.kt + ContactViewModel.kt + adapters.

Org directory, NOT device contacts (matches the shipped iOS app)

Like the iOS app, the Contacts tab shows the Astradial org Users & Extensions directory, not the device address book — so we drop the READ_CONTACTS permission (see security audit). Linphone's ContactsManager (ContactsContract bridge) is kept only to render SIP Friends; the device-contacts read path is disabled.

  1. Large One UI header Contacts, collapsing; search bar with the One UI look; + for a new entry where applicable.
  2. A–Z sticky sections + index rail on the right edge (One UI style).
  3. Rows: initials/photo avatar + name; tap → One UI contact detail with the action row (call / message / video — non-call actions may hand off to system apps); numbers tap-to-call via liblinphone.

In-call screen

Use the Android Telecom ConnectionService path Linphone already wires (core/CoreInCallService.kt, androidx.telecom, MANAGE_OWN_CALLS, USE_FULL_SCREEN_INTENT). On a Galaxy this surfaces Samsung's own system in-call UI / full-screen incoming-call screen — the strongest "native" signal, the One UI equivalent of iOS CallKit. The in-app call screen (ui/call/CallActivity.kt, restyled) must mirror the One UI call layout: large name/number top, timer, the One UI button grid (mute, keypad, speaker, add call, hold, video-slot→hidden/transfer), red end-call bottom-centre, with the in-call numpad as a One UI bottom sheet (call_numpad_bottom_sheet.xml).

What we deliberately discard from Linphone's UI

Linphone 6's branding (orange theme, its assistant/onboarding, sidebar/drawer DrawerMenuFragment, chat & meetings styling, its tab structure) is entirely replaced. Keep the view-model ↔ core wiring where convenient; no Linphone visual element survives into Astradial builds — app icon, splash, colours, layouts, fonts, the assistant onboarding. Provisioning is Astradial QR scan + manual SIP entry only (the OSS dialer ships authless; Firebase sign-in is the private overlay).

Acceptance test for "100% native One UI"

Side-by-side with the stock Samsung Phone app on the same Galaxy:

  1. Same bottom-nav tabs, icons, order, selected-pill look.
  2. One UI header collapse + bottom-weighted controls match (the one-handed law) on every screen.
  3. Keypad squircle geometry, letter labels, spacing, call-FAB size, press haptic + tone indistinguishable.
  4. Recents rows: same two-line structure, direction glyphs, badges, timestamp wording, info affordance.
  5. Contacts: same search, A–Z rail, section headers, contact-detail action row.
  6. Dark mode, largest font scale, and TalkBack all behave like One UI.
  7. Incoming call raises the Samsung system call screen (Telecom), not a custom one.
  8. A failed item is a bug, not a nice-to-have.