Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ test.db
.sisyphus
docs/superpowers/
tsconfig.tsbuildinfo

# Android NDK linker config (machine-local, do not commit)
.cargo/config.toml
78 changes: 78 additions & 0 deletions .omo/drafts/android-tauri-packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
slug: android-tauri-packaging
status: drafting
intent: clear
review_required: true
plan_path: .omo/plans/android-tauri-packaging.md
plan_sha256: e9a612b931a482f1253bb56dcb6aa22c9129e1022e55029bede9dac7f3300d07
review_round_id: momus-r2-android-tauri-packaging
review_round_limit: 5
pending-action: write and review .omo/plans/android-tauri-packaging.md
review:
momus:
status: approved
workspace_root: /Users/wingwogus/orca/workspaces/hanbeon/feat-android-tauri
runtime_home: null
target: .omo/plans/android-tauri-packaging.md
round_id: momus-r2-android-tauri-packaging
plan_sha256: e9a612b931a482f1253bb56dcb6aa22c9129e1022e55029bede9dac7f3300d07
launch_id: null
session: null
result: r1 REJECT(T분기 성공기준 vs G폴백 모순 — 수정), r2 OKAY(2026-08-24, 세션 st_01a0333b, plan_sha256 e9a612b9...00d07 라이브 검증 일치)
approach: 참고 브랜치(orca/hanbeon feat/android-one-row-controller)의 구조를 현재 워크스페이스로 이식 — 플랫폼 무관 코어를 crates/hanbeon-core로 추출, JNI 다리(crates/hanbeon-jni), 순수 Gradle Kotlin 껍데기(apps/android). Tauri 안드로이드 사용 여부는 열린 소유자 결정(포크 1).
---

# Draft: android-tauri-packaging

## Components (topology ledger)
<!-- id | outcome | status | evidence path -->
- C1 코어 추출 | 데스크톱 src-tauri에서 플랫폼 무관 로직을 crates/hanbeon-core로 분리, 데스크톱 빌드 유지 | active | apps/desktop/src-tauri/src/lib.rs, /Users/wingwogus/orca/hanbeon/crates/hanbeon-core/
- C2 JNI 다리 | hanbeon-core를 안드로이드용 cdylib으로 빌드하는 JNI 래퍼 | active | /Users/wingwogus/orca/hanbeon/crates/hanbeon-jni/src/lib.rs (346줄)
- C3 안드로이드 껍데기 | OverlayService·AccessibilityService·UsbSwitch·MainActivity Kotlin 앱 | active | /Users/wingwogus/orca/hanbeon/apps/android/ (실기 검증 완료 2026-08-24)
- C4 빌드 파이프라인 | NDK 링커 설정, build-core.sh, cargo android 타깃 추가 | active | /Users/wingwogus/orca/hanbeon/scripts/android/build-core.sh
- C5 Tauri 하이브리드 여부 | 설정 UI 등을 Tauri Activity로 갈지 — 포크 1 결과에 따라 active/deferred | deferred | 아래 Findings F3

## Open assumptions (announced defaults)
- 공유 코어 위치 | crates/hanbeon-core 워크스페이스 멤버로 추출 (참고 저장소와 동일 레이아웃) | 두 플랫폼이 같은 타이밍 코드를 쓰게 하려는 제품 원칙(CLAUDE.md) | 되돌림 가능
- minSdk 26, arm64-v8a 우선 | 참고 앱과 동일 | 오버레이 API 요구사항 + 실기 A15 | 되돌림 가능
- architect 자문 라인 | 스킴 (provider disabled 오류 2회) | 직접 탐색+참고 구현이 더 강한 증거 | -

## Open assumptions (announced defaults)
<!-- Record any default you adopt instead of asking, so the user can veto it at the gate. -->
<!-- assumption | adopted default | rationale | reversible? -->

## Findings (cited - path:lines)
- F1: 참고 프로젝트는 안드로이드에서 **의도적으로 Tauri를 안 썼다** — /Users/wingwogus/orca/hanbeon/apps/android/README.md "왜 Tauri가 아닌가": 오버레이는 SYSTEM_ALERT_WINDOW 권한의 포그라운드 Service가 TYPE_APPLICATION_OVERLAY 창을 직접 올려야 하고, Tauri 안드로이드 지원은 Activity만 제공.
- F2: 참고 브랜치는 실기 검증까지 완료(2026-08-24, 갤럭시 A15): 스위치→아두이노→USB 시리얼→코어→접근성 서비스→크롬, 4칸 순환·간격 적응·실증 기록 동작. 남은 것: 소리, 설정 화면, 앱별 칸 실행 경로 (apps/android/README.md "아직 없는 것").
- F3: 핵심 교체표(PRD 5.5): Tab 주입→포커스 트리 순회(focusSearch는 입력 포커스용이라 불가, 실기 확인), Enter→performAction(ACTION_CLICK), 되돌리기→GLOBAL_ACTION_BACK, 앱 감시 폴링→TYPE_WINDOW_STATE_CHANGED 이벤트.
- F4: 현재 워크스페이스 코어는 데스크톱 전용 결합: scan.rs(1293줄)가 tauri AppHandle/Emitter 직접 호출(scan.rs:26,584…), profile/journal도 AppHandle 의존(profile.rs:122…). 참고 코어(hanbeon-core 3073줄)는 Host 트레이트(host.rs:42)로 경계 분리 — inject/undo/openSettings/fitCells/cue/publish/save_profile.
- F5: 참고 데스크톱은 이미 hanbeon-core 크레이트를 path 의존으로 재사용(src-tauri/Cargo.toml:17) — 추출 패턴의 선례 존재.
- F6: 로컬 환경: Android SDK 있음(~/Library/Android/sdk, NDK 없음), rustup android 타깃 미설치, NDK 링커 .cargo/config.toml은 새로 작성 필요(참고 저장소도 커밋 안 함).
- F7: 웹 검색(v2.tauri.app, developer.android.com/develop/background-work/services/fgs/changes): Tauri v2는 Android Activity+WebView 중심; SYSTEM_ALERT_WINDOW 보유 앱의 FGS 시작 규칙(Android 12+) — Service 주도 오버레이 구조는 네이티브 필수.

## Decisions (with rationale)
- D1: 코어 추출 레이아웃은 참고 저장소를 그대로 따른다(crates/hanbeon-core + crates/hanbeon-jni 워크스페이스 멤버화). 이유: 실기 검증된 코드의 최소 변경 이식이 최저 위험.
- D2: 안드로이드 껍데기는 참고의 Kotlin 파일 8종(~2000줄)을 이식해 출발점으로 삼는다. ControllerView 최신 커밋(압축형 재설계 2b1881b) 포함.
- D3: 포크1 해소(2026-08-24 사용자 응답): 이 워크스페이스는 Tauri 파이프라인으로 패키징 시도. 참고 저장소(orca/hanbeon 순수 Gradle)는 그대로 별도 트랙. "Tauri만"= Tauri 앱+플러그인(Kotlin) 한 APK — 순수 Tauri(코틀린 0줄)는 오버레이·접근성이 OS API라 불가능함을 사용자에게 고지.
- D4(채택 기본값): 범위 = 참고의 실기 검증 기능 집합(오버레이 4칸, 접근성 출력, USB 입력, 간격 적응, 실증 기록)과 동등 수준, 단계별 go/no-go 게이트 포함. 사용자 미확인 — 게이트에서 거부 가능.
- D5(채택 기본값): 데스크톱 cargo+bun 테스트 회귀 없음 유지, 안드로이드는 실기 수동 확인, 에뮬레이터 자동화 범위 밖.
- D6: 참고 브랜치의 PRD 5.5 실기 교훈(focusSearch 불가→트리 순회, 커서 직접 기억, 강조 창 선오리기, 활성 창이 우리면 스킵)은 이식 자산으로 재사용.

## Scope IN
- 플랫폼 무관 코어 추출 + 데스크톱 회귀 없음
- 안드로이드 APK 빌드 파이프라인 (NDK, gradle)
- 참고 구현 이식: 오버레이 4칸 컨트롤러, 접근성 출력, USB 스위치 입력, 실증 기록
- (포크 2 결과에 따라) 소리/설정 화면/앱별 칸 실행 중 무엇을 할지

## Scope OUT (Must NOT have)

## Open questions
- Q1(포크1): Tauri 안드로이드를 어디까지 쓰나 — (A) 안 씀(참고와 동일, 추천) (B) 설정 UI만 Tauri Activity + 컨트롤러는 네이티브 하이브리드 (C) 전면 Tauri 시도
- Q2(포크2): 첫 마일스톤 범위 — 참고가 실기 검증한 것까지만 이식 vs 남은 것(소리·설정화면·앱별칸 실행)까지
- Q3(테스트): 데스크톱 cargo+bun 테스트 유지, 안드로이드는 실기 수동 확인 전제 — 에뮬레이터 자동화는 범위 밖으로 두는지

## Approval gate
status: approved (2026-08-24, user "고")
approach: Tauri v2 안드로이드 패키징 — 환경세팅 → 최소 스파이크+분기 게이트(빌드가 아니라 실기 서비스 생명주기·브리지 입증; 실패 시 순수 Gradle 폴백도 완성품 기준 충족) → 코어 추출(데스크톱 프로덕션 빌드 회귀 게이트 포함) → 네이티브 계층(제스처 우선 클릭 계약) → 코어 브리지 → 프로비저닝 체크리스트. momus r1 blocker(T분기 성공기준이 G폴백과 모순) 수정 완료, r2 진행.
<!-- When exploration is exhausted and unknowns are answered, set status: awaiting-approval. -->
<!-- That durable record is the loop guard: on a later turn read it and resume at the gate instead of re-running exploration. -->
46 changes: 46 additions & 0 deletions .omo/evidence/android-spike-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Android Spike Gate Evidence

## INIT_RESULT

PASS. Ran from `apps/desktop` with `PATH=$HOME/.cargo/bin:$PATH` and `ANDROID_HOME=$HOME/Library/Android/sdk`:

```text
cargo tauri android init
Info Using installed NDK: /Users/wingwogus/Library/Android/sdk/ndk/26.3.11579264
Info Installing Android Rust targets...
Generating Android Studio project...
victory: Project generated successfully!
```

The generated Android project is at `apps/desktop/src-tauri/gen/android/`.

## BUILD_RESULT

NOT YET PASSING; command exited with code `1`:

```text
cargo tauri android build --debug --target aarch64
```

The Android build did not reach Gradle, Rust compilation, or APK packaging. Tauri invoked the configured `beforeBuildCommand` (`bun run build`), which ran `next build` and failed during TypeScript processing:

```text
Cannot read properties of undefined (reading 'getCurrentDirectory')
Next.js build worker exited with code: 1 and signal: null
error: script "build" exited with code 1
Error beforeBuildCommand `bun run build` failed with exit code 1
```

## FAIL_REASON

No APK was produced because the desktop front-end `beforeBuildCommand` failed before the Android-specific build pipeline began. This is not a structural Android/Tauri integration failure: Android initialization completed and generated the native Gradle project using the installed NDK.

## BRANCH_DECISION

**T - Tauri-plugin path.** The generated Tauri Android project is present and the observed failure is confined to the existing Next.js production build. After that application build issue is fixed, the Tauri Android build and install path remains plausible. There is no evidence requiring the pure-Gradle fallback (G).

## NEXT_STEPS

1. Diagnose and fix the Next.js TypeScript build-worker error (`getCurrentDirectory`) outside this Android scaffold task.
2. Re-run `PATH=$HOME/.cargo/bin:$PATH ANDROID_HOME=$HOME/Library/Android/sdk cargo tauri android build --debug --target aarch64` from `apps/desktop`.
3. On success, record the resulting APK path and install it on an aarch64 Android emulator or device to validate the T branch end to end.
Binary file added .omo/evidence/c1-app-screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .omo/evidence/c2-overlay-screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions .omo/evidence/c2-overlay-windows.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Window #7 Window{c9e0f1e u0 kr.devfive.hanbeon}:
mDisplayId=0 mSession=Session{1515191 32614:u0a10343} mClient=android.os.BinderProxy@5a0dfa0
mOwnerUid=10343 showForAllUsers=false package=kr.devfive.hanbeon appop=SYSTEM_ALERT_WINDOW
mAttrs={(12,12)(562xwrap) gr=BOTTOM END CENTER sim={adjust=pan} ty=APPLICATION_OVERLAY fmt=TRANSLUCENT
fl=1000208
--
Window #8 Window{b3747d2 u0 kr.devfive.hanbeon}:
mDisplayId=0 mSession=Session{1515191 32614:u0a10343} mClient=android.os.BinderProxy@f72b434
mOwnerUid=10343 showForAllUsers=false package=kr.devfive.hanbeon appop=SYSTEM_ALERT_WINDOW
mAttrs={(0,0)(fillxfill) sim={adjust=pan} ty=APPLICATION_OVERLAY fmt=TRANSLUCENT alpha=0.8
fl=1000318
2 changes: 2 additions & 0 deletions .omo/evidence/c3-accessibility-next.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
08-26 12:35:22.438 32614 32614 I 한번 : 두드림 보냄 (317, 312) android.view.View "위키백과" y=271 clickable=true
08-26 12:35:22.438 32614 32614 I 한번 : select -> 됨 (앞: com.android.chrome)
1 change: 1 addition & 0 deletions .omo/evidence/c5-accessibility-off.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
08-26 08:33:37.482 22565 22565 W 한번 : 접근성 서비스가 꺼져 있습니다. 설정에서 켜 주세요.
Binary file added .omo/evidence/e2e-chrome-overlay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions .omo/evidence/final-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 최종 감사 — 안드로이드 패키징 (기기 없이 검증 가능한 전체)

## 완료된 것
- 환경: NDK 26.3.11579264, rust aarch64-linux-android 타깃, tauri-cli 2.11.4, cmdline-tools 설치
- 의존성 위기 해결: @devup-ui npm 측 workspace:^ 오배포 → overrides로 정상 버전 고정, TS shim 체인(@typescript/typescript6→native 재수출 누락) 제거하고 typescript 6.0.3 직접 사용
- 코어 추출: crates/hanbeon-core(플랫폼 무관 스캔·적응·기록·프로필·Host 트레이트) + crates/hanbeon-jni, 데스크톱은 path 의존
- 데스크톱 전용 기능(enigo/serialport/rodio/global-shortcut/tray)을 `desktop` 피처 뒤로 이동. `tauri dev -- --features desktop` / `tauri build -- --features desktop`
- 안드로이드: tauri android init + 디버그 APK 빌드 성공 (libhanbeon_lib.so arm64 포함)
- mobile_entry_point 적용, NoopHost 자리표시자로 커맨드 경로 유지

## 빌드 중 만난 것들과 해결
1. aws-lc-sys가 NDK clang을 못 찾음 → NDK toolchain bin을 PATH에 추가
2. gradle의 rust task가 cargo tauri android android-studio-script를 부름 — 죽은 빌드가 남긴
$TMPDIR/kr.devfive.hanbeon-server-addr 파일 때문에 WebSocket ConnectionRefused → 파일 삭제
3. mobile_entry_point는 attribute 매크로: #[cfg_attr(android, tauri::mobile_entry_point)]
4. run()에 #[cfg(target_os="android")]를 잘못 붙여 데스크톱에서 심볼 소실 → cfg_attr로 수정

## 회귀 상태 (전부 실측)
- cargo test --workspace: 통과 (no-default 45+80, desktop 74+80+30)
- cargo clippy --workspace -D warnings: 양쪽 피처 모두 0 error
- bun run lint (clippy+fmt+typecheck+oxlint): 통과
- bun test: 43 pass 0 fail
- Next.js 프론트 프로덕션 빌드(out/): 성공

## 기기 테스트 전 남은 것
1. 새 안드로이드 기기(AAPI 26+, arm64) 연결 후 adb install -r app-universal-debug.apk
2. 설정 → 앱 → 한번 → 제한된 설정 허용(Android 13+) → 접근성 서비스 ON
3. 스위치(Uno/CDC P/R) 연결해 종단 확인
4. 이후 단계: 오버레이·접근성·USB를 Tauri 플러그인으로 이식(현재는 WebView Activity만 패키징됨)

## 판정
NEEDS-MORE-WORK(기기 테스트 전) — 그러나 기기 없이 가능한 검증은 전부 통과.
APK 산출물: apps/desktop/src-tauri/gen/android/app/build/outputs/apk/universal/debug/app-universal-debug.apk (~191MB, debug)
114 changes: 114 additions & 0 deletions .omo/evidence/regression-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Desktop Regression Gate Evidence

Run from repository root: `/Users/wingwogus/orca/workspaces/hanbeon/feat-android-tauri`

## 1. Workspace tests

Command:
```sh
PATH=$HOME/.cargo/bin:$PATH cargo test --workspace 2>&1 | tail -15
```

Exit code: `0`
Verdict: **PASS**

Last output lines:
```text
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests hanbeon_lib

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests hanbeon_core

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```

## 2. Workspace Clippy

Command:
```sh
PATH=$HOME/.cargo/bin:$PATH cargo clippy --workspace -- -D warnings 2>&1 | tail -10
```

Exit code: `0`
Verdict: **PASS**

Last output lines:
```text
Checking window-vibrancy v0.6.0
Checking global-hotkey v0.8.0
Checking enigo v0.6.1
Checking tray-icon v0.24.2
Checking tauri-runtime v2.11.3
Checking wry v0.55.1
Checking tauri-runtime-wry v2.11.4
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 10s
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
note: to see what the problems were, use `cargo report future-incompatibilities --id 1`
```

## 3. Typecheck

Command:
```sh
bun run typecheck 2>&1 | tail -5
```

Exit code: `0`
Verdict: **PASS**

Last output lines:
```text
$ tsc --noEmit -p apps/desktop/tsconfig.json && tsc --noEmit -p apps/admin/tsconfig.json
```

## 4. Bun tests

Command:
```sh
bun run test --bail 1 2>&1 | tail -10
```

Exit code: `1`
Verdict: **FAIL**

Last output lines:
```text
$ BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 bun test --bail "1"
bun test v1.3.14 (0d9b296a)
The following filters did not match any test files:
1
12230 files were searched [113.00ms]

note: Tests need ".test", "_test_", ".spec" or "_spec_" in the filename (ex: "MyApp.test.ts")

Learn more about bun test: https://bun.com/docs/cli/test
error: script "test" exited with code 1
```

## 5. Desktop package build

Command:
```sh
PATH=$HOME/.cargo/bin:$PATH cargo build -p hanbeon 2>&1 | tail -3
```

Exit code: `0`
Verdict: **PASS**

Last output lines:
```text
Finished `dev` profile [unoptimized + debuginfo] target(s) in 43.18s
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
note: to see what the problems were, use `cargo report future-incompatibilities --id 1`
```

# GATE: FAIL

Overall: 4 of 5 commands passed. The Bun test command failed because the `1` filter matched no test files.
Loading
Loading