use crate::browser::views::TouchPoint;
use crate::error::Result;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct TouchManager {
client: Arc<crate::browser::cdp::CdpClient>,
}
impl TouchManager {
pub fn new(client: Arc<crate::browser::cdp::CdpClient>) -> Self {
Self { client }
}
pub async fn dispatch_touch(
&self,
event_type: &str,
touch_points: Vec<TouchPoint>,
) -> Result<()> {
let points: Vec<serde_json::Value> = touch_points
.into_iter()
.map(|p| {
let mut obj = serde_json::json!({
"x": p.x,
"y": p.y,
});
if let Some(id) = p.id {
obj["id"] = serde_json::json!(id);
}
if let Some(rx) = p.radius_x {
obj["radiusX"] = serde_json::json!(rx);
}
if let Some(ry) = p.radius_y {
obj["radiusY"] = serde_json::json!(ry);
}
if let Some(ra) = p.rotation_angle {
obj["rotationAngle"] = serde_json::json!(ra);
}
if let Some(force) = p.force {
obj["force"] = serde_json::json!(force);
}
obj
})
.collect();
self.client
.send_command(
"Input.dispatchTouchEvent",
serde_json::json!({
"type": event_type,
"touchPoints": points,
}),
)
.await?;
Ok(())
}
pub async fn tap(&self, x: f64, y: f64) -> Result<()> {
let point = TouchPoint::at(x, y);
self.dispatch_touch("touchStart", vec![point.clone()]).await?;
self.dispatch_touch("touchEnd", vec![point]).await?;
Ok(())
}
pub async fn double_tap(&self, x: f64, y: f64) -> Result<()> {
self.tap(x, y).await?;
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
self.tap(x, y).await?;
Ok(())
}
pub async fn swipe(
&self,
start_x: f64,
start_y: f64,
end_x: f64,
end_y: f64,
steps: u32,
) -> Result<()> {
let dx = (end_x - start_x) / steps as f64;
let dy = (end_y - start_y) / steps as f64;
self.dispatch_touch("touchStart", vec![TouchPoint::at(start_x, start_y)])
.await?;
for i in 1..steps {
self.dispatch_touch(
"touchMove",
vec![TouchPoint::at(start_x + dx * i as f64, start_y + dy * i as f64)],
)
.await?;
}
self.dispatch_touch("touchEnd", vec![TouchPoint::at(end_x, end_y)])
.await?;
Ok(())
}
pub async fn pinch(&self, center_x: f64, center_y: f64, scale_factor: f64) -> Result<()> {
let distance = 50.0;
let start_distance = distance;
let end_distance = distance * scale_factor;
self.dispatch_touch(
"touchStart",
vec![
TouchPoint::at(center_x - start_distance, center_y),
TouchPoint::at(center_x + start_distance, center_y),
],
)
.await?;
self.dispatch_touch(
"touchMove",
vec![
TouchPoint::at(center_x - end_distance, center_y),
TouchPoint::at(center_x + end_distance, center_y),
],
)
.await?;
self.dispatch_touch(
"touchEnd",
vec![
TouchPoint::at(center_x - end_distance, center_y),
TouchPoint::at(center_x + end_distance, center_y),
],
)
.await?;
Ok(())
}
pub async fn long_press(&self, x: f64, y: f64, duration_ms: u64) -> Result<()> {
let point = TouchPoint::at(x, y);
self.dispatch_touch("touchStart", vec![point.clone()]).await?;
tokio::time::sleep(tokio::time::Duration::from_millis(duration_ms)).await;
self.dispatch_touch("touchEnd", vec![point]).await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_touch_point_creation() {
let point = TouchPoint::at(100.0, 200.0);
assert_eq!(point.x, 100.0);
assert_eq!(point.y, 200.0);
assert_eq!(point.id, None);
}
#[test]
fn test_touch_point_with_id() {
let point = TouchPoint {
x: 50.0,
y: 75.0,
radius_x: Some(10.0),
radius_y: Some(10.0),
rotation_angle: Some(0.0),
force: Some(0.5),
id: Some(1),
};
assert_eq!(point.id, Some(1));
assert_eq!(point.force, Some(0.5));
}
}