mirror of
https://github.com/flectar/mail.git
synced 2026-11-12 10:15:19 +00:00
229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
# OpenHarmony platform
|
|
|
|
`platform/ohos` hosts the OpenHarmony port: a small Slint platform backend
|
|
that renders into an ArkTS `XComponent` surface, the NAPI cdylibs that host
|
|
it, and the ArkTS hap shell used to build and install both the backend demo
|
|
and the mail application. It mirrors `platform/android`; the shared UI and
|
|
application code stay at the repository root.
|
|
|
|
**Maintainer:** [springmin](https://github.com/springmin).
|
|
|
|
## How it plugs into the architecture
|
|
|
|
The application selects behaviour through the capability aliases declared in
|
|
`build.rs` (`mobile`, `desktop`, `linux_desktop`, ...), never through an
|
|
operating-system name. OpenHarmony is one of the `mobile` platforms:
|
|
|
|
- `build.rs` defines `mobile = any(target_os = "android", target_os = "ios",
|
|
target_env = "ohos")` and keeps OHOS out of `desktop`/`linux_desktop`.
|
|
- `src/platform/ohos.rs` is the application-side platform module: the items
|
|
of the host contract in [platform/README.md](../README.md) (PDFium location,
|
|
renderer name, PDF worker priority, preview lifecycle, app icon) plus the
|
|
system font directory for the email renderer.
|
|
- This tree is the only place that may name OpenHarmony: the host builds a
|
|
[`PlatformContext`](../README.md) (app-private paths, a document provider, a
|
|
credential store and an OAuth redirect broker) and calls
|
|
`flectar_mail::run`.
|
|
|
|
`scripts/check-platform-gates.py` (run in `quality.yml`) rejects operating
|
|
system names outside `src/platform/` and the build scripts, so new call sites
|
|
must use the aliases.
|
|
|
|
## Layout
|
|
|
|
| Path | What it is |
|
|
|------|------------|
|
|
| `backend/` | `slint-ohos-backend`: Slint `Platform`/`WindowAdapter` for a single XComponent window. femtovg/wgpu renderer (EGL/GLES) with the software renderer as fallback, input translation, IME hooks, clipboard, system fonts. |
|
|
| `mail/` | `flectar-mail-ohos` cdylib: NAPI module that builds the `PlatformContext` (app-private paths, `DocumentProvider` over the ArkTS pickers, Asset Store credential store, loopback OAuth broker), starts the application, and exposes the ArkTS bridge (file pickers, clipboard, IME text, external links). |
|
|
| `demo/` | `slint-ohos-demo`: minimal cdylib used as a smoke target for backend changes. |
|
|
| `shell/` | DevEco shell project (ArkTS). `page-templates/Index.mail.ets` is the real app page and `page-templates/Index.demo.ets` the demo page; `build-hap.sh` copies the selected template to `shell/entry/src/main/ets/pages/Index.ets`. |
|
|
| `scripts/env.sh` | Environment: SDK paths, `hdc`, `hap-sign-tool`, `LD_LIBRARY_PATH` for lld. Source it before any `cargo` command in this workspace. |
|
|
| `scripts/build-hap.sh` | End-to-end build: Rust cdylib → ArkTS `modules.abc` → hap staging → re-packed resources → signing. `APP=mail` or `APP=demo`. |
|
|
| `scripts/run-device.sh` | Installs the built hap, starts it and streams filtered logs (demo by default). |
|
|
| `scripts/fetch-pdfium.sh` | Fetches the prebuilt `libpdfium.so` used by the PDF preview. |
|
|
| `prebuilt/arm64/` | Prebuilt native libraries that ride inside the hap (signature covers `libs/**`). |
|
|
|
|
The workspace repeats the root `[patch.crates-io]` entries because this
|
|
directory is a cargo workspace of its own, and it depends on the application
|
|
with `default-features = false` (the host only drives the library; the
|
|
desktop binary is not built). fontique comes from crates.io with its
|
|
`fontconfig-dlopen` feature; OHOS has no Fontconfig, so the backend loads
|
|
system fonts explicitly from `/system/fonts`.
|
|
|
|
## Prerequisites
|
|
|
|
All paths are the ones this workspace uses by default; override through the
|
|
environment variables understood by `scripts/env.sh`.
|
|
|
|
- HarmonyOS SDK root (`OHOS_SDK_ROOT`, default `~/.harmonybrew/opt/ohos-sdk`)
|
|
plus its native subdirectory (`OHOS_SDK_NATIVE`).
|
|
- `hdc` and a DevEco-created signing material directory
|
|
(`$HOME/Documents/ohos/config`, bound to the device UDID). The store
|
|
passphrase is fetched at build time by the local `sign-huawei.sh` helper and
|
|
is never stored in this repository.
|
|
- `node` with hvigor installed (`$HOME/arkts-build/node_modules/@ohos/hvigor`),
|
|
and OpenJDK 17 (`$HOME/.harmonybrew/opt/openjdk@17`).
|
|
- The Rust `aarch64-unknown-linux-ohos` target (built natively on the device).
|
|
|
|
## Build and install
|
|
|
|
```sh
|
|
. platform/ohos/scripts/env.sh
|
|
APP=mail PROFILE=release CARGO_BUILD_JOBS=4 sh platform/ohos/scripts/build-hap.sh
|
|
|
|
hdc file send platform/ohos/build/flectar_mail_ohos-signed.hap /data/local/tmp/flectar.hap
|
|
hdc shell "bm install -r -d -p /data/local/tmp/flectar.hap"
|
|
hdc shell "aa start -a EntryAbility -b com.flectar.mail"
|
|
```
|
|
|
|
The bundle name defaults to `com.flectar.mail`. The hap version follows the
|
|
workspace `Cargo.toml`: `versionName` is the crate version and `versionCode`
|
|
follows the Android sequence from `scripts/android-version-code.py` (alpha <
|
|
beta < rc < release < the next patch), so store uploads stay monotonic
|
|
(`0.1.0-alpha.7` becomes `1000007`, `0.1.0` becomes `1009999`).
|
|
`VERSION_NAME` and `VERSION_CODE` override either value.
|
|
|
|
Release builds are signed with the AGC material through `hap-sign-tool`:
|
|
|
|
```sh
|
|
RELEASE_SIGN=1 \
|
|
RELEASE_P12=release.p12 RELEASE_CERT=com.flectar.mail.cer \
|
|
RELEASE_PROFILE=FlectarMailProfileRelease.p7b \
|
|
RELEASE_KEY_ALIAS=releasekey RELEASE_KEY_PWD=... RELEASE_STORE_PWD=... \
|
|
APP=mail PROFILE=release sh platform/ohos/scripts/build-hap.sh
|
|
```
|
|
|
|
Without `RELEASE_SIGN=1` the script uses the DevEco auto-signing material,
|
|
which is bound to the bundle it was issued for: if that material was generated
|
|
for a different name, pass `BUNDLE=<that name>` for local debug builds.
|
|
|
|
`bm install -r -d` is deliberate: `hdc install` rejects the hap ("install
|
|
version downgrade") because the shared bundle name carries the DevEco
|
|
project's version code.
|
|
|
|
The ArkTS entry page (`shell/entry/src/main/ets/pages/Index.ets`) is generated
|
|
from `shell/page-templates/Index.<APP>.ets` by the build script and is not
|
|
tracked; an IDE build that bypasses the script needs that copy first.
|
|
|
|
## CI
|
|
|
|
The platform is tier 3: it is built by a manually dispatched workflow
|
|
(`.github/workflows/ohos.yml`), not on every pull request. The HarmonyOS SDK
|
|
and hvigor are not anonymously downloadable, so the build job targets a
|
|
self-hosted runner with the prerequisites above. Dispatch runs build unsigned
|
|
(`SKIP_SIGN=1`); `sign: true` switches to `RELEASE_SIGN=1`, which consumes the
|
|
`FLECTAR_OHOS_*` repository secrets through `hap-sign-tool` and never touches
|
|
the pull request context.
|
|
|
|
## Why the hap is re-packed by hand
|
|
|
|
DevEco's `PackageHap` step needs the packing-tool jar, so `build-hap.sh`
|
|
builds the payload with hvigor only up to `modules.abc` and assembles the hap
|
|
itself. Three device-side quirks make the hand packing necessary — they are
|
|
handled (and asserted) in the script, do not "simplify" them away:
|
|
|
|
1. **Resources index.** hvigor's resource compile passes a `compression`
|
|
option and emits a legacy `resources.index` this device cannot load; every
|
|
`$string`/`$media` reference silently resolves to nothing (blank icon, app
|
|
name replaced by the bundle name). The script re-runs restool without
|
|
`compression` to obtain the `RestoolV2` index DevEco-built haps carry.
|
|
2. **Zip layout.** `resources.index` and `resources/**` must be STORED, and
|
|
the archive must not contain directory entries (the installer rejects
|
|
them).
|
|
3. **Resolved ids and module fields.** `labelId`/`iconId`/... are normally
|
|
resolved from `$string:`/`$media:` references by `PackageHap`; the script
|
|
injects them from the compiler's `id_defined.json`, together with the
|
|
device types (2in1/tablet/phone), `ohos.permission.INTERNET` (without it
|
|
every socket fails with EPERM) and the HarmonyOS 6.1 SDK coordinates.
|
|
|
|
## Runtime notes
|
|
|
|
- **Logs**: `hdc shell "hilog -T slint-ohos,flectar-mail"` for the backend and
|
|
application, `-T JSAPP` for the ArkTS console.
|
|
- **GPU rendering**: the femtovg/wgpu path (EGL/GLES) is attempted by default
|
|
and falls back to the software renderer automatically — before the surface
|
|
exists, after a failed attach, and permanently once an attach failed. Local
|
|
runs can opt out with `FLECTAR_OHOS_SOFTWARE=1`; the device build has no
|
|
environment, so the fallback is the safety net. The wgpu GL-backend
|
|
subtleties (why `Backends::GL` must be requested explicitly) are documented
|
|
in `backend/src/gpu.rs`.
|
|
- **System pickers**: the document picker opens in a window over the
|
|
XComponent without hiding it, and frames presented into the covered surface
|
|
leave the window unresponsive once the picker closes. Each pending picker
|
|
request therefore holds presentation (`backend/src/hold.rs`): the Slint
|
|
thread waits, without drawing, until the page reports the result.
|
|
- **Soft keyboard**: the page attaches the OHOS input-method controller when
|
|
Slint requests the input method. On 2in1 devices with a physical keyboard
|
|
the system suppresses the soft keyboard (system applications included)
|
|
unless it is explicitly summoned; the built-in IME uses `Alt+Shift`.
|
|
- **Input**: XComponent touch events drive Slint's pointer handling; physical
|
|
keyboard events are translated (key codes to text, modifiers, auto-repeat)
|
|
in `backend/src/xcomponent.rs`, and wheel/touchpad axis events reach the
|
|
same module through the XComponent UI input callback (Slint's flickables
|
|
then scroll without any application-side change).
|
|
- **Privacy**: Flectar publishes the app-specific policy at
|
|
`https://flectar.com/mail/privacy/`. The shared app links it from the
|
|
welcome screen and Settings > About, in the UI language; a consent flow is
|
|
tracked separately from this port.
|
|
- **Clipboard**: copying needs no permission, but pasting reads the system
|
|
pasteboard and therefore requires `ohos.permission.READ_PASTEBOARD`, an ACL
|
|
(restricted) permission. The module declares it and the page requests it on
|
|
the first denied paste. The signing profile must list the permission in
|
|
`acls.allowed-acls`; a hap built without such a profile fails to install
|
|
with "grant request permissions failed".
|
|
- **Data**: the application keeps its data under the standard bundle data
|
|
path. Import/export goes through the system document picker via the host
|
|
`DocumentProvider`, and credentials live in the system Asset Store, never
|
|
in plain files.
|
|
|
|
## Lint
|
|
|
|
The ArkTS sources are checked with `oxk` from
|
|
[ohos-rs/oxc-ark](https://github.com/ohos-rs/oxc-ark), the ArkTS tooling built
|
|
on oxc/oxlint (`cargo install oxk --git
|
|
https://github.com/ohos-rs/oxc-ark.git` with the `scripts/env.sh` environment).
|
|
The rule selection and the project API level live in
|
|
`shell/.oxlintrc.jsonc`:
|
|
|
|
```sh
|
|
cd platform/ohos/shell
|
|
oxk lint entry/src/main/ets --config .oxlintrc.jsonc --threads 1
|
|
```
|
|
|
|
## Verified on device
|
|
|
|
HarmonyOS 7.0.0.111 (API 26), 2-in-1 (HUAWEI MateBook Pro):
|
|
|
|
- IMAP IDLE sync, SMTP sending, OAuth sign-in through the system browser and
|
|
the loopback broker.
|
|
- CJK glyphs in mail bodies (Blitz/Parley) and the compose editor.
|
|
- Compose attachments, account-backup export, database-snapshot export and
|
|
certificate import through the system pickers.
|
|
- External links, wheel/touchpad scrolling, Asset Store
|
|
credentials including recovery from a stale row after reinstalling, and PDF
|
|
attachment preview through the bundled PDFium.
|
|
- Fresh install signed with the AGC release and debug profiles under the AGC
|
|
bundle.
|
|
|
|
## Known limitations
|
|
|
|
- Clipboard paste uses the ACL permission `ohos.permission.READ_PASTEBOARD`
|
|
(approved). A hap that declares it installs only with a signing profile
|
|
whose `acls.allowed-acls` lists it.
|
|
- Backup and snapshot import and multi-account setups are not yet exercised on
|
|
device.
|
|
- The clipboard path reads plain text only: no rich text, images or
|
|
drag-and-drop.
|
|
- The soft keyboard is summoned explicitly (Alt+Shift on 2-in-1) because the
|
|
system suppresses it for native surfaces with a physical keyboard.
|
|
- The email renderer runs on the CPU (upstream enables the GPU path only on
|
|
desktop).
|
|
|
|
## Tests
|
|
|
|
The backend has no automated device-side harness: `cargo test` covers the
|
|
shared crates at the repository root, and `APP=demo` (installed with
|
|
`scripts/run-device.sh`) is the manual smoke target for backend changes.
|
|
The non-Latin font fallback policy lives in `backend/src/fonts.rs`; the host
|
|
hands it to the mail renderer and the composer with `configure_email_fonts`.
|