1use std::net::SocketAddrV4;
2use std::time::Duration;
3
4use adb_client::{ADBDeviceExt, ADBServer, ADBServerDevice};
5use image::DynamicImage;
6
7use crate::common::point::Point;
8use crate::error::{DroidError, Result};
9
10pub struct DeviceController {
11 device: ADBServerDevice,
12}
13
14impl DeviceController {
15 pub fn new(device_identifier: Option<&str>, adb_addr: SocketAddrV4) -> Result<Self> {
19 let mut server = ADBServer::new(adb_addr);
20
21 let devices = server
22 .devices()
23 .map_err(|e| DroidError::AdbError(e.to_string()))?;
24 if devices.is_empty() {
25 return Err(DroidError::DeviceNotFound);
26 }
27
28 let target_identifier = match device_identifier {
29 Some(identifier) => devices
30 .iter()
31 .find(|d| d.identifier == identifier)
32 .map(|d| d.identifier.clone())
33 .ok_or(DroidError::DeviceNotFound)?,
34 None => devices[0].identifier.clone(),
35 };
36
37 log::info!(
38 "Connecting to device '{}' via ADB server at {}",
39 target_identifier,
40 adb_addr
41 );
42 let device = server
43 .get_device_by_name(&target_identifier)
44 .map_err(|e| DroidError::AdbError(e.to_string()))?;
45
46 Ok(Self { device })
47 }
48
49 fn shell(&mut self, command: &str) -> Result<String> {
52 log::debug!("Executing ADB shell command: {}", command);
53 let args: Vec<&str> = command.split_whitespace().collect();
54 let mut output_buffer: Vec<u8> = Vec::new();
55
56 self.device
57 .shell_command(&args, &mut output_buffer)
58 .map_err(|e| DroidError::AdbError(e.to_string()))?;
59
60 String::from_utf8(output_buffer)
61 .map_err(|e| DroidError::AdbError(format!("Shell output is not valid UTF-8: {}", e)))
62 }
63
64 pub fn screenshot(&mut self) -> Result<DynamicImage> {
66 log::debug!("Capturing screenshot...");
67 let png_data = self
68 .device
69 .framebuffer_bytes()
70 .map_err(|e| DroidError::AdbError(e.to_string()))?;
71
72 image::load_from_memory(&png_data).map_err(DroidError::ImageError)
73 }
74
75 pub fn tap(&mut self, point: Point) -> Result<()> {
77 let cmd = format!("input tap {} {}", point.x, point.y);
78 self.shell(&cmd)?;
79 Ok(())
80 }
81
82 pub fn swipe(&mut self, start: Point, end: Point, duration: Duration) -> Result<()> {
84 let cmd = format!(
85 "input swipe {} {} {} {} {}",
86 start.x,
87 start.y,
88 end.x,
89 end.y,
90 duration.as_millis()
91 );
92 self.shell(&cmd)?;
93 Ok(())
94 }
95
96 pub fn input_text(&mut self, text: &str) -> Result<()> {
98 let escaped_text = text.replace(' ', "%s");
100 let cmd = format!("input text {}", escaped_text);
101 self.shell(&cmd)?;
102 Ok(())
103 }
104
105 pub fn input_keyevent(&mut self, key_code: i32) -> Result<()> {
107 let cmd = format!("input keyevent {}", key_code);
108 self.shell(&cmd)?;
109 Ok(())
110 }
111}