Reverse-engineer DWARF II telescope API and build open-source Go client
Complete reverse-engineering of the DWARFLAB Android app (v3.4.0) protocol
and implementation of a working CLI tool to control DWARF II telescopes.
Analysis (from APK decompilation with jadx):
- Extracted 17 protobuf definitions (382 messages) from embedded descriptors
- Mapped all 323 WebSocket command IDs across 16 modules
- Documented the full protocol: BLE discovery, WebSocket control (port 9900),
RTSP preview, WsPacket envelope (proto v2.3)
- Documented the Android UI structure (screens, navigation, shooting modes)
- Key discovery: telescope responds with type=3 (reply), not type=1 (response),
and several commands are fire-and-forget (RGB, camera open/close)
dwarfctl Go client:
- Protobuf bindings generated from extracted .proto files (397 messages)
- WebSocket transport layer with request-response matching and notification fan-out
- Typed API covering cameras, motors, astrophotography, focus, tracking, system, power
- Cobra CLI with 30+ subcommands and --debug traffic logging
- 57 unit tests (transport round-trip, command routing, proto encoding)
- Validated on real hardware: state, photo, motor slew (all directions/speeds),
focus, RGB, time/location sync all confirmed working
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
502
analysis/UI_REFERENCE.md
Normal file
502
analysis/UI_REFERENCE.md
Normal file
@ -0,0 +1,502 @@
|
||||
# DWARFLAB App — UI / UX Reference (reverse-engineered)
|
||||
|
||||
Reverse-engineered from `DWARFLAB.apk` v3.4.0 (`com.convergence.dwarflab`). This
|
||||
document describes the **user interface, screen hierarchy, navigation, and
|
||||
interactions** so the app can be reimplemented in another language/framework.
|
||||
|
||||
Focus: **Splash → Home → Connection → Capture (main shooter) → Settings**.
|
||||
(Atlas/Sky-Atlas and Album/Gallery screens are documented separately.)
|
||||
|
||||
---
|
||||
|
||||
## 1. App structure & navigation model
|
||||
|
||||
The app uses **Jetpack Navigation** (single-activity `MainActivity` + a
|
||||
`NavHostFragment`) with a **`BottomNavigationView`** of 4 tabs, plus several
|
||||
full-screen Activities launched outside the nav graph.
|
||||
|
||||
### 1.1 Entry / launch flow
|
||||
```
|
||||
SplashActivity (launcher, branding + init)
|
||||
└─▶ MainActivity (NavHost + BottomNavigationView, 4 tabs)
|
||||
├─ Tab 1: Home → HomeFragment
|
||||
├─ Tab 2: Atlas → AtlasActivity (separate)
|
||||
├─ Tab 3: Album → AlbumFragment / AlbumActivity
|
||||
└─ Tab 4: Settings → SettingsFragment
|
||||
```
|
||||
- `mobile_navigation.xml` is the main nav graph; `startDestination =
|
||||
navigation_device` (Home).
|
||||
- `HeartbeatEntryActivity` is a transparent launcher alias used to keep a
|
||||
foreground service alive (the BLE/Wi-Fi heartbeat to the scope).
|
||||
- Tab labels (from `bottom_nav_menu.xml` + strings `home_3/4/5`, `camera_astro_69`):
|
||||
| tab | id | label (en) | icon |
|
||||
|-----|----|-----------|------|
|
||||
| 1 | `navigation_device` | **Home** | `icon_function_tabbar_home` |
|
||||
| 2 | `navigation_atlas` | **Atlas** | `icon_function_tabbar_atlas` |
|
||||
| 3 | `navigation_album` | **Album** | `icon_function_tabbar_album` |
|
||||
| 4 | `navigation_settings` | **Settings** | `icon_function_tabbar_settings` |
|
||||
|
||||
The bottom bar can be hidden (`MainActivity.isShowBottomNav`) when entering
|
||||
full-screen sub-flows (device connect, OTA, capture).
|
||||
|
||||
### 1.2 Activities (beyond the nav graph)
|
||||
| Activity | Purpose |
|
||||
|----------|---------|
|
||||
| `SplashActivity` | Cold-start splash, version checks, routing to connect vs. main |
|
||||
| `MainActivity` | Hosts the 4 tabs + NavHost |
|
||||
| `CaptureActivity` | **The shooting screen** (9199 lines — the heart of the app) |
|
||||
| `AtlasActivity` | Sky atlas / star map / GoTo target selection |
|
||||
| `AlbumActivity` | Gallery of captured photos/videos/FITS |
|
||||
| `LocationActivity` | Map to set observing location |
|
||||
| `CaliFrameActivity` | Calibration-frame capture/management |
|
||||
| `HelperActivity` | In-app help / guide center |
|
||||
| `WebActivity` / `NavActivity` | Embedded web views (privacy, OSS notice, help docs) |
|
||||
| `NfcWriteActivity` | NFC tag update (device name change) |
|
||||
| `MosaicGuideActivity`, `SkyLightTipActivity`, `StarSearchActivity` | Atlas sub-screens |
|
||||
|
||||
---
|
||||
|
||||
## 2. Splash & first-run
|
||||
|
||||
`SplashActivity` → decides between:
|
||||
- **Not connected**: routes to `HomeFragment` which shows a *device-discovery /
|
||||
connect* CTA when no scope is paired.
|
||||
- **App upgrade available**: shows `AppUpdateDialog` (force-update path uses
|
||||
`FirmwareForceUpdateDialog`).
|
||||
- **System notice**: `AppNotice` banner.
|
||||
|
||||
`MainActivity` also handles: app-upgrade dialogs, product-activation dialog
|
||||
(`ProductActivateDialog`), not-enough-storage dialog, and an activation-error
|
||||
dialog. Activation is gated behind internet access (strings `settings_25`–`45`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Home tab (`HomeFragment`) — `fragment_home.xml`
|
||||
|
||||
The dashboard / device hub. Two visual states:
|
||||
|
||||
### 3.1 Disconnected state
|
||||
- Title bar: device-name placeholder + a **"Guide"** button (`home_16`,
|
||||
top-right, primary pill).
|
||||
- Centered illustration + **"Connect"** call-to-action → navigates to
|
||||
`DeviceSearchFragment` (BLE scan).
|
||||
- `view_bg_home_disconnected` background shown.
|
||||
|
||||
### 3.2 Connected state
|
||||
Top bar becomes a **device selector dropdown** (`tv_title` with
|
||||
`drawableEnd icon_action_arrow_down`): tapping opens device switcher.
|
||||
Below: device status card, then action entries.
|
||||
|
||||
Navigation actions declared on `navigation_device` (HomeFragment):
|
||||
| Action | Destination | Meaning |
|
||||
|--------|-------------|---------|
|
||||
| `action_deviceFragment_to_deviceSearchFragment` | Device Search | Add/switch device |
|
||||
| `action_homeFragment_to_locationFragment` | Location | Set GPS/observing site |
|
||||
| `action_homeFragment_to_ScheduleFragment` | Schedule | Shooting plans list |
|
||||
| `action_homeFragment_to_upgradeFragment` | OTA Upgrade | Firmware update |
|
||||
| `action_homeFragment_to_scheduleDetailFragment` | Schedule Detail | Edit a plan |
|
||||
| `action_homeFragment_to_helperFragment` | Helper | In-app guide |
|
||||
| `action_homeFragment_to_webFragment` | Web | Embedded web content |
|
||||
| `action_home_to_taskListFragment` | Task List | Task/creation jobs (`createType` arg) |
|
||||
| `action_home_to_astroFitsFragment` | Astro FITS | View a FITS file (`filePath`) |
|
||||
| `action_home_to_caliFrameListFragment` | Cali-Frame List | Manage calibration frames (`caliFrameType`, `cameraType`) |
|
||||
|
||||
So **Home is a launcher** into: connect, location, schedule, OTA, tasks,
|
||||
cali-frames, help. The actual *shooting* is entered by tapping the connected
|
||||
device card → launches `CaptureActivity`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Device connection flow
|
||||
|
||||
A multi-step wizard (mostly bottom-sheet dialogs) living under
|
||||
`navigation_device_connect` and friends.
|
||||
|
||||
```
|
||||
DeviceSearchFragment (BLE scan, lists nearby scopes)
|
||||
├─▶ DeviceConnectFragment (dialog) — enter device password
|
||||
│ ├─ success → DeviceConnectSuccessFragment
|
||||
│ ├─ wrong pwd → DeviceConnectPwdErrorAgainFragment
|
||||
│ └─ upgrade needed → UpgradeReminderFragment → OTAUpgradeFragment
|
||||
├─▶ WiFiListFragment (STA mode: scan Wi-Fi for the scope)
|
||||
│ └─▶ StaInfoFragment (enter Wi-Fi password) → DeviceConnectFragment
|
||||
├─▶ DeviceConnectManualFragment (manual IP entry)
|
||||
├─▶ DeviceStaPwdFragment / DeviceStaConnectFailedFragment
|
||||
└─▶ DeviceResetTutorialFragment / DeviceConnectFailedFragment
|
||||
```
|
||||
|
||||
Key strings reveal the model:
|
||||
- **Connection modes** (`settings_connection_14–21`):
|
||||
- **Auto (Recommended)** — auto-choose AP vs STA.
|
||||
- **STA mode** — phone & scope join the same home Wi-Fi (scope gets internet).
|
||||
- **AP mode** — connect directly to the DWARF hotspot (outdoor/no-Wi-Fi).
|
||||
- **Wi-Fi Compatible Mode** + **Enable 2.4GHz** toggles for region/legacy fixes.
|
||||
- **Region** setting (`settings_connection_10`) — country code for Wi-Fi regs.
|
||||
- **Activate Wi-Fi at Startup** — auto-enable hotspot on boot.
|
||||
- Device password screen distinguishes **default password** vs custom
|
||||
(`isDefaultPwd` arg → `DevicePasswordFragment`).
|
||||
|
||||
`DeviceSearchFragment` args: `type` (0 = first connect, 1 = add another),
|
||||
`deviceName` (nullable, for reconnect). Result carries a `DeviceInfo`
|
||||
(`com.convergence.dwarflab.net.discovery.DeviceInfo`) containing BLE-resolved
|
||||
IP/SSID/PSK (see API_REFERENCE §2.1 for the BLE `DwarfEcho` payload).
|
||||
|
||||
---
|
||||
|
||||
## 5. Capture screen (`CaptureActivity`) — the shooter
|
||||
|
||||
This is the most complex screen (~9200 lines). Landscape-oriented camera
|
||||
viewfinder with overlaid controls. Layout: `activity_capture.xml`.
|
||||
|
||||
### 5.1 Layout regions
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ [ TechniqueBar (top-right) ] [ Sky-finder actions (top-right) ] │
|
||||
│ │
|
||||
│ ┌────────────────────────────┐ ┌──────────┐ │
|
||||
│ │ │ │ Wide PIP │ ← wide-angle │
|
||||
│ │ MAIN PREVIEW (RTSP) │ │ preview │ inset (top-L) │
|
||||
│ │ Tele camera │ └──────────┘ │
|
||||
│ │ (RtspPlayerView) │ │
|
||||
│ │ + TrackBoxView overlay │ [ RGB container (right) ] │
|
||||
│ │ + PreviewBorderView │ │
|
||||
│ │ + ShootingProgressView │ │
|
||||
│ └────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─Left─┐ ┌─Joystick─┐ ┌──Right panel──┐ │
|
||||
│ │panel │ │ (polar │ │ Focus │ │
|
||||
│ │ │ │ D-pad) │ │ ┌───────────┐ │ │
|
||||
│ │ ✕back│ │ │ │ │ CAPTURE │ │ │
|
||||
│ │ PIP │ └──────────┘ │ │ BUTTON │ │ │
|
||||
│ │ more │ │ └───────────┘ │ │
|
||||
│ │ ? │ [ ParameterIndicatorBar ] │ Parameter │ │
|
||||
│ │ album│ [ DeviceIndicatorBar ] └───────────────┘ │
|
||||
│ │ clear│ │
|
||||
│ └──────┘ [ GoTo composition pill ] [ ZoomLensPanel ] │
|
||||
│ │
|
||||
│ (overlays: FocusProgress, StopProgress, CaptureStatus, │
|
||||
│ FullscreenProgress, CaliFrameProgress, PanoComposition) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 Left control panel (`view_capture_left_control_panel.xml`, 48dp wide)
|
||||
Vertical icon stack, top→bottom:
|
||||
| id | icon | action |
|
||||
|----|------|--------|
|
||||
| `iv_photography_cancel` | arrow-left | **Back** (exit capture) |
|
||||
| `iv_photography_pip` | pip icon | **Toggle PIP** (picture-in-picture wide inset) |
|
||||
| `iv_photography_more_feature_set` | (dynamic) | **More settings** (opens `CamMoreFeatureSetDialog`) |
|
||||
| `iv_photography_guide` | question-circle | **Guide/help** (hidden by default) |
|
||||
| `iv_open_album` | album icon | **Open Album** (jump to gallery) |
|
||||
| `ivClearAll` | clear-all | **Clear** (reset selection/overlay) |
|
||||
|
||||
### 5.3 Right control panel (`view_capture_right_panel.xml`, 84dp wide)
|
||||
Vertical stack:
|
||||
| id | label | icon | action |
|
||||
|----|-------|------|--------|
|
||||
| `btn_focus_view_capture_right_panel` | **Focus** (`camera_general_2`) | `icon_function_main_focus` | Open focus controls (AF, manual, astro-AF) |
|
||||
| `item_main_capture_button_view` | — (76dp circular) | dynamic | **Main shutter** (see §5.5) |
|
||||
| `btn_parameter_view_capture_right_panel` | **Parameter** (`camera_astro_222`) | `icon_function_general_parameter` | Open `CamParameterSetDialog` (exposure/gain/WB/ISP) |
|
||||
|
||||
`ZoomLensPanel` sits just left of the right panel (zoom/crop-ratio control).
|
||||
|
||||
### 5.4 TechniqueBar (top-right) — mode selector
|
||||
A pill that shows the current **ShootingModeType** + **ShootingTech** and a
|
||||
right-side action (`RightActionType`). Tapping opens
|
||||
`ShootingModeSelectDialog` to pick a mode.
|
||||
|
||||
**`ShootingModeType`** (the "scene"/target class) — `ShootingModeType.java`:
|
||||
| value | name | notes |
|
||||
|-------|------|-------|
|
||||
| 1 | `NORMAL` | generic |
|
||||
| 2 | `DSO` | deep-sky object (needs calibration + EQ) |
|
||||
| 3 | `SUN_MOON` | solar/lunar |
|
||||
| 4 | `MILKY_WAY` | Milky Way wide-field |
|
||||
| 5 | `STAR_TRAIL` | star-trail time accumulation |
|
||||
| 6 | `AUTO_TRACKING` | generic object tracking |
|
||||
| 7 | `PANORAMA` | panorama/grid capture |
|
||||
| 8 | `SUN` | sun (ND filter warning!) |
|
||||
| 9 | `MOON` | moon |
|
||||
| 10 | `PLANET` | planetary |
|
||||
| 0 | `UNKNOWN` | default |
|
||||
|
||||
Each mode carries flags: `isWidePreviewOnly`, `isWidePreviewSuggested`,
|
||||
`isPipWindowBtnEnable`, `isHidePip`, `isViewRectangleVisible`,
|
||||
`isAddQuickSet`, `cameraType` (Tele/Wide), `shootingTech`,
|
||||
`isAstroFocus`, `isShowAutoFocusSwitch`, `isSunOrMoon`, `isLongTouchStop`,
|
||||
`isSolarSystem`, `isShowZoomLens`, `isEnterAtlasVisible`, `isEnableCurves`,
|
||||
`isAreaFocusEnable`. These drive which UI controls are visible.
|
||||
|
||||
**`ShootingTech`** (the capture technique) — `ShootingTech.java`:
|
||||
| value | name | UI ring style |
|
||||
|-------|------|---------------|
|
||||
| 0 | `UNKNOWN` | — |
|
||||
| 1 | `SINGLE_SHOT` | single photo |
|
||||
| 2 | `STACKING` | live-stacking (astro) |
|
||||
| 3 | `BURST` | burst |
|
||||
| 4 | `VIDEO` | video record |
|
||||
| 5 | `TIMELAPSE` | timelapse |
|
||||
| 6 | `PANORAMA` | panorama/grid |
|
||||
|
||||
**`RightActionType`** (what the TechniqueBar's right button does):
|
||||
`NORMAL, FOCUS, PARAMETER, TECHNOLOGY, TECHNOLOGY_SETTING, MODE, LongPressStop`.
|
||||
|
||||
### 5.5 Main capture button (`MainCaptureButtonView`)
|
||||
A 76dp circular shutter whose appearance is driven by **`CaptureState`**
|
||||
(`CaptureState.java`) and the current `ShootingModeType`/`ShootingTech`.
|
||||
States include:
|
||||
`None, PreparingAstronomyDarkFrame, RawDarkTaking, RawDarkStopping,
|
||||
RawDarkStopped, RawTaking, RawStopping, GoLive, AIEnhance, AIEnhanceStopping,
|
||||
PanoramaTaking, PanoramaStopping, RecordTaking, RecordStopping, …`
|
||||
|
||||
Behavior:
|
||||
- **Tap** → start capture (photo/burst/stack/record depending on tech).
|
||||
- **Long-press** → stop (`isLongPressTriggered`; shows `item_long_press_stop`
|
||||
pill "Long Press to Stop" / `camera_general_153`, then "Stopping…").
|
||||
- Ring/teeth animations reflect exposure countdown (`ShootingProgressView`)
|
||||
and long-exposure progress (`NotifyProto.LongExpPhotoProgress`).
|
||||
|
||||
### 5.6 Joystick (`PolarDpadJoystickView`, 258dp)
|
||||
Bottom-left polar D-pad for manual slewing. Sends
|
||||
`CMD_STEP_MOTOR_SERVICE_JOYSTICK` (14006) with `vector_angle`/`vector_length`.
|
||||
Has low/high speed indicators and four directional triangle overlays.
|
||||
|
||||
### 5.7 Overlays (z-ordered by elevation)
|
||||
| Widget | elevation | purpose |
|
||||
|--------|-----------|---------|
|
||||
| `TrackBoxView` (tele + wide) | 4dp | tracking bounding-box overlay |
|
||||
| `PreviewBorderView` | — | PIP border / crop frame |
|
||||
| `ShootingProgressView` (main + minor) | 1dp | exposure countdown ring/text |
|
||||
| `FocusProgressLayout` | 3dp | autofocus sweep overlay |
|
||||
| `StopProgressLayout` | 3dp | stop-in-progress overlay |
|
||||
| `CaptureStatusLayout` | 3dp | status HUD (bottom-center) |
|
||||
| `FullscreenProgressLayout` | 4dp | full-screen progress (calibration, GoTo) |
|
||||
| `CaliFrameProgressLayout` | 4dp | calibration-frame progress |
|
||||
| `PanoCompositionLayout` | 30dp (top) | panorama composition canvas |
|
||||
| `ParameterIndicatorBar` / `DeviceIndicatorBar` | 1dp | bottom status chips |
|
||||
|
||||
### 5.8 Sky-finder / Milky-Way action rows (top-right & bottom-center)
|
||||
- `item_action_sky_finder`: calibration icon, AR-toggle, exit — for the
|
||||
Sky-Target-Finder / AR overlay mode (`CMD_ASTRO_START_SKY_TARGET_FINDER`).
|
||||
- `item_action_milky_way`: "Locate Milky Way" (`camera_astro_419`) +
|
||||
"Confirm" (`camera_astro_412`) pills — Milky Way composition helper.
|
||||
- `flGoComposition`: "GoTo" composition pill (`camera_pano_52`) for panorama
|
||||
framing.
|
||||
|
||||
### 5.9 Key dialogs launched from Capture
|
||||
| Dialog | Trigger | Purpose |
|
||||
|--------|---------|---------|
|
||||
| `ShootingModeSelectDialog` | TechniqueBar tap | Pick ShootingModeType |
|
||||
| `CamModeSelectDialog` | mode button | Pick ShootingTech (photo/video/stack/…) |
|
||||
| `CamParameterSetDialog` / `CamParamSetVM` | Parameter button | Exposure/Gain/WB/ISP per camera |
|
||||
| `CamMoreFeatureSetDialog` | left "more" icon | Extra feature toggles |
|
||||
| `PenalSetDialog` | panel button | Side-panel config |
|
||||
| `StatusHUDDialog` | indicator bar | Detailed status overlay |
|
||||
| `DarkSceneShootingDialog` | dark-frame flow | Dark-frame capture wizard |
|
||||
| `CaliFramePrepareDialog` | cali-frame flow | Calibration-frame prep |
|
||||
| `AtlasGotoDialog` | GoTo action | Confirm GoTo target |
|
||||
| `AiEnhanceProgressDialog` | AI enhance | AI post-process progress |
|
||||
| `ReservationCurveDialog` | curves | Tone/exposure curves editor |
|
||||
| `SkyFinderNodeTargetsDialog` | sky finder | Pick target from AR nodes |
|
||||
| `TrackSunHintDialog` | sun tracking | Sun-tracking safety hint |
|
||||
| `BottomTipDialog` / `TipDialog` / `MessageDialog` | various | Tips & confirmations |
|
||||
|
||||
---
|
||||
|
||||
## 6. Settings tab (`SettingsFragment`) — `fragment_settings.xml`
|
||||
|
||||
A single `RecyclerView` whose rows are built programmatically in
|
||||
`SettingsFragment.java` (grouped sections). Title: "Settings" (`settings_1`).
|
||||
|
||||
### 6.1 Top-level rows (from nav actions on `navigation_settings`)
|
||||
| Row (string) | Destination fragment |
|
||||
|--------------|----------------------|
|
||||
| Profile / Login-Register | `ProfileFragment` / `LoginRegisterFragment` |
|
||||
| **My Device** (`settings_2`) | `MyDeviceFragment` |
|
||||
| **Connection Settings** (`settings_3`) | `ConnectionSettingsFragment` |
|
||||
| **Advanced Settings** (`settings_4`) | `AdvancedFragment` |
|
||||
| **Location** (`settings_5`) | `LocationFragment` |
|
||||
| **General** (`settings_6`) | `GeneralFragment` |
|
||||
| **Voice Assistant** | `VoiceAssistantFragment` |
|
||||
| **Support** (`settings_7`) | `SupportFragment` |
|
||||
| **About** (`settings_9`) | `AboutFragment` |
|
||||
| Clear Cache (`settings_8`) | (action, no nav) |
|
||||
|
||||
### 6.2 My Device (`MyDeviceFragment`) sub-tree
|
||||
- Device name → `DeviceNameFragment` (edit name; also triggers NFC update).
|
||||
- Device password → `DevicePasswordFragment` (change pwd; `isDefaultPwd` flag)
|
||||
→ `DeviceResetFragment` (factory reset).
|
||||
- **NFC Update** (`settings_64`) → `NfcWriteActivity` — hold phone near ring light.
|
||||
- **Activation info** → `ActivateInfoFragment` (status, time, warranty).
|
||||
- **Storage** → `StorageFragment` (total/available size args).
|
||||
- Firmware/OTA — reuses `OTAUpgradeFragment`.
|
||||
|
||||
### 6.3 Connection Settings (`ConnectionSettingsFragment`)
|
||||
Toggles + radio (strings `settings_connection_1–29`):
|
||||
- **Connection mode**: Auto / STA / AP (radio).
|
||||
- **Activate Wi-Fi at Startup** (switch).
|
||||
- **Enable 2.4GHz**, **Wi-Fi Compatible Mode** (switches).
|
||||
- **Region** → `RegionSettingsFragment` (country code).
|
||||
- Configure STA network → `WiFiListFragment` → `StaInfoFragment`/`StaInfoSetFragment`.
|
||||
|
||||
### 6.4 General (`GeneralFragment`) sub-tree
|
||||
- **Language** → `LanguageSettingsFragment`.
|
||||
- **Temperature unit** → `TemperatureUnitFragment`.
|
||||
- **Appearance / Dark mode** → `AppearanceFragment`.
|
||||
- **Image correction** → `ImageCorrectionFragment` → list (`ImageCorrectionListFragment`) → item (`ImageCorrectionListItemFragment`) + helper.
|
||||
- **Floating window** → `FloatingWindowFragment`.
|
||||
- **Calendar** (dialog) → `CalendarFragment`.
|
||||
- **Auto-stop duration** → `AutoStopDurationFragment`.
|
||||
- **Emotion / voice** → `EmotionFragment`, `EmotionScaleFragment`, `SpeechRateFragment`, `VoiceTypeFragment`, `VoiceNavigationModeFragment`.
|
||||
- **Upload log / Log type** → `UploadLogFragment`, `LogTypeFragment`.
|
||||
|
||||
### 6.5 Advanced (`AdvancedFragment`)
|
||||
Device-level toggles: master lock, MTP mode, CPU mode, low-temp protection,
|
||||
lens defog, auto-cooling, auto-shutdown (the `CMD_SYSTEM_*` / `CMD_DEVICE_*`
|
||||
commands). Shows `layout_advanced_settings_unavailable.xml` when the connected
|
||||
device/firmware doesn't support an option.
|
||||
|
||||
### 6.6 Support / About
|
||||
- **Support** (`settings_7`): FAQ/web links, contact `support@dwarflab.com`,
|
||||
log upload.
|
||||
- **About** (`settings_9`): app version, OSS notice
|
||||
(`site.dwarflabapp.com/app-about/open-source-software-android`), privacy &
|
||||
terms (en/zh).
|
||||
|
||||
---
|
||||
|
||||
## 7. OTA / firmware upgrade flow
|
||||
|
||||
```
|
||||
UpgradeReminderFragment (dialog: "new firmware available")
|
||||
└─▶ OTAUpgradeFragment (download + apply)
|
||||
└─▶ WiFiConnectFragment (dialog, if scope needs Wi-Fi for download)
|
||||
```
|
||||
- Args: `ssid`, `psd`, `isStaMode` — the scope may need to join Wi-Fi to fetch
|
||||
the firmware; the app drives this via BLE (`ReqSta`) then polls download
|
||||
progress.
|
||||
- Firmware download URL:
|
||||
`https://dwarflab.com/pages/dwarflab-app-firmware-download`.
|
||||
- `FirmwareForceUpdateDialog` blocks the app if firmware is too old for a
|
||||
required feature (e.g. astrophotography — see `camera_astro_116`).
|
||||
|
||||
---
|
||||
|
||||
## 8. Schedule / shooting-plan flow
|
||||
|
||||
```
|
||||
HomeFragment
|
||||
└─▶ ScheduleFragment (list of plans)
|
||||
├─▶ ScheduleDetailFragment (edit one plan; add targets, set params)
|
||||
│ └─▶ ScheduleErrorReasonFragment (if sync fails) [args: reason]
|
||||
└─▶ (sync/lock/unlock/delete via ScheduleProto cmds 16100–16108)
|
||||
```
|
||||
- A plan = a timed shooting session with one or more targets, per-camera
|
||||
params, and a date/time window (sunset→sunrise auto-calculated from Atlas).
|
||||
- Plans can be **locked** (16107) so they reserve their time slot; unlocking
|
||||
(16106) frees it.
|
||||
- "Add to Schedule" (`camera_general_113`), "Date of Plan"
|
||||
(`camera_general_114`), and the 12-hour-ahead sync limit
|
||||
(`camera_general_118`) are key UX constraints.
|
||||
|
||||
---
|
||||
|
||||
## 9. Shared UI conventions & components
|
||||
|
||||
### 9.1 Custom widgets (`ui/widget/`)
|
||||
| Widget | Path | Role |
|
||||
|--------|------|------|
|
||||
| `RtspPlayerView` | `media/` | RTSP preview (ijkplayer, TCP transport) |
|
||||
| `PolarDpadJoystickView` | `widget/joystick/` | Polar slew D-pad |
|
||||
| `TechniqueBar` | `widget/capture/` | Mode/tech selector pill |
|
||||
| `MainCaptureButtonView` | `widget/capture/` | Circular shutter |
|
||||
| `CaptureLeftControlPanel` / `CaptureRightControlPanel` | `widget/capture/` | Side control columns |
|
||||
| `ZoomLensPanel` | `widget/capture/` | Zoom / crop-ratio |
|
||||
| `ParameterIndicatorBar` / `DeviceIndicatorBar` | `widget/capture/` | Bottom status chips |
|
||||
| `ShootingProgressView` | `widget/capture/` | Exposure countdown ring |
|
||||
| `TrackBoxView` | `widget/camera/module/` | Tracking bbox overlay |
|
||||
| `PreviewBorderView` | `widget/camera/module/` | PIP/crop border |
|
||||
| `PanoPreviewGridLineView` | `widget/camera/module/` | Panorama grid |
|
||||
| `TargetRectView` | `widget/camera/module/` | Target rectangle |
|
||||
| `FocusProgressLayout` / `StopProgressLayout` | `widget/camera/module/` | Full-screen progress |
|
||||
| `RGBContainerLayout` | `widget/camera/function/` | RGB ring-light control |
|
||||
| `FullscreenProgressLayout` / `CaliFrameProgressLayout` / `PanoCompositionLayout` | `widget/capture/` | Full-screen overlays |
|
||||
| `DWRangeSliderView` | `widget/atlas/` | Range slider (params) |
|
||||
| `FitSystemView` | `widget/` | Status-bar inset helper |
|
||||
|
||||
### 9.2 Design tokens
|
||||
- Material 3 color scheme: `dl_color_on_surface`, `dl_color_surface_container_low_dark`,
|
||||
`dl_color_primary_dark`, `dl_color_mask_strong`, `dl_color_border_variant_dark`,
|
||||
`dl_color_white_80`, `dl_color_transparent`…
|
||||
- Spacing scale: `dl_gap_level_4/8/12/16/24/32` (4-pt grid), `dl_margin_level_3`,
|
||||
`dl_padding_level_2`, `dl_radius_rounded`, `dl_radius_medium`.
|
||||
- Typography styles: `title_large_emphasized`, `title_small_emphasized`,
|
||||
`body_medium`, `body_medium_emphasized`, `body_small_emphasized`,
|
||||
`label_medium`, `Photography_label_2`, `CamControlBtnStyle`.
|
||||
- Transitions: `right_slide_enter/exit`, `left_slide_enter/exit` (push),
|
||||
`dialog_bottom_enter/exit` (bottom-sheets).
|
||||
|
||||
### 9.3 Guide / onboarding overlays
|
||||
`ui/guide/overlay/`: `BaseGuideDialogFragment`,
|
||||
`GuideDialogFragment`, `ActivityGuideDialogFragment`,
|
||||
`FullScreenOverlayDialogFragment` — coach-mark overlays with anchor points
|
||||
(`ui/guide/anchor/`) used for first-run feature discovery.
|
||||
|
||||
---
|
||||
|
||||
## 10. Internationalization
|
||||
|
||||
- Default strings in `res/values/strings.xml`; localized variants exist
|
||||
(Chinese `values-zh`, etc.). All user-visible text is keyed
|
||||
(`camera_astro_N`, `camera_general_N`, `settings_N`, `home_N`,
|
||||
`settings_connection_N`, `album_N`, `guide_N`…).
|
||||
- Help docs link to language-specific URLs:
|
||||
`https://help.dwarflab.com/en` vs `/zh`.
|
||||
- Voice assistant has its own strings and supports emotion/speech-rate tuning.
|
||||
|
||||
---
|
||||
|
||||
## 11. Map: UI action → API command
|
||||
|
||||
| UI action | WS command(s) | Proto |
|
||||
|-----------|---------------|-------|
|
||||
| Tap shutter (photo) | 10002 / 12022 | `ReqPhotograph` |
|
||||
| Burst start/stop | 10003 / 10004 | `…Burst` |
|
||||
| Video record start/stop | 10005 / 10006 | `…Record` |
|
||||
| Set exposure | 10009 / 12004 | `ReqSetExp` |
|
||||
| Set gain | 10013 / 12006 | `ReqSetGain` |
|
||||
| WB / brightness / contrast / saturation / hue / sharpness | 10015–10024 | `ReqSetWB…` |
|
||||
| Open/close camera | 10000/10001 (tele), 12000/12001 (wide) | — |
|
||||
| Joystick slew | 14006 | `ReqMotorServiceJoystick` |
|
||||
| GoTo target | 11002/11003/11013/11014 | `ReqStartGotoDSO`/`…SolarSystem` |
|
||||
| Calibrate | 11000 | `ReqStartCalibration` |
|
||||
| Live stacking | 11005/11006 | `Req…LiveStacking` |
|
||||
| Dark frames | 11007/11021 | `Req…RawDark…` |
|
||||
| Auto-focus | 15000 | `ReqAutoFocus` |
|
||||
| Start track | 14800 | `ReqStartTrack` |
|
||||
| Panorama grid | 15500/15501 | `Req…Grid` |
|
||||
| AI enhance | 11029/11030 | `Req…AiEnhance` |
|
||||
| EQ solving | 11018/11019 | `Req…EqSolving` |
|
||||
| Power / reboot / RGB | 13500–13505 | `RGB.proto` |
|
||||
| Set time/location | 13000/13010 | `ReqSetTime`/`ReqSetLocation` |
|
||||
| Sync schedule | 16100 | `Schedule.proto` |
|
||||
|
||||
Full command table: `CMD_TABLE.md`. Proto payloads: `analysis/protos/`.
|
||||
|
||||
---
|
||||
|
||||
## 12. Suggested reimplementation plan (cross-platform)
|
||||
|
||||
1. **Screens to build first** (MVP): Splash → Home (connect CTA) →
|
||||
DeviceSearch (BLE) → DeviceConnect (password) → Capture (preview + shutter
|
||||
+ params + joystick) → Settings (My Device + Connection).
|
||||
2. **State model**: a single `DeviceStore` (connected? device_id, ip, cameras
|
||||
on/off, current ShootingModeType/Tech, CaptureState, live params) fed by
|
||||
the WebSocket NOTIFY stream (§API_REFERENCE §3.2). All UI reads from this.
|
||||
3. **Capture screen** is the hard part — replicate the overlay z-order
|
||||
(§5.7) and drive the capture button from `CaptureState`.
|
||||
4. **Two preview surfaces** (Tele main + Wide PIP) each need an RTSP
|
||||
consumer; toggle PIP via `CMD_SWITCH_MAIN_PREVIEW` (14809).
|
||||
5. Keep the **mode-driven visibility** model: each `ShootingModeType` flag
|
||||
(`isWidePreviewOnly`, `isAstroFocus`, `isShowZoomLens`…) controls which
|
||||
controls render — don't hardcode per-screen logic.
|
||||
Reference in New Issue
Block a user