@@ -79,6 +79,14 @@ struct WebRtcMediaStreamToken {
7979 cancellation : broadcast:: Sender < ( ) > ,
8080}
8181
82+ struct AndroidWebRtcControlTouch {
83+ started_at : Instant ,
84+ start_x : f64 ,
85+ start_y : f64 ,
86+ latest_x : f64 ,
87+ latest_y : f64 ,
88+ }
89+
8290#[ derive( Debug , Deserialize ) ]
8391#[ serde( rename_all = "camelCase" ) ]
8492pub struct WebRtcOfferPayload {
@@ -772,6 +780,7 @@ async fn run_android_webrtc_control_queue(
772780 mut receiver : mpsc:: UnboundedReceiver < ControlMessage > ,
773781) {
774782 let mut pending = VecDeque :: new ( ) ;
783+ let mut active_touch: Option < AndroidWebRtcControlTouch > = None ;
775784 loop {
776785 let mut message = match pending. pop_front ( ) {
777786 Some ( message) => message,
@@ -791,8 +800,13 @@ async fn run_android_webrtc_control_queue(
791800 }
792801 }
793802
794- if let Err ( error) =
795- run_android_webrtc_control_message ( state. clone ( ) , udid. clone ( ) , message) . await
803+ if let Err ( error) = run_android_webrtc_control_message (
804+ state. clone ( ) ,
805+ udid. clone ( ) ,
806+ message,
807+ & mut active_touch,
808+ )
809+ . await
796810 {
797811 warn ! ( "Android WebRTC control message failed for {udid}: {error}" ) ;
798812 }
@@ -803,38 +817,61 @@ async fn run_android_webrtc_control_message(
803817 state : AppState ,
804818 udid : String ,
805819 message : ControlMessage ,
820+ active_touch : & mut Option < AndroidWebRtcControlTouch > ,
806821) -> Result < ( ) , AppError > {
807- task :: spawn_blocking ( move || match message {
822+ match message {
808823 ControlMessage :: Touch { x, y, phase } => {
809824 if !x. is_finite ( ) || !y. is_finite ( ) {
810825 return Err ( AppError :: bad_request (
811826 "`x` and `y` must be finite normalized numbers." ,
812827 ) ) ;
813828 }
814- state
815- . android
816- . send_touch ( & udid, x. clamp ( 0.0 , 1.0 ) , y. clamp ( 0.0 , 1.0 ) , & phase)
829+ return handle_android_webrtc_touch (
830+ state,
831+ udid,
832+ x. clamp ( 0.0 , 1.0 ) ,
833+ y. clamp ( 0.0 , 1.0 ) ,
834+ phase,
835+ active_touch,
836+ )
837+ . await ;
817838 }
818839 ControlMessage :: EdgeTouch { x, y, phase, .. } => {
819840 if !x. is_finite ( ) || !y. is_finite ( ) {
820841 return Err ( AppError :: bad_request (
821842 "`x` and `y` must be finite normalized numbers." ,
822843 ) ) ;
823844 }
824- state
825- . android
826- . send_touch ( & udid, x. clamp ( 0.0 , 1.0 ) , y. clamp ( 0.0 , 1.0 ) , & phase)
845+ return handle_android_webrtc_touch (
846+ state,
847+ udid,
848+ x. clamp ( 0.0 , 1.0 ) ,
849+ y. clamp ( 0.0 , 1.0 ) ,
850+ phase,
851+ active_touch,
852+ )
853+ . await ;
827854 }
828855 ControlMessage :: MultiTouch { x1, y1, phase, .. } => {
829856 if !x1. is_finite ( ) || !y1. is_finite ( ) {
830857 return Err ( AppError :: bad_request (
831858 "`x1` and `y1` must be finite normalized numbers." ,
832859 ) ) ;
833860 }
834- state
835- . android
836- . send_touch ( & udid, x1. clamp ( 0.0 , 1.0 ) , y1. clamp ( 0.0 , 1.0 ) , & phase)
861+ return handle_android_webrtc_touch (
862+ state,
863+ udid,
864+ x1. clamp ( 0.0 , 1.0 ) ,
865+ y1. clamp ( 0.0 , 1.0 ) ,
866+ phase,
867+ active_touch,
868+ )
869+ . await ;
837870 }
871+ _ => { }
872+ }
873+
874+ task:: spawn_blocking ( move || match message {
838875 ControlMessage :: Key {
839876 key_code,
840877 modifiers,
@@ -863,11 +900,77 @@ async fn run_android_webrtc_control_message(
863900 ControlMessage :: RotateLeft => state. android . rotate_left ( & udid) ,
864901 ControlMessage :: RotateRight => state. android . rotate_right ( & udid) ,
865902 ControlMessage :: ToggleAppearance => state. android . toggle_appearance ( & udid) ,
903+ ControlMessage :: Touch { .. }
904+ | ControlMessage :: EdgeTouch { .. }
905+ | ControlMessage :: MultiTouch { .. } => Ok ( ( ) ) ,
866906 } )
867907 . await
868908 . map_err ( |error| AppError :: internal ( format ! ( "Failed to join Android control task: {error}" ) ) ) ?
869909}
870910
911+ async fn handle_android_webrtc_touch (
912+ state : AppState ,
913+ udid : String ,
914+ x : f64 ,
915+ y : f64 ,
916+ phase : String ,
917+ active_touch : & mut Option < AndroidWebRtcControlTouch > ,
918+ ) -> Result < ( ) , AppError > {
919+ match phase. as_str ( ) {
920+ "began" => {
921+ * active_touch = Some ( AndroidWebRtcControlTouch {
922+ started_at : Instant :: now ( ) ,
923+ start_x : x,
924+ start_y : y,
925+ latest_x : x,
926+ latest_y : y,
927+ } ) ;
928+ Ok ( ( ) )
929+ }
930+ "moved" => {
931+ if let Some ( touch) = active_touch. as_mut ( ) {
932+ touch. latest_x = x;
933+ touch. latest_y = y;
934+ }
935+ Ok ( ( ) )
936+ }
937+ "ended" => {
938+ let touch = active_touch. take ( ) . unwrap_or ( AndroidWebRtcControlTouch {
939+ started_at : Instant :: now ( ) ,
940+ start_x : x,
941+ start_y : y,
942+ latest_x : x,
943+ latest_y : y,
944+ } ) ;
945+ let distance = ( ( x - touch. start_x ) . powi ( 2 ) + ( y - touch. start_y ) . powi ( 2 ) ) . sqrt ( ) ;
946+ let duration_ms = touch. started_at . elapsed ( ) . as_millis ( ) . clamp ( 80 , 1500 ) as u64 ;
947+ task:: spawn_blocking ( move || {
948+ if distance >= 0.025 {
949+ state. android . send_swipe_adb (
950+ & udid,
951+ touch. start_x ,
952+ touch. start_y ,
953+ x,
954+ y,
955+ duration_ms,
956+ )
957+ } else {
958+ state. android . send_tap_adb ( & udid, x, y)
959+ }
960+ } )
961+ . await
962+ . map_err ( |error| {
963+ AppError :: internal ( format ! ( "Failed to join Android touch task: {error}" ) )
964+ } ) ?
965+ }
966+ "cancelled" => {
967+ * active_touch = None ;
968+ Ok ( ( ) )
969+ }
970+ _ => Ok ( ( ) ) ,
971+ }
972+ }
973+
871974async fn run_webrtc_control_queue (
872975 session : crate :: simulators:: session:: SimulatorSession ,
873976 state : AppState ,
0 commit comments