diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index 502ecab3..dbe03222 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -144,6 +144,9 @@ public void InstallCommit(DeviceData device, string session) => public XmlDocument DumpScreen(DeviceData device) => throw new NotImplementedException(); + + public Task DumpScreenAsync(DeviceData device) => + throw new NotImplementedException(); public void Click(DeviceData device, Cords cords) => throw new NotImplementedException(); @@ -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 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 FindElementsAsync(DeviceData device, string xpath, TimeSpan timeout = default) => + throw new NotImplementedException(); public void SendKeyEvent(DeviceData device, string key) => throw new NotImplementedException(); @@ -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) => diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index fb5a2df4..ce0c300b 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -789,6 +789,28 @@ public XmlDocument DumpScreen(DeviceData device) return null; } + /// + public async Task 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; + } /// public void Click(DeviceData device, Cords cords) @@ -889,6 +911,41 @@ public Element FindElement(DeviceData device, string xpath, TimeSpan timeout = d return null; } + /// + public async Task 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 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; + } + /// public Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout = default) { @@ -928,6 +985,46 @@ public Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout } return null; } + + /// + public async Task 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 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; + } /// public void SendKeyEvent(DeviceData device, string key) @@ -969,11 +1066,11 @@ public void ClearInput(DeviceData device, int charcount) } /// - 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); /// - 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); /// diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index d6e5d00c..d28702cf 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -432,6 +432,13 @@ public interface IAdbClient /// Xml containing current hierarchy. XmlDocument DumpScreen(DeviceData device); + /// + /// Gets the current device screen snapshot asynchronously. + /// + /// The device for which to get the screen snapshot. + /// Xml containing current hierarchy. + Task DumpScreenAsync(DeviceData device); + /// /// Clicks on the specified coordinates. /// @@ -476,6 +483,15 @@ public interface IAdbClient /// The class Element FindElement(DeviceData device, string xpath, TimeSpan timeout = default); + /// + /// Get element by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// + /// + /// + /// The class + Task FindElementAsync(DeviceData device, string xpath, TimeSpan timeout = default); + /// /// Get elements by xpath. You can specify the waiting time in timeout. /// @@ -485,6 +501,15 @@ public interface IAdbClient /// The class Element[] FindElements(DeviceData device, string xpath, TimeSpan timeout = default); + /// + /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// + /// + /// + /// The class + Task FindElementsAsync(DeviceData device, string xpath, TimeSpan timeout = default); + /// /// Send keyevent to specific. You can see keyevents here https://developer.android.com/reference/android/view/KeyEvent. /// @@ -509,14 +534,14 @@ public interface IAdbClient /// /// /// - void StartApp(DeviceData device, string packagename); + Task StartApp(DeviceData device, string packagename); /// /// Stop an Android application on device. /// /// /// - void StopApp(DeviceData device, string packagename); + Task StopApp(DeviceData device, string packagename); /// /// Click BACK button.