droidrun_core/driver/
recording.rs1use std::collections::HashSet;
3use std::path::Path;
4use std::sync::Mutex;
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9use super::{Action, AppInfo, DeviceDriver};
10use crate::error::Result;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(tag = "action_type")]
15pub enum RecordedAction {
16 #[serde(rename = "tap")]
17 Tap { x: i32, y: i32 },
18
19 #[serde(rename = "swipe")]
20 Swipe {
21 start_x: i32,
22 start_y: i32,
23 end_x: i32,
24 end_y: i32,
25 duration_ms: u32,
26 },
27
28 #[serde(rename = "input_text")]
29 InputText { text: String, clear: bool },
30
31 #[serde(rename = "key_press")]
32 KeyPress { keycode: i32 },
33
34 #[serde(rename = "start_app")]
35 StartApp {
36 package: String,
37 activity: Option<String>,
38 },
39
40 #[serde(rename = "drag")]
41 Drag {
42 start_x: i32,
43 start_y: i32,
44 end_x: i32,
45 end_y: i32,
46 duration_ms: u32,
47 },
48}
49
50pub struct RecordingDriver<D: DeviceDriver> {
55 inner: D,
56 log: Mutex<Vec<RecordedAction>>,
57}
58
59impl<D: DeviceDriver> RecordingDriver<D> {
60 pub fn new(inner: D) -> Self {
61 Self {
62 inner,
63 log: Mutex::new(Vec::new()),
64 }
65 }
66
67 pub fn recorded_actions(&self) -> Vec<RecordedAction> {
69 self.log.lock().unwrap().clone()
70 }
71
72 pub fn clear_log(&self) {
74 self.log.lock().unwrap().clear();
75 }
76
77 pub fn to_json(&self) -> serde_json::Result<String> {
79 serde_json::to_string_pretty(&self.recorded_actions())
80 }
81
82 fn record(&self, action: RecordedAction) {
83 self.log.lock().unwrap().push(action);
84 }
85}
86
87#[async_trait]
88impl<D: DeviceDriver> DeviceDriver for RecordingDriver<D> {
89 async fn connect(&mut self) -> Result<()> {
90 self.inner.connect().await
91 }
92
93 async fn ensure_connected(&mut self) -> Result<()> {
94 self.inner.ensure_connected().await
95 }
96
97 async fn tap(&self, x: i32, y: i32) -> Result<()> {
98 self.inner.tap(x, y).await?;
99 self.record(RecordedAction::Tap { x, y });
100 Ok(())
101 }
102
103 async fn swipe(
104 &self,
105 x1: i32,
106 y1: i32,
107 x2: i32,
108 y2: i32,
109 duration_ms: u32,
110 ) -> Result<()> {
111 self.inner.swipe(x1, y1, x2, y2, duration_ms).await?;
112 self.record(RecordedAction::Swipe {
113 start_x: x1,
114 start_y: y1,
115 end_x: x2,
116 end_y: y2,
117 duration_ms,
118 });
119 Ok(())
120 }
121
122 async fn input_text(&self, text: &str, clear: bool) -> Result<bool> {
123 let result = self.inner.input_text(text, clear).await?;
124 self.record(RecordedAction::InputText {
125 text: text.to_string(),
126 clear,
127 });
128 Ok(result)
129 }
130
131 async fn press_key(&self, keycode: i32) -> Result<()> {
132 self.inner.press_key(keycode).await?;
133 self.record(RecordedAction::KeyPress { keycode });
134 Ok(())
135 }
136
137 async fn drag(
138 &self,
139 x1: i32,
140 y1: i32,
141 x2: i32,
142 y2: i32,
143 duration_ms: u32,
144 ) -> Result<()> {
145 self.inner.drag(x1, y1, x2, y2, duration_ms).await?;
146 self.record(RecordedAction::Drag {
147 start_x: x1,
148 start_y: y1,
149 end_x: x2,
150 end_y: y2,
151 duration_ms,
152 });
153 Ok(())
154 }
155
156 async fn start_app(&self, package: &str, activity: Option<&str>) -> Result<String> {
157 let result = self.inner.start_app(package, activity).await?;
158 self.record(RecordedAction::StartApp {
159 package: package.to_string(),
160 activity: activity.map(|a| a.to_string()),
161 });
162 Ok(result)
163 }
164
165 async fn install_app(&self, path: &Path) -> Result<String> {
168 self.inner.install_app(path).await
169 }
170
171 async fn get_apps(&self, include_system: bool) -> Result<Vec<AppInfo>> {
172 self.inner.get_apps(include_system).await
173 }
174
175 async fn list_packages(&self, include_system: bool) -> Result<Vec<String>> {
176 self.inner.list_packages(include_system).await
177 }
178
179 async fn screenshot(&self, hide_overlay: bool) -> Result<Vec<u8>> {
180 self.inner.screenshot(hide_overlay).await
181 }
182
183 async fn get_ui_tree(&self) -> Result<serde_json::Value> {
184 self.inner.get_ui_tree().await
185 }
186
187 async fn get_date(&self) -> Result<String> {
188 self.inner.get_date().await
189 }
190
191 fn supported_actions(&self) -> &HashSet<Action> {
192 self.inner.supported_actions()
193 }
194}