diff --git a/src/Quamotion.Malaga/TouchAction.cs b/src/Quamotion.Malaga/TouchAction.cs
new file mode 100644
index 0000000..78acfa3
--- /dev/null
+++ b/src/Quamotion.Malaga/TouchAction.cs
@@ -0,0 +1,217 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Quamotion.Malaga
+{ ///
+ /// Represents a touch action which can be executed using the WebDriverAgent.
+ ///
+ public class TouchAction
+ {
+ ///
+ /// Gets the operation being executed.
+ ///
+ [JsonConverter(typeof(StringEnumConverter), /* camelCaseText */ true)]
+ [JsonProperty("action")]
+ public TouchOperation Action { get; private set; }
+
+ ///
+ /// Gets a dictionary which contains the options for this operation.
+ ///
+ [JsonProperty("options")]
+ public Dictionary Options { get; private set; } = new Dictionary();
+
+ ///
+ /// Performs a tap (finger down and finger up) operation.
+ ///
+ ///
+ /// The x-coordinate of the position at which to tap.
+ ///
+ ///
+ /// The y-coordinate of the position at which to tap.
+ ///
+ ///
+ /// The number of taps to perform.
+ ///
+ ///
+ /// A which represents the tap operation.
+ ///
+ public static TouchAction Tap(double x, double y, int count = 1)
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.Tap,
+ Options = new Dictionary()
+ {
+ { nameof(x), x },
+ { nameof(y), y },
+ { nameof(count), count }
+ }
+ };
+ }
+
+ ///
+ /// Performs a long press operation.
+ ///
+ ///
+ /// The x-coordinate of the position at which to tap.
+ ///
+ ///
+ /// The y-coordinate of the position at which to tap.
+ ///
+ ///
+ /// The duration, in milliseconds, of the tap gesture.
+ ///
+ ///
+ /// The amount of pressure to apply.
+ ///
+ ///
+ /// A which represents the long press operation.
+ ///
+ public static TouchAction LongPress(double x, double y, double duration = 500, double pressure = 0)
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.LongPress,
+ Options = new Dictionary()
+ {
+ { nameof(x), x },
+ { nameof(y), y },
+ { nameof(duration), duration },
+ { nameof(pressure), pressure }
+ }
+ };
+ }
+
+ ///
+ /// Performs a finger down operation.
+ ///
+ ///
+ /// The x-coordinate of the position at which to perform the finger down.
+ ///
+ ///
+ /// The y-coordinate of the position at which to perform the finger down.
+ ///
+ ///
+ /// The amount of pressure to apply.
+ ///
+ ///
+ /// A which represents the finger down operation.
+ ///
+ public static TouchAction Press(double x, double y, double pressure = 0)
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.Press,
+ Options = new Dictionary()
+ {
+ { nameof(x), x },
+ { nameof(y), y },
+ { nameof(pressure), pressure }
+ }
+ };
+ }
+
+ ///
+ /// Performs a finger up operation.
+ ///
+ ///
+ /// A which represents the finger up operation.
+ ///
+ public static TouchAction Release()
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.Release,
+ Options = new Dictionary()
+ {
+ }
+ };
+ }
+
+ ///
+ /// Moves the finger to a new position.
+ ///
+ ///
+ /// The x-coordiante of the position to which to move the finger.
+ ///
+ ///
+ /// The y-coordinate of the position to which to move the finger.
+ ///
+ ///
+ /// A which represents the move operation.
+ ///
+ public static TouchAction MoveTo(double x, double y)
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.MoveTo,
+ Options = new Dictionary()
+ {
+ { nameof(x), x },
+ { nameof(y), y },
+ }
+ };
+ }
+
+ ///
+ /// Waits for a specific amount of time.
+ ///
+ ///
+ /// The amount of time to wait.
+ ///
+ ///
+ /// A which represents the wait operation.
+ ///
+ public static TouchAction Wait(TimeSpan timeSpan)
+ {
+ return Wait(timeSpan.TotalMilliseconds);
+ }
+
+ ///
+ /// Waits for a specific amount of time.
+ ///
+ ///
+ /// The amount of time to wait, in milliseconds.
+ ///
+ ///
+ /// A which represents the wait operation.
+ ///
+ public static TouchAction Wait(double ms)
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.Wait,
+ Options = new Dictionary()
+ {
+ { nameof(ms), ms },
+ }
+ };
+ }
+
+ ///
+ /// Cancels the current operation.
+ ///
+ ///
+ /// A which represents the cancel operation.
+ ///
+ public static TouchAction Cancel()
+ {
+ return new TouchAction()
+ {
+ Action = TouchOperation.Cancel,
+ Options = new Dictionary()
+ {
+ }
+ };
+ }
+
+ ///
+ public override string ToString()
+ {
+ return this.Action.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Quamotion.Malaga/TouchOperation.cs b/src/Quamotion.Malaga/TouchOperation.cs
new file mode 100644
index 0000000..70f2823
--- /dev/null
+++ b/src/Quamotion.Malaga/TouchOperation.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Quamotion.Malaga
+{
+ ///
+ /// Represents a touch operation.
+ ///
+ public enum TouchOperation
+ {
+ ///
+ /// Performs a single tap.
+ ///
+ Tap,
+
+ ///
+ /// Performs a finger down and wait operation.
+ ///
+ LongPress,
+
+ ///
+ /// Performs a finger down operation.
+ ///
+ Press,
+
+ ///
+ /// Performs a finger up operation.
+ ///
+ Release,
+
+ ///
+ /// Performs a finger move operation.
+ ///
+ MoveTo,
+
+ ///
+ /// Performs a wait operation.
+ ///
+ Wait,
+
+ ///
+ /// Cancels the current operation.
+ ///
+ Cancel
+ }
+}
diff --git a/src/Quamotion.Malaga/WdaCommandExecutor.cs b/src/Quamotion.Malaga/WdaCommandExecutor.cs
index c5425d4..6694b91 100644
--- a/src/Quamotion.Malaga/WdaCommandExecutor.cs
+++ b/src/Quamotion.Malaga/WdaCommandExecutor.cs
@@ -29,6 +29,10 @@ public WdaCommandExecutor(Uri addressOfRemoteServer, string sessionId, TimeSpan
this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.Type, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/keyboard/type"));
this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.PressDeviceButton, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/pressDeviceButton"));
+
+ this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.TouchAndHold, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/touchAndHold"));
+ this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.Drag, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/dragfromtoforduration"));
+ this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.PerformTouch, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/touch/perform"));
}
public override Response Execute(Command commandToExecute)
diff --git a/src/Quamotion.Malaga/WdaDriver.cs b/src/Quamotion.Malaga/WdaDriver.cs
index eb112dd..b3128ac 100644
--- a/src/Quamotion.Malaga/WdaDriver.cs
+++ b/src/Quamotion.Malaga/WdaDriver.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Drawing;
namespace Quamotion.Malaga
{
@@ -110,6 +111,103 @@ public virtual ScreenOrientation Rotation
}
}
+ ///
+ /// Sends a touch and hold event to the device.
+ ///
+ ///
+ /// The position at which to touch.
+ ///
+ ///
+ /// The duration of the touch event, in seconds.
+ ///
+ public virtual void TouchAndHold(PointF location, TimeSpan duration)
+ {
+ // TODO: find a way to handle the timeout
+ // var timeout = Math.Max(Timeouts.TestServerRequestTimeout.TotalSeconds, 2 * duration.TotalSeconds);
+ this.Execute(
+ WdaDriverCommand.TouchAndHold,
+ new Dictionary()
+ {
+ { "x", location.X },
+ { "y", location.Y },
+ { "duration", duration.TotalSeconds }
+ });
+ }
+
+ ///
+ /// Sends a drag gesture to the device.
+ ///
+ ///
+ /// The location at which the drag gesture begins.
+ ///
+ ///
+ /// The location at which the drag gesture ends.
+ ///
+ ///
+ /// The duration of the drag gesture.
+ ///
+ public virtual void Drag(PointF from, PointF to, TimeSpan duration)
+ {
+ // Drag on iOS is slow, _very_ slow.
+ // Increase the timeout to make sure we don't time out (which would be a TaskCancelledException)
+ // TODO: find a way to work with the timeout
+ // var timeout = Math.Max(Timeouts.TestServerRequestTimeout.TotalSeconds, 15 + (5 * duration.TotalSeconds));
+
+ this.Execute(
+ WdaDriverCommand.Drag,
+ new Dictionary()
+ {
+
+ { "fromX", from.X },
+ { "fromY", from.Y },
+ { "toX", to.X },
+ { "toY", to.Y },
+ { "duration", duration.TotalSeconds }
+ });
+ }
+
+ ///
+ /// Performs a sequence of touch actions.
+ ///
+ ///
+ /// The touch actions to execute.
+ ///
+ ///
+ /// A which can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// A which represents the asynchronous operation.
+ ///
+ public virtual void PerformTouch(Collection touches)
+ {
+ double duration = 0;
+
+ foreach (var touch in touches)
+ {
+ if (touch.Action == TouchOperation.Wait)
+ {
+ duration += (double)touch.Options["ms"];
+ }
+ else if (touch.Action == TouchOperation.LongPress)
+ {
+ duration += (double)touch.Options["duration"];
+ }
+ }
+
+ // TODO: figure out a way to pass the timeout
+ // TimeSpan.FromMilliseconds(Timeouts.TestServerRequestTimeout.TotalMilliseconds + (2 * duration)),
+
+ var json = JsonConvert.SerializeObject(
+ new
+ {
+ actions = touches
+ });
+
+ this.Execute(
+ WdaDriverCommand.PerformTouch
+ json);
+ }
+
///
/// Gets the text of the current alert.
///
diff --git a/src/Quamotion.Malaga/WdaDriverCommand.cs b/src/Quamotion.Malaga/WdaDriverCommand.cs
index 45f0a88..8010e4f 100644
--- a/src/Quamotion.Malaga/WdaDriverCommand.cs
+++ b/src/Quamotion.Malaga/WdaDriverCommand.cs
@@ -9,7 +9,6 @@ public static class WdaDriverCommand
public const string GetOrientation = "getOrientation";
public const string SetOrientation = "setOrientation";
-
public const string GetRotation = "getRotation";
public const string SetRotation = "setRotation";
@@ -21,5 +20,9 @@ public static class WdaDriverCommand
public const string Type = "wdaType";
public const string PressDeviceButton = "deviceButton";
+
+ public const string TouchAndHold = "touchAndHold";
+ public const string Drag = "drag";
+ public const string PerformTouch = "performTouch";
}
}