feat(scene): add scene editor, executor and fakes (phase 1)

Introduce the scene module skeleton that satisfies phase 1 exit conditions for
tzh's responsibility in project_plan.md section 6.5: scene list, editor and Fake-driven
execution that can be tested independently.

Domain layer (domain/):
- Scene, SceneAction (SocketPower / InfraredKey), SceneIcon, SceneColor
- SceneExecutionResult with per-action outcome aggregation
- SceneRepository port (observe/get/create/update/delete, provider-isolated)
- SceneExecutor port (sequential execution, per-action failure continues)

Fakes (testing/fakes/):
- FakeSceneRepository: in-memory, provider-isolated, seeds local-experience catalog
- FakeSceneExecutor: drives existing SocketGateway/InfraredGateway fakes,
  rejects concurrent execution of the same scene

Android app (android/app/):
- SceneListScreen / SceneListViewModel: list, empty state, delete confirm
- SceneEditorScreen / SceneEditorViewModel: name/icon/color, action add/remove/reorder
- SceneExecutionScreen / SceneExecutionViewModel: run, progress, per-action summary
- SceneRoutes + NavGraphBuilder extensions wired into ABoxManagerApp
- SceneModule (debug) binds FakeSceneRepository, FakeSceneExecutor and a
  LocalProfileId qualifier that feat/provider-login will retire later

Build (settings.gradle.kts):
- Apply foojay-resolver-convention so the JVM 17 toolchain is auto-provisioned

Resources: zh-rCN / en strings for all scene surfaces
Tests: 20 unit tests covering repository isolation, CRUD, executor ordering,
failure continuation and concurrent-execution rejection
This commit is contained in:
tzh
2026-08-25 11:25:35 +08:00
committed by Moler
parent ed98c2be98
commit 533ce46de6
19 changed files with 1884 additions and 0 deletions
@@ -42,4 +42,43 @@
<string name="scenes_placeholder_description">Scene actions and execution slot for tzh.</string>
<string name="activity_placeholder_title">Activity Log</string>
<string name="activity_placeholder_description">No recorded events yet.</string>
<!-- Scene: list -->
<string name="scene_list_title">Scenes</string>
<string name="scene_create_action">New scene</string>
<string name="scene_action_count">%1$d actions</string>
<string name="scene_edit">Edit scene</string>
<string name="scene_delete">Delete scene</string>
<string name="scene_empty_title">No scenes yet</string>
<string name="scene_empty_message">Create your first scene to combine socket and infrared actions.</string>
<!-- Scene: delete dialog -->
<string name="scene_delete_dialog_title">Delete scene</string>
<string name="scene_delete_dialog_message">This action cannot be undone.</string>
<string name="scene_delete_confirm">Delete</string>
<string name="scene_delete_cancel">Cancel</string>
<!-- Scene: editor -->
<string name="scene_editor_new_title">New scene</string>
<string name="scene_editor_edit_title">Edit scene</string>
<string name="scene_back">Back</string>
<string name="scene_name_label">Name</string>
<string name="scene_icon_label">Icon</string>
<string name="scene_color_label">Accent color (hex)</string>
<string name="scene_actions_label">Actions (%1$d)</string>
<string name="scene_add_socket_action">Add socket action</string>
<string name="scene_add_infrared_action">Add infrared action</string>
<string name="scene_device_name_label">Device name</string>
<string name="scene_infrared_key_label">Infrared key</string>
<string name="scene_add_action">Add</string>
<string name="scene_save_action">Save scene</string>
<string name="scene_action_socket">Socket %1$s -> %2$s</string>
<string name="scene_action_infrared">Infrared %1$s -> %2$s</string>
<!-- Scene: execution -->
<string name="scene_execution_title">Run scene</string>
<string name="scene_execution_run">Run</string>
<string name="scene_execution_running">Running %1$s…</string>
<string name="scene_execution_summary">%1$d succeeded, %2$d failed of %3$d actions</string>
<string name="scene_execution_done">Done</string>
</resources>