Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added DumpScreenAsync, FindElementAsync, and FindElementsAsync methods #36

Merged
merged 2 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ public void InstallCommit(DeviceData device, string session) =>

public XmlDocument DumpScreen(DeviceData device) =>
throw new NotImplementedException();

public Task<XmlDocument> DumpScreenAsync(DeviceData device) =>
throw new NotImplementedException();

public void Click(DeviceData device, Cords cords) =>
throw new NotImplementedException();
Expand All @@ -159,9 +162,15 @@ public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed)

public Element FindElement(DeviceData device, string xpath, TimeSpan timeout = default) =>
throw new NotImplementedException();

public Task<Element> FindElementAsync(DeviceData device, string xpath, TimeSpan timeout = default) =>
throw new NotImplementedException();

public Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout = default) =>
throw new NotImplementedException();

public Task<Element[]> FindElementsAsync(DeviceData device, string xpath, TimeSpan timeout = default) =>
throw new NotImplementedException();

public void SendKeyEvent(DeviceData device, string key) =>
throw new NotImplementedException();
Expand All @@ -172,10 +181,10 @@ public void SendText(DeviceData device, string text) =>
public void ClearInput(DeviceData device, int charcount) =>
throw new NotImplementedException();

public void StartApp(DeviceData device, string packagename) =>
public Task StartApp(DeviceData device, string packagename) =>
throw new NotImplementedException();

public void StopApp(DeviceData device, string packagename) =>
public Task StopApp(DeviceData device, string packagename) =>
throw new NotImplementedException();

public void BackBtn(DeviceData device) =>
Expand Down
101 changes: 99 additions & 2 deletions AdvancedSharpAdbClient/AdbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,28 @@ public XmlDocument DumpScreen(DeviceData device)
return null;
}

/// <inheritdoc/>
public async Task<XmlDocument> DumpScreenAsync(DeviceData device)
{
XmlDocument doc = new();
using IAdbSocket socket = adbSocketFactory(EndPoint);
socket.SetDevice(device);
socket.SendAdbRequest("shell:uiautomator dump /dev/tty");
AdbResponse response = socket.ReadAdbResponse();
using StreamReader reader = new(socket.GetShellStream(), Encoding);
#if !NET35
string xmlString = await reader.ReadToEndAsync();
#else
string xmlString = await Utilities.Run(reader.ReadToEnd).ConfigureAwait(false);
#endif
xmlString = xmlString.Replace("Events injected: 1\r\n", "").Replace("UI hierchary dumped to: /dev/tty", "").Trim();
if (xmlString != "" && !xmlString.StartsWith("ERROR"))
{
doc.LoadXml(xmlString);
return doc;
}
return null;
}

/// <inheritdoc/>
public void Click(DeviceData device, Cords cords)
Expand Down Expand Up @@ -889,6 +911,41 @@ public Element FindElement(DeviceData device, string xpath, TimeSpan timeout = d
return null;
}

/// <inheritdoc/>
public async Task<Element> FindElementAsync(DeviceData device, string xpath, TimeSpan timeout = default)
{
Stopwatch stopwatch = new();
stopwatch.Start();
while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout)
{
XmlDocument doc = await DumpScreenAsync(device);
if (doc != null)
{
XmlNode xmlNode = doc.SelectSingleNode(xpath);
if (xmlNode != null)
{
string bounds = xmlNode.Attributes["bounds"].Value;
if (bounds != null)
{
int[] cords = bounds.Replace("][", ",").Replace("[", "").Replace("]", "").Split(',').Select(int.Parse).ToArray(); // x1, y1, x2, y2
Dictionary<string, string> attributes = new();
foreach (XmlAttribute at in xmlNode.Attributes)
{
attributes.Add(at.Name, at.Value);
}
Cords cord = new((cords[0] + cords[2]) / 2, (cords[1] + cords[3]) / 2); // Average x1, y1, x2, y2
return new Element(this, device, cord, attributes);
}
}
}
if (timeout == TimeSpan.Zero)
{
break;
}
}
return null;
}

/// <inheritdoc/>
public Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout = default)
{
Expand Down Expand Up @@ -928,6 +985,46 @@ public Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout
}
return null;
}

/// <inheritdoc/>
public async Task<Element[]> FindElementsAsync(DeviceData device, string xpath, TimeSpan timeout = default)
{
Stopwatch stopwatch = new();
stopwatch.Start();
while (timeout == TimeSpan.Zero || stopwatch.Elapsed < timeout)
{
XmlDocument doc = await DumpScreenAsync(device);
if (doc != null)
{
XmlNodeList xmlNodes = doc.SelectNodes(xpath);
if (xmlNodes != null)
{
Element[] elements = new Element[xmlNodes.Count];
for (int i = 0; i < elements.Length; i++)
{
string bounds = xmlNodes[i].Attributes["bounds"].Value;
if (bounds != null)
{
int[] cords = bounds.Replace("][", ",").Replace("[", "").Replace("]", "").Split(',').Select(int.Parse).ToArray(); // x1, y1, x2, y2
Dictionary<string, string> attributes = new();
foreach (XmlAttribute at in xmlNodes[i].Attributes)
{
attributes.Add(at.Name, at.Value);
}
Cords cord = new((cords[0] + cords[2]) / 2, (cords[1] + cords[3]) / 2); // Average x1, y1, x2, y2
elements[i] = new Element(this, device, cord, attributes);
}
}
return elements.Length == 0 ? null : elements;
}
}
if (timeout == TimeSpan.Zero)
{
break;
}
}
return null;
}

/// <inheritdoc/>
public void SendKeyEvent(DeviceData device, string key)
Expand Down Expand Up @@ -969,11 +1066,11 @@ public void ClearInput(DeviceData device, int charcount)
}

/// <inheritdoc/>
public async void StartApp(DeviceData device, string packagename) =>
public async Task StartApp(DeviceData device, string packagename) =>
await ExecuteRemoteCommandAsync($"monkey -p {packagename} 1", device, null, CancellationToken.None);

/// <inheritdoc/>
public async void StopApp(DeviceData device, string packagename) =>
public async Task StopApp(DeviceData device, string packagename) =>
await ExecuteRemoteCommandAsync($"am force-stop {packagename}", device, null, CancellationToken.None);

/// <inheritdoc/>
Expand Down
29 changes: 27 additions & 2 deletions AdvancedSharpAdbClient/Interfaces/IAdbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,13 @@ public interface IAdbClient
/// <returns>Xml containing current hierarchy.</returns>
XmlDocument DumpScreen(DeviceData device);

/// <summary>
/// Gets the current device screen snapshot asynchronously.
/// </summary>
/// <param name="device">The device for which to get the screen snapshot.</param>
/// <returns>Xml containing current hierarchy.</returns>
Task<XmlDocument> DumpScreenAsync(DeviceData device);

/// <summary>
/// Clicks on the specified coordinates.
/// </summary>
Expand Down Expand Up @@ -476,6 +483,15 @@ public interface IAdbClient
/// <returns>The <see cref="Element"/> class</returns>
Element FindElement(DeviceData device, string xpath, TimeSpan timeout = default);

/// <summary>
/// Get element by xpath asynchronously. You can specify the waiting time in timeout.
/// </summary>
/// <param name="device"></param>
/// <param name="xpath"></param>
/// <param name="timeout"></param>
/// <returns>The <see cref="Element"/> class</returns>
Task<Element> FindElementAsync(DeviceData device, string xpath, TimeSpan timeout = default);

/// <summary>
/// Get elements by xpath. You can specify the waiting time in timeout.
/// </summary>
Expand All @@ -485,6 +501,15 @@ public interface IAdbClient
/// <returns>The <see cref="Element"/> class</returns>
Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout = default);

/// <summary>
/// Get elements by xpath asynchronously. You can specify the waiting time in timeout.
/// </summary>
/// <param name="device"></param>
/// <param name="xpath"></param>
/// <param name="timeout"></param>
/// <returns>The <see cref="Element"/> class</returns>
Task<Element[]> FindElementsAsync(DeviceData device, string xpath, TimeSpan timeout = default);

/// <summary>
/// Send keyevent to specific. You can see keyevents here https://developer.android.com/reference/android/view/KeyEvent.
/// </summary>
Expand All @@ -509,14 +534,14 @@ public interface IAdbClient
/// </summary>
/// <param name="device"></param>
/// <param name="packagename"></param>
void StartApp(DeviceData device, string packagename);
Task StartApp(DeviceData device, string packagename);

/// <summary>
/// Stop an Android application on device.
/// </summary>
/// <param name="device"></param>
/// <param name="packagename"></param>
void StopApp(DeviceData device, string packagename);
Task StopApp(DeviceData device, string packagename);

/// <summary>
/// Click BACK button.
Expand Down