diff --git a/README.md b/README.md index 4673792..95ecf8f 100644 --- a/README.md +++ b/README.md @@ -2,41 +2,62 @@ English | [Tiếng Việt](README.vi.md) # handlive-android -The HandLive app for Android phones, written in Kotlin (minSdk 29). The app runs continuity features, such as Handoff, on Android. The phone stays in sync with a Mac, iPhone and iPad, and those Apple devices sync back. +[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) -Kotlin, Gradle KTS, AGP 9.4, Kotlin 2.4, compileSdk 37 / targetSdk 35, Jetpack Compose. The UI is multilingual: English by default, Vietnamese as the second language (strings come from `../shared/strings`, detailed design 0.12). Specification: `../docs/detailed-design/` (hub repository). Plan: `../plans/20260925-implementation/`. +The HandLive companion service and application for Android phones, written in Kotlin with Jetpack Compose (minSdk 29, compileSdk 37). It connects Android with macOS, iOS, and iPadOS devices to provide two-way clipboard sync, an SMS bridge, and call notifications with action controls. -This repository is one part of the HandLive workspace: the hub repository `handlive` (docs, plans) is the parent directory, and `../shared` is the `handlive-shared` repository (test vectors, schemas, design tokens); the `core/design` tests also read the hub's `../docs/design-system`. Clone the whole set from the hub with `tools/workspace.sh clone `. See this repository's `CLAUDE.md`. +## Overview + +- **Cross-platform continuity:** Seamlessly syncs clipboard, bridges SMS messages, and displays call notifications across your devices. +- **End-to-end encrypted:** All data transferred over local Wi-Fi or through the relay is protected with XChaCha20-Poly1305 and X25519 session keys. +- **Two build flavors:** + - `foss` (default): Fully open-source build without Google Play Services or Firebase dependencies; connects directly over LAN or via WebSocket relay. + - `gms`: Adds Firebase Cloud Messaging (FCM) high-priority data wake-ups. +- **Multilingual UI:** English by default, Vietnamese as second language, with localized strings generated from `handlive-shared`. +- **Call integration without `InCallService`:** Uses `TelecomManager` for actions (Answer, Decline, End) and system telephony listeners without invasive call-handling services. Call audio on the computer is not supported (Phase 4 stopped). + +## Workspace context + +This repository is one component of the HandLive workspace: +- Hub repository [`handlive`](https://github.com/HandLive/handlive) (docs, plans) is the parent directory. +- [`handlive-shared`](https://github.com/HandLive/handlive-shared) (`../shared`) supplies shared protocol schemas, test vectors, and UI strings. +- Clone the full workspace from the hub using `tools/workspace.sh clone `. See [`CLAUDE.md`](CLAUDE.md) for local workspace workflows. ## Layout | Path | Contents | |------|----------| -| `app/` | Compose app, package `app.handlive.android`: first-run setup (SET-01), the Devices and Settings tabs with pairing, device details (clipboard, SMS and calls per device), the Accessibility disclosure, the SMS and Calls switches with their permission primers, Calls from Other Apps with the Notification access primer, Permissions & Background, and Remove Device from Server / Delete All HandLive Data; installs the features at process start. Two flavors: `foss` (default, no Play Services or Firebase) and `gms` (FCM wake-ups, see Build settings) | -| `buildSrc/` | Task `:core:design:generateHandLiveTheme` generates `HandLiveTheme` from `../shared/design-tokens/tokens.json`; `:core:strings:generateStringResources` generates the string resources from `../shared/strings/ui-strings.json` | -| `core/protocol` | Envelope, Payload, Ack, `ErrorCode` (0.8.1), HL frames, clipboard chunks, SMS and `call_event` messages, relay wrappers, control ops, `HR` frames and REST bodies, UUIDv7, b64/b64u; test fixtures read `../shared` and validate against its JSON schemas | -| `core/crypto` | Tink XChaCha20-Poly1305, X25519, Ed25519, HKDF, `device_id`, PRK, session/rekey/stream key schedule, `K_push` and push envelopes, `hl_master` key store | -| `core/transport` | Ktor/Netty WSS (TLS 1.3, self-signed P-256 certificate), server-side handshake, capability, rekey, session replacement (4409), admission limits and idle close (4410/4411/4429); the relay client on OkHttp with certificate pinning (registration, JWT, REST, `/v1/relay`) and `/v1/ctl` sessions carried over the relay; instrumented Netty + TLS smoke test in `src/androidTest` | -| `core/data` | Room `handlive.db` version 3 (`paired_device`, `sms_observer_state`, `push_outbox` with the `reason` of each queued push; schemas in `core/data/schemas`), DataStore settings keys (0.9.5), `HandLiveData` container | -| `core/strings` | String resources generated at build time from the catalog (English default, Vietnamese); tests match them to the catalog and forbid hard-coded UI text | -| `core/design` | `HandLiveTheme` (4 appearances, Inter / Be Vietnam Pro / Roboto Mono), `HLButton`, `HLSwitch`, `HLGroupedList`, `HLStatusIndicator`, `HLIcon` (Material Symbols Rounded) | -| `feature/connection` | `HandLiveService` (foreground service `connectedDevice`), TLS server on 47800–47809 with `/v1/pair`, mDNS with hourly hints, capability updates (clipboard, SMS, calls, relay, missing permissions), envelope routing, `HLBENCH/1` lines in debug builds | -| `feature/pairing` | Pairing by QR code (CameraX + ZXing core) or PIN (Argon2id), device list and Security Code, unpairing | -| `feature/clipboard` | Clipboard sync: copy detection through Accessibility, `ClipboardReadActivity`, Send Clipboard button, Quick Settings tile and Share target, writes and forwarding, chunked images, safe auto-clear | -| `feature/sms` | SMS bridge: `ContentObserver` on the SMS provider (no `RECEIVE_SMS`), `sms/new`, paged `sms/sync` and `sms/history`, sending with `SmsManager` (SIM choice, multipart, `SendRegistry`, forward-only statuses), `sms/read_changed`, the permission suggestion notification, `HLBENCH/1` SMS events | -| `feature/call` | Calls without `InCallService` (C12): the call state listeners (`TelephonyCallback` / `PhoneStateListener`, default and per SIM) and the `PHONE_STATE` receiver feed the call context (`call_id`, waiting calls, late numbers, SIM labels, rebuilt midway); `call_event/state` per session with its `controls`; `answer`, `reject`, `end` through `TelecomManager` (hold, DTMF, mute answer `CALL_HFP_REQUIRED`); `call_event/log_sync` pages and `log_new` from a `ContentObserver` on the call log, with the `end_reason` correction; the incoming and missed calls handed to the relay for iPhone and iPad without a session; `HLBENCH/1` call events; calls of other apps (CALL-05, `appcall/`): a `NotificationListenerService` reads only call notifications (`CallStyle` or an ongoing notification linked to a ringing call within 3 s), keeps their contexts in memory, sends `call_event/app_call` to the sessions with `features.call.app_calls` in effect and carries out `answer`, `reject`, `end` with the app's own PendingIntents (a "tap to answer" notification without the background-start exemption) | -| `feature/relay` | The phone on the relay: `/v1/relay` only while a client may be waiting there (idle close after 5 minutes, `RECONNECT_BACKOFF`; held open while a call rings), device, pair and push token registration, remote revocation, pairing through a rendezvous, alert pushes for iPhone and iPad sealed with `K_push` (new SMS, incoming and missed calls) with the `push_outbox` retry queue, and the device deletion of SET-02 | -| `config/` | ktlint, detekt | +| `app/` | Compose app (`app.handlive.android`): first-run setup (SET-01), Devices and Settings tabs, pairing flows, per-device details, Accessibility disclosures, SMS and Calls permission primers, background execution settings, and device deregistration. Flavors: `foss` (default) and `gms` | +| `buildSrc/` | Code generation tasks: `:core:design:generateHandLiveTheme` from `../shared/design-tokens/tokens.json`; `:core:strings:generateStringResources` from `../shared/strings/ui-strings.json` | +| `core/protocol` | Wire formats: Envelope, Payload, Ack, `ErrorCode` (0.8.1), HL frames, clipboard chunks, SMS and `call_event` messages, relay wrappers, control ops, `HR` frames, REST bodies, UUIDv7, Base64/Base64URL; validates against schemas in `../shared` | +| `core/crypto` | Cryptographic engine: Tink XChaCha20-Poly1305, X25519, Ed25519, HKDF, `device_id`, PRK, session/rekey/stream key schedules, `K_push`, and `hl_master` KeyStore storage | +| `core/transport` | Embedded Ktor/Netty WSS server (TLS 1.3, self-signed P-256 certificate), handshake, capability exchange, session management (4409 replacement, 4410/4411/4429 idle/limits); OkHttp relay client with certificate pinning | +| `core/data` | Room database `handlive.db` v3 (`paired_device`, `sms_observer_state`, `push_outbox`), DataStore settings keys (0.9.5), and `HandLiveData` container | +| `core/strings` | Build-time string resources generated from the catalog; unit tests enforce catalog coverage and prevent hardcoded UI text | +| `core/design` | `HandLiveTheme` (4 appearance variants, Inter / Be Vietnam Pro / Roboto Mono typography), `HLButton`, `HLSwitch`, `HLGroupedList`, `HLStatusIndicator`, `HLIcon` | +| `feature/connection` | `HandLiveService` (foreground service), TLS server on ports 47800–47809, mDNS discovery with hourly hints, capability synchronization, envelope routing, and `HLBENCH/1` logging in debug builds | +| `feature/pairing` | Pairing via QR code (CameraX + ZXing) or PIN (Argon2id), paired device list, Security Code verification, and unpairing | +| `feature/clipboard` | Two-way clipboard synchronization: Accessibility copy detection, `ClipboardReadActivity`, Quick Settings tile, Share target, chunked image transfer, and safe auto-clear | +| `feature/sms` | SMS bridge: `ContentObserver` on SMS provider (no `RECEIVE_SMS`), `sms/new`, paged sync (`sms/sync`, `sms/history`), multi-part sending via `SmsManager`, forward-only status updates, and read state sync | +| `feature/call` | Call monitoring and controls: telephony listeners and `PHONE_STATE` receiver feeding call state contexts; `answer`, `reject`, `end` via `TelecomManager`; call log sync via `ContentObserver`; alert pushes for incoming and missed calls; app call support (`appcall/`) | +| `feature/relay` | Relay client: on-demand `/v1/relay` connection, device registration, pairing rendezvous, and alert push triggers (`K_push`) with `push_outbox` retry queue | +| `config/` | Linter configs: ktlint, detekt | + +## Requirements + +- **JDK:** Version 21 (e.g. OpenJDK 21) +- **Android SDK:** Compile SDK 37 (`platforms;android-37.0`), Min SDK 29, Target SDK 35 +- **Build tools:** Android Gradle Plugin (AGP) 9.4 with Kotlin 2.4 ## Build settings -Nothing secret is in git; every setting is a Gradle property (`~/.gradle/gradle.properties` or `-P`) or an environment variable, and the build succeeds without them (CI sets none): +Configuration uses Gradle properties (`~/.gradle/gradle.properties` or `-P`) or environment variables. All settings are optional; the project builds clean with defaults: -| Gradle property | Environment variable | Use | -|-----------------|----------------------|-----| -| `handlive.relayHost` | `HANDLIVE_RELAY_HOST` | The relay host (CONN-03); empty = a build without the relay | -| `handlive.relayExtraPins` | `HANDLIVE_RELAY_EXTRA_PINS` | Extra `sha256/` certificate pins, comma-separated, next to ISRG Root X1 and X2 in the code | -| `handlive.fcm.applicationId`, `.apiKey`, `.projectId`, `.senderId` | `HANDLIVE_FCM_APPLICATION_ID`, `_API_KEY`, `_PROJECT_ID`, `_SENDER_ID` | `gms` flavor only: the Firebase options for FCM wake-ups (no `google-services.json`); empty = no push | +| Gradle property | Environment variable | Description | +|-----------------|----------------------|-------------| +| `handlive.relayHost` | `HANDLIVE_RELAY_HOST` | Relay server hostname (CONN-03); empty builds without relay support | +| `handlive.relayExtraPins` | `HANDLIVE_RELAY_EXTRA_PINS` | Additional `sha256/` certificate pins, comma-separated, complementing ISRG Root X1/X2 | +| `handlive.fcm.applicationId`, `.apiKey`, `.projectId`, `.senderId` | `HANDLIVE_FCM_APPLICATION_ID`, `_API_KEY`, `_PROJECT_ID`, `_SENDER_ID` | `gms` flavor only: Firebase credentials for FCM wake-up pushes; empty disables push | ## Commands @@ -48,6 +69,15 @@ HL_WRITE_ROUNDTRIP=1 ./gradlew :core:crypto:test # rewrites ../shared/test-vec adb logcat -s HLBENCH # debug builds: benchmark lines for ../shared/tools/bench ``` +## Documentation and community + +- [System Architecture](https://github.com/HandLive/handlive/blob/main/docs/system-architecture.md) — System design and security model +- [Detailed Design](https://github.com/HandLive/handlive/tree/main/docs/detailed-design) — Protocols, formats, and specifications +- [Deployment Guide](https://github.com/HandLive/handlive/blob/main/docs/deployment-guide.md) — Packaging and distribution guidelines +- [CONTRIBUTING](https://github.com/HandLive/.github/blob/main/CONTRIBUTING.md) — PR workflow, small commits, and DCO sign-off (`git commit -s`) +- [SECURITY](https://github.com/HandLive/.github/blob/main/SECURITY.md) — Vulnerability reporting policy +- [CODE_OF_CONDUCT](https://github.com/HandLive/.github/blob/main/CODE_OF_CONDUCT.md) — Community standards + ## License -Apache License 2.0 — see [LICENSE](LICENSE); bundled fonts and icons keep their own licenses, listed in [NOTICE](NOTICE). Contributions follow [CONTRIBUTING](https://github.com/HandLive/.github/blob/main/CONTRIBUTING.md) (small commits under a real name, DCO sign-off with `git commit -s`); report vulnerabilities privately as described in [SECURITY](https://github.com/HandLive/.github/blob/main/SECURITY.md). +Apache License 2.0 — see [LICENSE](LICENSE). Bundled fonts and icons retain their respective upstream licenses, listed in [NOTICE](NOTICE). diff --git a/README.vi.md b/README.vi.md index f9731c4..21795d1 100644 --- a/README.vi.md +++ b/README.vi.md @@ -2,52 +2,82 @@ # handlive-android -Ứng dụng trên điện thoại Android, viết bằng Kotlin (minSdk 29). Ứng dụng thực thi các tính năng continuity, như Handoff, ngay trên Android. Điện thoại đồng bộ với Mac, iPhone và iPad. Các máy Apple đồng bộ ngược lại. +[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) -Kotlin, Gradle KTS, AGP 9.4, Kotlin 2.4, compileSdk 37 / targetSdk 35, Compose. Giao diện đa ngôn ngữ: tiếng Anh mặc định, tiếng Việt là ngôn ngữ thứ hai (chuỗi từ `../shared/strings`, thiết kế chi tiết 0.12). Đặc tả: `../docs/detailed-design/` (kho hub). Kế hoạch: `../plans/20260925-implementation/`. +Dịch vụ và ứng dụng HandLive dành cho điện thoại Android, viết bằng Kotlin với Jetpack Compose (minSdk 29, compileSdk 37). Ứng dụng kết nối Android với các thiết bị macOS, iOS và iPadOS để đồng bộ bảng nhớ tạm hai chiều, làm cầu nối SMS và hiển thị thông báo cuộc gọi kèm nút thao tác. -Kho này nằm trong workspace HandLive. Kho hub `handlive` (tài liệu, kế hoạch) là thư mục cha. `../shared` là kho `handlive-shared` (test vector, schema, design token). Test `core/design` còn đọc `../docs/design-system` trên hub. Clone cả bộ từ hub: `tools/workspace.sh clone `. Xem `CLAUDE.md` trong kho này. +## Tổng quan + +- **Tính năng liên kết thiết bị:** Đồng bộ mượt mà bảng nhớ tạm, chuyển tiếp tin nhắn SMS và hiển thị thông báo cuộc gọi trên các thiết bị của bạn. +- **Mã hóa đầu-cuối:** Toàn bộ dữ liệu truyền qua Wi-Fi cục bộ hoặc qua máy chủ relay đều được bảo vệ bằng XChaCha20-Poly1305 và khóa phiên X25519. +- **Hai flavor khi build:** + - `foss` (mặc định): Bản build mã nguồn mở hoàn toàn, không phụ thuộc Google Play Services hay Firebase; kết nối trực tiếp qua mạng cục bộ hoặc qua WebSocket relay. + - `gms`: Bổ sung tính năng đánh thức bằng tin dữ liệu ưu tiên cao qua Firebase Cloud Messaging (FCM). +- **Giao diện đa ngôn ngữ:** Tiếng Anh mặc định, tiếng Việt là ngôn ngữ thứ hai, với các chuỗi hiển thị được sinh từ `handlive-shared`. +- **Tích hợp cuộc gọi không cần `InCallService`:** Sử dụng `TelecomManager` cho các thao tác (Trả lời, Từ chối, Kết thúc) và listener hệ thống thay vì dịch vụ xử lý cuộc gọi can thiệp sâu. Không hỗ trợ âm thanh cuộc gọi trên máy tính (Phase 4 đã dừng). + +## Bối cảnh workspace + +Kho lưu trữ này là một thành phần trong workspace HandLive: +- Kho hub [`handlive`](https://github.com/HandLive/handlive) (tài liệu, kế hoạch) là thư mục cha. +- [`handlive-shared`](https://github.com/HandLive/handlive-shared) (`../shared`) cung cấp JSON schema, test vector và chuỗi giao diện dùng chung. +- Clone toàn bộ workspace từ hub bằng lệnh `tools/workspace.sh clone `. Xem [`CLAUDE.md`](CLAUDE.md) để biết quy trình làm việc trong workspace. ## Bố cục | Đường dẫn | Nội dung | |-----------|----------| -| `app/` | Ứng dụng Compose, gói `app.handlive.android`: thiết lập lần đầu (SET-01), hai thẻ Thiết bị và Cài đặt cùng ghép nối, chi tiết thiết bị (bảng nhớ tạm, SMS và cuộc gọi theo từng máy), lời công bố Hỗ trợ tiếp cận, công tắc SMS và Cuộc gọi kèm màn giải thích quyền, Quyền và chạy nền, Xóa thiết bị khỏi máy chủ / Xóa toàn bộ dữ liệu HandLive; cài các tính năng khi tiến trình khởi động. Hai flavor: `foss` (mặc định, không Play Services hay Firebase) và `gms` (đánh thức qua FCM, xem Thiết lập khi build) | -| `buildSrc/` | Task `:core:design:generateHandLiveTheme` sinh `HandLiveTheme` từ `../shared/design-tokens/tokens.json`; `:core:strings:generateStringResources` sinh tài nguyên chuỗi từ `../shared/strings/ui-strings.json` | -| `core/protocol` | Envelope, Payload, Ack, `ErrorCode` (0.8.1), khung HL, chunk bảng nhớ tạm, tin nhắn SMS và `call_event`, lớp bọc relay, op điều khiển, khung `HR` và thân REST, UUIDv7, b64/b64u; test fixture đọc `../shared` và kiểm theo JSON schema của nó | -| `core/crypto` | Tink XChaCha20-Poly1305, X25519, Ed25519, HKDF, `device_id`, PRK, lịch khóa phiên/rekey/stream, `K_push` và envelope push, kho khóa `hl_master` | -| `core/transport` | Ktor/Netty WSS (TLS 1.3, chứng chỉ P-256 tự ký), bắt tay phía S, capability, rekey, thay phiên 4409, giới hạn nhận kết nối và đóng phiên im lặng (4410/4411/4429); client relay trên OkHttp có ghim chứng chỉ (đăng ký, JWT, REST, `/v1/relay`) và phiên `/v1/ctl` đi qua relay; smoke test instrumented Netty + TLS ở `src/androidTest` | -| `core/data` | Room `handlive.db` phiên bản 3 (`paired_device`, `sms_observer_state`, `push_outbox` kèm `reason` của từng push chờ gửi; lược đồ ở `core/data/schemas`), khóa cài đặt DataStore (0.9.5), bộ chứa `HandLiveData` | -| `core/strings` | Tài nguyên chuỗi sinh lúc build từ catalog (mặc định tiếng Anh, thêm tiếng Việt); test đối chiếu với catalog và cấm chữ giao diện viết cứng | -| `core/design` | `HandLiveTheme` (4 giao diện, Inter / Be Vietnam Pro / Roboto Mono), `HLButton`, `HLSwitch`, `HLGroupedList`, `HLStatusIndicator`, `HLIcon` (Material Symbols Rounded) | -| `feature/connection` | `HandLiveService` (dịch vụ nền trước kiểu `connectedDevice`), máy chủ TLS trên 47800–47809 kèm `/v1/pair`, mDNS với gợi ý theo giờ, cập nhật capability (bảng nhớ tạm, SMS, cuộc gọi, relay, quyền còn thiếu), định tuyến envelope, dòng `HLBENCH/1` ở bản debug | -| `feature/pairing` | Ghép nối bằng mã QR (CameraX + ZXing core) hoặc PIN (Argon2id), danh sách thiết bị và Mã an toàn, hủy ghép nối | -| `feature/clipboard` | Đồng bộ bảng nhớ tạm: nhận biết thao tác sao chép qua Trợ năng, `ClipboardReadActivity`, nút Gửi bảng nhớ tạm, ô Cài đặt nhanh và mục Chia sẻ, ghi và chuyển tiếp, ảnh gửi theo khối, tự xóa an toàn | -| `feature/sms` | Cầu nối SMS: `ContentObserver` trên SMS provider (không cần `RECEIVE_SMS`), `sms/new`, `sms/sync` và `sms/history` theo trang, gửi bằng `SmsManager` (chọn SIM, nhiều phần, `SendRegistry`, trạng thái chỉ tiến), `sms/read_changed`, thông báo gợi ý cấp quyền, sự kiện SMS của `HLBENCH/1` | -| `feature/call` | Cuộc gọi không dùng `InCallService` (C12): listener trạng thái cuộc gọi (`TelephonyCallback` / `PhoneStateListener`, mặc định và theo từng SIM) và receiver `PHONE_STATE` nuôi ngữ cảnh cuộc gọi (`call_id`, cuộc gọi chờ, số đến muộn, nhãn SIM, dựng lại giữa chừng); `call_event/state` cho từng phiên kèm `controls`; `answer`, `reject`, `end` qua `TelecomManager` (giữ máy, DTMF, tắt tiếng trả `CALL_HFP_REQUIRED`); trang `call_event/log_sync` và `log_new` từ `ContentObserver` trên nhật ký cuộc gọi, kèm sửa `end_reason`; cuộc gọi đến và cuộc gọi nhỡ chuyển cho relay để báo iPhone và iPad không có phiên; sự kiện cuộc gọi của `HLBENCH/1` | -| `feature/relay` | Điện thoại trên relay: chỉ nối `/v1/relay` khi có client có thể đang chờ ở đó (tự rời sau 5 phút rảnh, `RECONNECT_BACKOFF`; giữ kết nối khi có cuộc gọi đang đổ chuông), đăng ký thiết bị, cặp và push token, thu hồi từ xa, ghép nối qua rendezvous, push cảnh báo cho iPhone và iPad niêm phong bằng `K_push` (SMS mới, cuộc gọi đến và cuộc gọi nhỡ) kèm hàng đợi thử lại `push_outbox`, và xóa thiết bị theo SET-02 | -| `config/` | ktlint, detekt | +| `app/` | Ứng dụng Compose (`app.handlive.android`): thiết lập lần đầu (SET-01), các thẻ Thiết bị và Cài đặt, quy trình ghép nối, chi tiết từng máy, thông báo Trợ năng, giải thích quyền SMS và Cuộc gọi, cài đặt chạy nền, và hủy đăng ký thiết bị. Hai flavor: `foss` (mặc định) và `gms` | +| `buildSrc/` | Tác vụ sinh mã: `:core:design:generateHandLiveTheme` từ `../shared/design-tokens/tokens.json`; `:core:strings:generateStringResources` từ `../shared/strings/ui-strings.json` | +| `core/protocol` | Định dạng truyền tải: Envelope, Payload, Ack, `ErrorCode` (0.8.1), khung HL, chunk bảng nhớ tạm, tin nhắn SMS và `call_event`, lớp bọc relay, op điều khiển, khung `HR`, thân REST, UUIDv7, Base64/Base64URL; kiểm tra hợp lệ theo schema trong `../shared` | +| `core/crypto` | Khối mật mã: Tink XChaCha20-Poly1305, X25519, Ed25519, HKDF, `device_id`, PRK, lịch khóa phiên/rekey/stream, `K_push`, và lưu trữ KeyStore `hl_master` | +| `core/transport` | Máy chủ Ktor/Netty WSS nhúng (TLS 1.3, chứng chỉ P-256 tự ký), bắt tay, trao đổi capability, quản lý phiên (thay thế 4409, đóng khi nhàn rỗi/giới hạn 4410/4411/4429); client relay OkHttp kèm ghim chứng chỉ | +| `core/data` | Cơ sở dữ liệu Room `handlive.db` v3 (`paired_device`, `sms_observer_state`, `push_outbox`), các khóa cài đặt DataStore (0.9.5), và bộ chứa `HandLiveData` | +| `core/strings` | Tài nguyên chuỗi sinh lúc build từ catalog; test đảm bảo độ phủ catalog và cấm chuỗi giao diện viết cứng | +| `core/design` | `HandLiveTheme` (4 biến thể giao diện, kiểu chữ Inter / Be Vietnam Pro / Roboto Mono), `HLButton`, `HLSwitch`, `HLGroupedList`, `HLStatusIndicator`, `HLIcon` | +| `feature/connection` | `HandLiveService` (dịch vụ nền trước), máy chủ TLS trên cổng 47800–47809, mDNS với gợi ý theo giờ, đồng bộ capability, định tuyến envelope, và log `HLBENCH/1` ở bản debug | +| `feature/pairing` | Ghép nối bằng mã QR (CameraX + ZXing) hoặc PIN (Argon2id), danh sách thiết bị đã ghép, xác thực Mã an toàn, và hủy ghép nối | +| `feature/clipboard` | Đồng bộ bảng nhớ tạm hai chiều: nhận diện sao chép qua Trợ năng, `ClipboardReadActivity`, ô Cài đặt nhanh, mục Chia sẻ, truyền ảnh theo khối, và tự xóa an toàn | +| `feature/sms` | Cầu nối SMS: `ContentObserver` trên SMS provider (không cần `RECEIVE_SMS`), `sms/new`, đồng bộ phân trang (`sms/sync`, `sms/history`), gửi nhiều phần qua `SmsManager`, cập nhật trạng thái chỉ tiến, và đồng bộ trạng thái đọc | +| `feature/call` | Giám sát và thao tác cuộc gọi: listener trạng thái điện thoại và receiver `PHONE_STATE` nuôi ngữ cảnh cuộc gọi; `answer`, `reject`, `end` qua `TelecomManager`; đồng bộ nhật ký qua `ContentObserver`; push cảnh báo cuộc gọi đến và nhỡ; hỗ trợ cuộc gọi từ ứng dụng khác (`appcall/`) | +| `feature/relay` | Client relay: kết nối `/v1/relay` theo nhu cầu, đăng ký thiết bị, điểm hẹn ghép nối, và gửi push cảnh báo (`K_push`) qua hàng đợi thử lại `push_outbox` | +| `config/` | Cấu hình linter: ktlint, detekt | + +## Yêu cầu + +- **JDK:** Phiên bản 21 (ví dụ OpenJDK 21) +- **Android SDK:** Compile SDK 37 (`platforms;android-37.0`), Min SDK 29, Target SDK 35 +- **Công cụ build:** Android Gradle Plugin (AGP) 9.4 với Kotlin 2.4 ## Thiết lập khi build -Không có gì bí mật trong git; mọi thiết lập là Gradle property (`~/.gradle/gradle.properties` hoặc `-P`) hoặc biến môi trường, và thiếu chúng vẫn build được (CI không đặt gì): +Cấu hình sử dụng Gradle property (`~/.gradle/gradle.properties` hoặc `-P`) hoặc biến môi trường. Mọi thiết lập là tùy chọn; dự án build thành công với cấu hình mặc định: | Gradle property | Biến môi trường | Dùng cho | |-----------------|-----------------|----------| | `handlive.relayHost` | `HANDLIVE_RELAY_HOST` | Máy chủ relay (CONN-03); để trống = bản build không có relay | -| `handlive.relayExtraPins` | `HANDLIVE_RELAY_EXTRA_PINS` | Thêm ghim chứng chỉ `sha256/`, cách nhau bằng dấu phẩy, bên cạnh ISRG Root X1 và X2 trong mã | -| `handlive.fcm.applicationId`, `.apiKey`, `.projectId`, `.senderId` | `HANDLIVE_FCM_APPLICATION_ID`, `_API_KEY`, `_PROJECT_ID`, `_SENDER_ID` | Chỉ flavor `gms`: tùy chọn Firebase để đánh thức qua FCM (không dùng `google-services.json`); để trống = không có push | +| `handlive.relayExtraPins` | `HANDLIVE_RELAY_EXTRA_PINS` | Bổ sung ghim chứng chỉ `sha256/`, phân tách bằng dấu phẩy, bên cạnh ISRG Root X1/X2 | +| `handlive.fcm.applicationId`, `.apiKey`, `.projectId`, `.senderId` | `HANDLIVE_FCM_APPLICATION_ID`, `_API_KEY`, `_PROJECT_ID`, `_SENDER_ID` | Chỉ flavor `gms`: thông tin Firebase để đánh thức qua FCM; để trống = không bật push | ## Lệnh ```sh -./gradlew check # test JVM (foss) + Android Lint + ktlint + detekt (JDK 21, platforms;android-37.0) -./gradlew assembleFossDebug assembleGmsDebug # flavor mặc định và flavor có FCM -./gradlew :core:transport:connectedDebugAndroidTest # smoke test instrumented, cần máy thật -HL_WRITE_ROUNDTRIP=1 ./gradlew :core:crypto:test # ghi lại ../shared/test-vectors/envelope-roundtrip.json -adb logcat -s HLBENCH # bản debug: dòng đo hiệu năng cho ../shared/tools/bench +./gradlew check # JVM tests (foss) + Android Lint + ktlint + detekt (JDK 21, platforms;android-37.0) +./gradlew assembleFossDebug assembleGmsDebug # the default flavor and the one with FCM +./gradlew :core:transport:connectedDebugAndroidTest # instrumented smoke test, needs a device +HL_WRITE_ROUNDTRIP=1 ./gradlew :core:crypto:test # rewrites ../shared/test-vectors/envelope-roundtrip.json +adb logcat -s HLBENCH # debug builds: benchmark lines for ../shared/tools/bench ``` +## Tài liệu và cộng đồng + +- [Kiến trúc hệ thống](https://github.com/HandLive/handlive/blob/main/docs/system-architecture.vi.md) — Thiết kế hệ thống và mô hình bảo mật +- [Thiết kế chi tiết](https://github.com/HandLive/handlive/tree/main/docs/detailed-design) — Giao thức, định dạng và đặc tả kỹ thuật +- [Hướng dẫn triển khai](https://github.com/HandLive/handlive/blob/main/docs/deployment-guide.vi.md) — Đóng gói và phát hành ứng dụng +- [CONTRIBUTING](https://github.com/HandLive/.github/blob/main/CONTRIBUTING.vi.md) — Quy trình gửi PR, commit nhỏ và ký xác nhận DCO (`git commit -s`) +- [SECURITY](https://github.com/HandLive/.github/blob/main/SECURITY.vi.md) — Chính sách báo cáo lỗ hổng bảo mật +- [CODE_OF_CONDUCT](https://github.com/HandLive/.github/blob/main/CODE_OF_CONDUCT.vi.md) — Quy tắc ứng xử cộng đồng + ## Giấy phép -Apache License 2.0 — xem [LICENSE](LICENSE); font và biểu tượng đóng gói theo giấy phép riêng ghi trong [NOTICE](NOTICE). Đóng góp theo [CONTRIBUTING](https://github.com/HandLive/.github/blob/main/CONTRIBUTING.vi.md) (commit nhỏ, đứng tên người thật, ký DCO bằng `git commit -s`); báo lỗi bảo mật kín theo [SECURITY](https://github.com/HandLive/.github/blob/main/SECURITY.vi.md). +Apache License 2.0 — xem [LICENSE](LICENSE). Phông chữ và biểu tượng đi kèm giữ nguyên giấy phép ban đầu, liệt kê trong [NOTICE](NOTICE).