From 2e4a2cc88a000a9d306f15a3796714b6e734b0e3 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 8 Dec 2023 20:14:21 +0800 Subject: [PATCH 01/12] Make receiver process when reading lines --- .../Receivers/ConsoleOutputReceiverTests.cs | 8 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 4 +- AdvancedSharpAdbClient/AdbClient.cs | 3 +- .../Receivers/EnvironmentVariablesReceiver.cs | 47 +++++----- .../Receivers/GetPropReceiver.cs | 48 +++++----- .../Receivers/InfoOutputReceiver.cs | 29 +++--- .../Receivers/InstallOutputReceiver.cs | 90 +++++++++---------- .../Receivers/PackageManagerReceiver.cs | 5 +- .../Receivers/ProcessOutputReceiver.cs | 30 +++---- .../Receivers/ConsoleOutputReceiver.cs | 9 +- .../Receivers/IShellOutputReceiver.cs | 14 +-- .../Receivers/MultilineReceiver.cs | 36 ++++---- .../Receivers/ShellOutputReceiver.cs | 33 +++++++ 13 files changed, 188 insertions(+), 168 deletions(-) create mode 100644 AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs diff --git a/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs index 46da400c..48de73b7 100644 --- a/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/Receivers/ConsoleOutputReceiverTests.cs @@ -51,14 +51,14 @@ public void ThrowOnErrorTest() AssertThrowsException("/dev/test: access denied"); // Should not thrown an exception - ConsoleOutputReceiver receiver = new(); - receiver.ThrowOnError("Stay calm and watch cat movies."); + ConsoleOutputReceiver receiver = new() { ParsesErrors = true }; + receiver.AddOutput("Stay calm and watch cat movies."); } private static void AssertThrowsException(string line) where T : Exception { - ConsoleOutputReceiver receiver = new(); - _ = Assert.Throws(() => receiver.ThrowOnError(line)); + ConsoleOutputReceiver receiver = new() { ParsesErrors = true }; + _ = Assert.Throws(() => receiver.AddOutput(line)); } } } diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index c15d9a52..c2144b98 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -233,10 +233,8 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS while (!cancellationToken.IsCancellationRequested) { string? line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (line == null) { break; } - - receiver?.AddOutput(line); + if (receiver?.AddOutput(line) is false) { break; } } } catch (Exception e) diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 59ebd5d0..85e36cff 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -382,7 +382,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke { string? line = reader.ReadLine(); if (line == null) { break; } - receiver?.AddOutput(line); + if (receiver?.AddOutput(line) is false) { break; } } } catch (Exception e) @@ -1175,6 +1175,7 @@ public void SendText(DeviceData device, string text) throw new InvalidTextException(); } } + /// public void StartApp(DeviceData device, string packageName) { diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index 40d72da0..adeb7b5b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -3,6 +3,7 @@ // using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient.Receivers.DeviceCommands @@ -10,7 +11,7 @@ namespace AdvancedSharpAdbClient.Receivers.DeviceCommands /// /// Processes the output of the printenv command, which dumps all environment variables of an Android device. /// - public sealed partial class EnvironmentVariablesReceiver : MultiLineReceiver + public sealed partial class EnvironmentVariablesReceiver : ShellOutputReceiver { /// /// The path to the printenv command. @@ -22,6 +23,11 @@ public sealed partial class EnvironmentVariablesReceiver : MultiLineReceiver /// private const string EnvPattern = @"^([^=\s]+)\s*=\s*(.*)$"; + /// + /// The cached by . + /// + private Regex? regex = null; + /// /// Initializes a new instance of the class. /// @@ -32,34 +38,33 @@ public EnvironmentVariablesReceiver() { } /// public Dictionary EnvironmentVariables { get; } = []; - /// - /// Processes the new lines. - /// - /// The lines. - protected override void ProcessNewLines(IEnumerable lines) + /// + [MemberNotNull(nameof(regex))] + public override bool AddOutput(string line) { - Regex regex = EnvRegex(); - foreach (string line in lines) + regex ??= EnvRegex(); + if (string.IsNullOrEmpty(line) || line.StartsWith('#')) { - if (string.IsNullOrEmpty(line) || line.StartsWith('#')) - { - continue; - } + return true; + } - Match m = regex.Match(line); - if (m.Success) - { - string label = m.Groups[1].Value.Trim(); - string value = m.Groups[2].Value.Trim(); + Match m = regex.Match(line); + if (m.Success) + { + string label = m.Groups[1].Value.Trim(); + string value = m.Groups[2].Value.Trim(); - if (label.Length > 0) - { - EnvironmentVariables[label] = value; - } + if (label.Length > 0) + { + EnvironmentVariables[label] = value; } } + return true; } + /// + protected override void Done() => regex = null; + #if NET7_0_OR_GREATER [GeneratedRegex(EnvPattern)] private static partial Regex EnvRegex(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index a7caf189..0a3dc218 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -3,6 +3,7 @@ // using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient.Receivers.DeviceCommands @@ -10,7 +11,7 @@ namespace AdvancedSharpAdbClient.Receivers.DeviceCommands /// /// Parses the output of the getprop command, which lists all properties of an Android device. /// - public sealed partial class GetPropReceiver : MultiLineReceiver + public sealed partial class GetPropReceiver : ShellOutputReceiver { /// /// The path to the getprop executable to run on the device. @@ -22,6 +23,11 @@ public sealed partial class GetPropReceiver : MultiLineReceiver /// private const string GetPropPattern = "^\\[([^]]+)\\]\\:\\s*\\[(.*)\\]$"; + /// + /// The cached by . + /// + private Regex? regex = null; + /// /// Initializes a new instance of the class. /// @@ -32,38 +38,38 @@ public GetPropReceiver() { } /// public Dictionary Properties { get; } = []; - /// - /// Processes the new lines. - /// - /// The lines to process. - protected override void ProcessNewLines(IEnumerable lines) + /// + [MemberNotNull(nameof(regex))] + public override bool AddOutput(string line) { - Regex regex = GetPropRegex(); + regex ??= GetPropRegex(); // We receive an array of lines. We're expecting // to have the build info in the first line, and the build // date in the 2nd line. There seems to be an empty line // after all that. - foreach (string line in lines) + if (string.IsNullOrEmpty(line) || line.StartsWith('#') || line.StartsWith('$')) { - if (string.IsNullOrEmpty(line) || line.StartsWith('#') || line.StartsWith('$')) - { - continue; - } - Match m = regex.Match(line); - if (m.Success) - { - string label = m.Groups[1].Value.Trim(); - string value = m.Groups[2].Value.Trim(); + return true; + } + + Match m = regex.Match(line); + if (m.Success) + { + string label = m.Groups[1].Value.Trim(); + string value = m.Groups[2].Value.Trim(); - if (label.Length > 0) - { - Properties[label] = value; - } + if (label.Length > 0) + { + Properties[label] = value; } } + return true; } + /// + protected override void Done() => regex = null; + #if NET7_0_OR_GREATER [GeneratedRegex(GetPropPattern)] private static partial Regex GetPropRegex(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index f99485af..c8d7e346 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -11,7 +11,7 @@ namespace AdvancedSharpAdbClient.Receivers.DeviceCommands /// /// Processes command line output of a adb shell command. /// - public class InfoOutputReceiver : MultiLineReceiver + public class InfoOutputReceiver : ShellOutputReceiver { /// /// Initializes a new instance of the class. @@ -56,28 +56,23 @@ public InfoOutputReceiver() { } /// Function parsing one string and returning the property value if possible. public void AddPropertyParser(string property, Func parser) => PropertyParsers[property] = parser; - /// - /// Processes the new lines, and sets version information if the line represents package information data. - /// - /// The lines to process. - protected override void ProcessNewLines(IEnumerable lines) + /// + public override bool AddOutput(string line) { - foreach (string line in lines) + if (line == null) { - if (line == null) - { - continue; - } + return true; + } - foreach (KeyValuePair> parser in PropertyParsers) + foreach (KeyValuePair> parser in PropertyParsers) + { + object? propertyValue = parser.Value(line); + if (propertyValue != null) { - object? propertyValue = parser.Value(line); - if (propertyValue != null) - { - Properties[parser.Key] = propertyValue; - } + Properties[parser.Key] = propertyValue; } } + return true; } } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 29e91c98..71d25ae7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -2,7 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System.Collections.Generic; using System.Text.RegularExpressions; namespace AdvancedSharpAdbClient.Receivers.DeviceCommands @@ -10,7 +9,7 @@ namespace AdvancedSharpAdbClient.Receivers.DeviceCommands /// /// Processes output of the pm install command. /// - public partial class InstallOutputReceiver : MultiLineReceiver + public partial class InstallOutputReceiver : ShellOutputReceiver { /// /// The error message that indicates an unknown error occurred. @@ -65,67 +64,64 @@ public InstallOutputReceiver() { } /// public bool Success { get; private set; } - /// - /// Processes the new lines. - /// - /// The lines. - protected override void ProcessNewLines(IEnumerable lines) + /// + public override bool AddOutput(string line) { - Regex successRegex = SuccessRegex(); - Regex failureRegex = FailureRegex(); - Regex errorRegex = ErrorRegex(); - - foreach (string line in lines) + if (line.Length > 0) { - if (line.Length > 0) + if (line.StartsWith(SuccessOutput)) { - if (line.StartsWith(SuccessOutput)) - { - Match m = successRegex.Match(line); - SuccessMessage = string.Empty; + Regex successRegex = SuccessRegex(); + Match m = successRegex.Match(line); + SuccessMessage = string.Empty; - ErrorMessage = null; + ErrorMessage = null; - if (m.Success) - { - string msg = m.Groups[1].Value; - SuccessMessage = msg ?? string.Empty; - } - - Success = true; - } - else if (line.StartsWith(FailureOutput)) + if (m.Success) { - Match m = failureRegex.Match(line); - ErrorMessage = UnknownError; + string msg = m.Groups[1].Value; + SuccessMessage = msg ?? string.Empty; + } - SuccessMessage = null; + Success = true; + return false; + } + else if (line.StartsWith(FailureOutput)) + { + Regex failureRegex = FailureRegex(); + Match m = failureRegex.Match(line); + ErrorMessage = UnknownError; - if (m.Success) - { - string msg = m.Groups[1].Value; - ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; - } + SuccessMessage = null; - Success = false; - } - else + if (m.Success) { - Match m = errorRegex.Match(line); - ErrorMessage = UnknownError; + string msg = m.Groups[1].Value; + ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; + } - SuccessMessage = null; + Success = false; + return false; + } + else + { + Regex errorRegex = ErrorRegex(); + Match m = errorRegex.Match(line); + ErrorMessage = UnknownError; - if (m.Success) - { - string msg = m.Groups[1].Value; - ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; - } + SuccessMessage = null; - Success = false; + if (m.Success) + { + string msg = m.Groups[1].Value; + ErrorMessage = StringExtensions.IsNullOrWhiteSpace(msg) ? UnknownError : msg; } + + Success = false; + return false; } } + return true; } #if NET7_0_OR_GREATER diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 9ea82c3b..44c8f8a7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -22,10 +22,7 @@ public class PackageManagerReceiver(PackageManager packageManager) : MultiLineRe /// public PackageManager PackageManager { get; } = packageManager; - /// - /// Processes the new lines. - /// - /// The lines. + /// protected override void ProcessNewLines(IEnumerable lines) { PackageManager.Packages.Clear(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index eb459f7f..7f51dc66 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -9,7 +9,7 @@ namespace AdvancedSharpAdbClient.Receivers.DeviceCommands /// /// Parses the output of a cat /proc/[pid]/stat command. /// - public class ProcessOutputReceiver : MultiLineReceiver + public class ProcessOutputReceiver : ShellOutputReceiver { /// /// Initializes a new instance of the class. @@ -22,25 +22,23 @@ public ProcessOutputReceiver() { } public List Processes { get; } = []; /// - protected override void ProcessNewLines(IEnumerable lines) + public override bool AddOutput(string line) { - foreach (string line in lines) + // Process has already died (e.g. the cat process itself) + if (line.Contains("No such file or directory")) { - // Process has already died (e.g. the cat process itself) - if (line.Contains("No such file or directory")) - { - continue; - } + return false; + } - try - { - Processes.Add(new AndroidProcess(line, cmdLinePrefix: true)); - } - catch - { - // Swallow - } + try + { + Processes.Add(new AndroidProcess(line, cmdLinePrefix: true)); + } + catch + { + // Swallow } + return true; } } } diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 8d857f41..04abfc35 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -37,13 +37,10 @@ public partial class ConsoleOutputReceiver(ILogger? logge /// A that represents the current . public override string ToString() => output.ToString(); - /// - /// Throws an error message if the console output line contains an error message. - /// - /// The line to inspect. - public virtual void ThrowOnError(string line) + /// + protected override void ThrowOnError(string line) { - if (!ParsesErrors) + if (ParsesErrors && !line.StartsWith('#') && !line.StartsWith('$')) { if (line.EndsWith(": not found")) { diff --git a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs index bd0f16a4..c6b1bca3 100644 --- a/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/IShellOutputReceiver.cs @@ -14,17 +14,11 @@ namespace AdvancedSharpAdbClient.Receivers public interface IShellOutputReceiver { /// - /// Gets a value indicating whether the receiver parses error messages. + /// Adds a line to the output. /// - /// if this receiver parsers error messages; otherwise . - /// The default value is . If set to , the - /// will detect common error messages and throw an exception. - bool ParsesErrors { get; } - - /// - /// Adds the output. - /// - void AddOutput(string line); + /// The line to add to the output. + /// if continue receive messages; otherwise . + bool AddOutput(string line); /// /// Flushes the output. diff --git a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs index f98c2765..8cd88263 100644 --- a/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/MultilineReceiver.cs @@ -9,43 +9,43 @@ namespace AdvancedSharpAdbClient.Receivers /// /// A multiline receiver to receive and process shell output with multi lines. /// - public abstract class MultiLineReceiver : IShellOutputReceiver + public abstract class MultiLineReceiver : ShellOutputReceiver { /// /// Initializes a new instance of the class. /// - public MultiLineReceiver() => Lines = new List(); + public MultiLineReceiver() { } /// /// Gets or sets a value indicating whether [trim lines]. /// /// if [trim lines]; otherwise, . - public bool TrimLines { get; set; } + public bool TrimLines { get; set; } = false; /// /// Gets or sets a value indicating whether the receiver parses error messages. /// /// if this receiver parsers error messages; otherwise . - /// The default value is . If set to , the + /// The default value is . If set to , the /// will detect common error messages and throw an exception. - public virtual bool ParsesErrors { get; protected set; } + public virtual bool ParsesErrors { get; init; } = true; /// /// Gets or sets the lines. /// /// The lines. - protected ICollection Lines { get; set; } + protected List Lines { get; set; } = []; - /// - /// Adds a line to the output. - /// - /// The line to add to the output. - public virtual void AddOutput(string line) => Lines.Add(line); + /// + public override bool AddOutput(string line) + { + ThrowOnError(line); + Lines.Add(TrimLines ? line.Trim() : line); + return true; + } - /// - /// Flushes the output. - /// - public virtual void Flush() + /// + public override void Flush() { if (Lines.Count > 0) { @@ -53,14 +53,14 @@ public virtual void Flush() ProcessNewLines(Lines); Lines.Clear(); } - Done(); } /// - /// Finishes the receiver. + /// Throws an error message if the console output line contains an error message. /// - protected virtual void Done() + /// The line to inspect. + protected virtual void ThrowOnError(string line) { // Do nothing } diff --git a/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs new file mode 100644 index 00000000..e09e7456 --- /dev/null +++ b/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System.Collections.Generic; + +namespace AdvancedSharpAdbClient.Receivers +{ + /// + /// A multiline receiver to receive and process shell output with multi lines. + /// + public abstract class ShellOutputReceiver : IShellOutputReceiver + { + /// + /// Initializes a new instance of the class. + /// + public ShellOutputReceiver() { } + + /// + public abstract bool AddOutput(string line); + + /// + public virtual void Flush() => Done(); + + /// + /// Finishes the receiver. + /// + protected virtual void Done() + { + // Do nothing + } + } +} From 8b4baae89b81d5e1f1999028effbc0123d992a18 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 8 Dec 2023 22:53:33 +0800 Subject: [PATCH 02/12] Move shell command method to DeviceClient --- .../AdbClientTests.Async.cs | 680 +----------------- .../AdbClientTests.cs | 646 +---------------- .../DeviceCommands/DeviceClientTexts.Async.cs | 454 ++++++++++++ .../DeviceCommands/DeviceClientTexts.cs | 442 ++++++++++++ .../DeviceExtensionsTests.Async.cs | 47 ++ .../DeviceCommands/DeviceExtensionsTests.cs | 76 +- .../Models/AndroidProcessTests.cs | 2 +- .../DeviceCommands/Models/VersionInfoTests.cs | 2 +- .../EnvironmentVariablesReceiverTests.cs | 2 +- .../Receivers/GetPropReceiverTests.cs | 2 +- .../Receivers/InstallOutputReceiverTests.cs | 2 +- .../Receivers/PackageManagerReceiverTests.cs | 2 +- .../Receivers/ProcessOutputReceiverTests.cs | 2 +- .../Receivers/VersionInfoReceiverTests.cs | 2 +- .../Dummys/DummyAdbClient.cs | 70 -- .../Models/FramebufferTests.cs | 4 + .../Properties/GlobalUsings.cs | 5 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 418 ----------- AdvancedSharpAdbClient/AdbClient.cs | 358 --------- .../DeviceCommands/DeviceClient.Async.cs | 453 ++++++++++++ .../DeviceCommands/DeviceClient.cs | 402 +++++++++++ .../DeviceCommands/DeviceExtensions.Async.cs | 33 + .../DeviceCommands/DeviceExtensions.cs | 27 + .../DeviceCommands/Models/AndroidProcess.cs | 2 +- .../{ => DeviceCommands}/Models/Element.cs | 75 +- .../Models/Enums/AndroidProcessState.cs | 2 +- .../Enums/PackageInstallProgressState.cs | 2 +- .../Models/Enums/PerProcessFlags.cs | 2 +- .../Models/InstallProgressEventArgs.cs | 2 +- .../DeviceCommands/Models/NamespaceDoc.cs | 6 +- .../DeviceCommands/Models/VersionInfo.cs | 2 +- .../DeviceCommands/PackageManager.cs | 2 +- .../Receivers/EnvironmentVariablesReceiver.cs | 2 +- .../Receivers/GetPropReceiver.cs | 2 +- .../Receivers/InfoOutputReceiver.cs | 2 +- .../Receivers/InstallOutputReceiver.cs | 2 +- .../DeviceCommands/Receivers/NamespaceDoc.cs | 6 +- .../Receivers/PackageManagerReceiver.cs | 2 +- .../Receivers/ProcessOutputReceiver.cs | 2 +- .../Receivers/VersionInfoReceiver.cs | 2 +- .../Extensions/AdbClientExtensions.Async.cs | 32 - .../Extensions/AdbClientExtensions.cs | 27 - .../Interfaces/IAdbClient.Async.cs | 181 ----- .../Interfaces/IAdbClient.cs | 141 ---- .../Properties/GlobalUsings.cs | 5 +- .../Receivers/ConsoleOutputReceiver.cs | 2 +- .../Receivers/ShellOutputReceiver.cs | 2 - 47 files changed, 2026 insertions(+), 2610 deletions(-) create mode 100644 AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs create mode 100644 AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs create mode 100644 AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs create mode 100644 AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs rename AdvancedSharpAdbClient/{ => DeviceCommands}/Models/Element.cs (83%) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index ef87d743..a991b284 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -6,10 +6,10 @@ using System.IO; using System.Linq; using System.Net; +using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -326,6 +326,9 @@ public async void ExecuteRemoteCommandAsyncUnresponsiveTest() /// Tests the method. /// [Fact] +#if WINDOWS + [SupportedOSPlatform("windows")] +#endif public async void GetFrameBufferAsyncTest() { string[] requests = @@ -827,681 +830,6 @@ public async void GetFeatureSetAsyncTest() Assert.Equal("stat_v2", features.LastOrDefault()); } - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenStringAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - string xml = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreenStringAsync(Device)); - - Assert.Equal(cleanDump, xml); - } - - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenStringAsyncMIUITest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string miuiDump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); - string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); - byte[] miuiStreamData = Encoding.UTF8.GetBytes(miuiDump); - await using MemoryStream miuiStream = new(miuiStreamData); - - string miuiXml = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [miuiStream], - () => TestClient.DumpScreenStringAsync(Device)); - - Assert.Equal(cleanMIUIDump, miuiXml); - } - - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenStringAsyncEmptyTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - await using MemoryStream emptyStream = new(); - - string emptyXml = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [emptyStream], - () => TestClient.DumpScreenStringAsync(Device)); - - Assert.True(string.IsNullOrEmpty(emptyXml)); - } - - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenStringAsyncErrorTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string errorXml = File.ReadAllText(@"Assets/dumpscreen_error.txt"); - byte[] errorStreamData = Encoding.UTF8.GetBytes(errorXml); - await using MemoryStream errorStream = new(errorStreamData); - - await Assert.ThrowsAsync(() => - RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [errorStream], - () => TestClient.DumpScreenStringAsync(Device))); - } - - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - XmlDocument xml = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreenAsync(Device)); - - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - XmlDocument doc = new(); - doc.LoadXml(cleanDump); - - Assert.Equal(doc, xml); - } - -#if WINDOWS10_0_17763_0_OR_GREATER - /// - /// Tests the method. - /// - [Fact] - public async void DumpScreenWinRTAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - Windows.Data.Xml.Dom.XmlDocument xml = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreenWinRTAsync(Device)); - - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - Windows.Data.Xml.Dom.XmlDocument doc = new(); - doc.LoadXml(cleanDump); - - Assert.Equal(doc.InnerText, xml.InnerText); - } - -#endif - - /// - /// Tests the method. - /// - [Fact] - public async void ClickAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input tap 100 100" - ]; - - byte[] streamData = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission - at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) - at android.os.Parcel.createException(Parcel.java:2357) - at android.os.Parcel.readException(Parcel.java:2340) - at android.os.Parcel.readException(Parcel.java:2282) - at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) - at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) - at com.android.commands.input.Input.injectMotionEvent(Input.java:397) - at com.android.commands.input.Input.access$200(Input.java:41) - at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) - at com.android.commands.input.Input$InputTap.run(Input.java:217) - at com.android.commands.input.Input.onRun(Input.java:107) - at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) - at com.android.commands.input.Input.main(Input.java:71) - at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) - at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) -Caused by: android.os.RemoteException: Remote stack trace: - at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) - at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) - at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) - at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)"u8.ToArray(); - await using MemoryStream shellStream = new(streamData); - - JavaException exception = await Assert.ThrowsAsync(() => - RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickAsync(Device, 100, 100))); - - Assert.Equal("SecurityException", exception.JavaName); - Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); - Assert.Equal(@" at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) - at android.os.Parcel.createException(Parcel.java:2357) - at android.os.Parcel.readException(Parcel.java:2340) - at android.os.Parcel.readException(Parcel.java:2282) - at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) - at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) - at com.android.commands.input.Input.injectMotionEvent(Input.java:397) - at com.android.commands.input.Input.access$200(Input.java:41) - at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) - at com.android.commands.input.Input$InputTap.run(Input.java:217) - at com.android.commands.input.Input.onRun(Input.java:107) - at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) - at com.android.commands.input.Input.main(Input.java:71) - at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) - at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) -Caused by: android.os.RemoteException: Remote stack trace: - at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) - at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) - at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) - at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); - } - - /// - /// Tests the method. - /// - [Fact] - public async void ClickCordsAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input tap 100 100" - ]; - - byte[] streamData = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"u8.ToArray(); - await using MemoryStream shellStream = new(streamData); - - _ = await Assert.ThrowsAsync(() => - RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickAsync(Device, new Point(100, 100)))); - } - - /// - /// Tests the method. - /// - [Fact] - public async void SwipeAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input swipe 100 200 300 400 500" - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SwipeAsync(Device, 100, 200, 300, 400, 500)); - } - - /// - /// Tests the method. - /// - [Fact] - public async void SwipeAsyncElementTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input swipe 100 200 300 400 500" - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SwipeAsync(Device, new Element(TestClient, Device, new Rectangle(0, 0, 200, 400)), new Element(TestClient, Device, new Rectangle(0, 0, 600, 800)), 500)); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("21216 27761\r\n", true)] - [InlineData(" 21216 27761\r\n", true)] - [InlineData(" \r\n", false)] - [InlineData("\r\n", false)] - [InlineData(" ", false)] - [InlineData("", false)] - public async void IsAppRunningAsyncTest(string response, bool expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:pidof com.google.android.gms" - ]; - - byte[] streamData = Encoding.UTF8.GetBytes(response); - await using MemoryStream shellStream = new(streamData); - - bool result = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.IsAppRunningAsync(Device, "com.google.android.gms")); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("app.lawnchair", true)] - [InlineData("com.android.settings", true)] - [InlineData("com.google.android.gms", false)] - public async void IsAppInForegroundAsyncTest(string packageName, bool expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity" - ]; - - byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - await using MemoryStream shellStream = new(streamData); - - bool result = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.IsAppInForegroundAsync(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] - [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] - public async void GetAppStatusAsyncTest(string packageName, string response, AppStatus expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity", - "host:transport:169.254.109.177:5555", - $"shell:pidof {packageName}" - ]; - - byte[] activityData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - await using MemoryStream activityStream = new(activityData); - byte[] pidData = Encoding.UTF8.GetBytes(response); - await using MemoryStream pidStream = new(pidData); - - AppStatus result = await RunTestAsync( - OkResponses(4), - NoResponseMessages, - requests, - [activityStream, pidStream], - () => TestClient.GetAppStatusAsync(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("app.lawnchair", AppStatus.Foreground)] - [InlineData("com.android.settings", AppStatus.Foreground)] - public async void GetAppStatusAsyncForegroundTest(string packageName, AppStatus expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity" - ]; - - byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - await using MemoryStream shellStream = new(streamData); - - AppStatus result = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.GetAppStatusAsync(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Fact] - public async void FindElementAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - Element element = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.FindElementAsync(Device)); - - Assert.Equal(144, element.GetChildCount()); - Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", child.Text); - Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); - Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); - Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); - } - - /// - /// Tests the method. - /// - [Fact] - public async void FindElementsAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - Element[] elements = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - async () => await TestClient.FindElementsAsync(Device).ToArrayAsync()); - - int childCount = elements.Length; - Array.ForEach(elements, x => childCount += x.GetChildCount()); - Assert.Equal(145, childCount); - Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); - } - - /// - /// Tests the method. - /// - [Fact] - public async void FindAsyncElementsTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - await using MemoryStream shellStream = new(streamData); - - List elements = await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - async () => - { - List elements = []; - await foreach (Element element in TestClient.FindAsyncElements(Device)) - { - elements.Add(element); - } - return elements; - }); - - int childCount = elements.Count; - elements.ForEach(x => childCount += x.GetChildCount()); - Assert.Equal(145, childCount); - Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Attributes["text"]); - Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); - } - - /// - /// Tests the method. - /// - [Fact] - public async void SendKeyEventAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_MOVE_END" - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SendKeyEventAsync(Device, "KEYCODE_MOVE_END")); - } - - /// - /// Tests the method. - /// - [Fact] - public async void SendTextAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input text Hello, World", - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SendTextAsync(Device, "Hello, World")); - } - - /// - /// Tests the method. - /// - [Fact] - public async void ClearInputAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_MOVE_END", - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL" - ]; - - await using MemoryStream firstShellStream = new(); - await using MemoryStream secondShellStream = new(); - - await RunTestAsync( - OkResponses(4), - NoResponseMessages, - requests, - [firstShellStream, secondShellStream], - () => TestClient.ClearInputAsync(Device, 3)); - } - - /// - /// Tests the method. - /// - [Fact] - public async void StartAppAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:monkey -p com.android.settings 1", - ]; - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - () => TestClient.StartAppAsync(Device, "com.android.settings")); - } - - /// - /// Tests the method. - /// - [Fact] - public async void StopAppAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:am force-stop com.android.settings", - ]; - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - () => TestClient.StopAppAsync(Device, "com.android.settings")); - } - - /// - /// Tests the method. - /// - [Fact] - public async void ClickBackButtonAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_BACK" - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickBackButtonAsync(Device)); - } - - /// - /// Tests the method. - /// - [Fact] - public async void ClickHomeButtonAsyncTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_HOME" - ]; - - await using MemoryStream shellStream = new(); - - await RunTestAsync( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickHomeButtonAsync(Device)); - } - private Task RunConnectAsyncTest(Func test, string connectString) { string[] requests = [$"host:connect:{connectString}"]; diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index bff1ee48..200cabee 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -6,8 +6,8 @@ using System.IO; using System.Linq; using System.Net; +using System.Runtime.Versioning; using System.Text; -using System.Xml; using Xunit; namespace AdvancedSharpAdbClient.Tests @@ -437,6 +437,9 @@ public void CreateRefreshableFramebufferTest() } [Fact] +#if WINDOWS + [SupportedOSPlatform("windows")] +#endif public void GetFrameBufferTest() { string[] requests = @@ -935,647 +938,6 @@ public void GetFeatureSetTest() Assert.Equal("stat_v2", features.LastOrDefault()); } - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenStringTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - using MemoryStream shellStream = new(streamData); - - string xml = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreenString(Device)); - - Assert.Equal(cleanDump, xml); - } - - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenStringMIUITest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string miuidump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); - string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); - byte[] miuiStreamData = Encoding.UTF8.GetBytes(miuidump); - using MemoryStream miuiStream = new(miuiStreamData); - - string miuiXml = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [miuiStream], - () => TestClient.DumpScreenString(Device)); - - Assert.Equal(cleanMIUIDump, miuiXml); - } - - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenStringEmptyTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - byte[] emptyStreamData = Encoding.UTF8.GetBytes(string.Empty); - using MemoryStream emptyStream = new(emptyStreamData); - - string emptyXml = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [emptyStream], - () => TestClient.DumpScreenString(Device)); - - Assert.True(string.IsNullOrEmpty(emptyXml)); - } - - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenStringErrorTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string errorXml = File.ReadAllText(@"Assets/dumpscreen_error.txt"); - byte[] errorStreamData = Encoding.UTF8.GetBytes(errorXml); - using MemoryStream errorStream = new(errorStreamData); - - Assert.Throws(() => - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [errorStream], - () => TestClient.DumpScreenString(Device))); - } - - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - using MemoryStream shellStream = new(streamData); - - XmlDocument xml = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreen(Device)); - - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - XmlDocument doc = new(); - doc.LoadXml(cleanDump); - - Assert.Equal(doc, xml); - } - -#if WINDOWS10_0_17763_0_OR_GREATER - /// - /// Tests the method. - /// - [Fact] - public void DumpScreenWinRTTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - using MemoryStream shellStream = new(streamData); - - Windows.Data.Xml.Dom.XmlDocument xml = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.DumpScreenWinRT(Device)); - - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); - Windows.Data.Xml.Dom.XmlDocument doc = new(); - doc.LoadXml(cleanDump); - - Assert.Equal(doc.InnerText, xml.InnerText); - } - -#endif - - /// - /// Tests the method. - /// - [Fact] - public void ClickTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input tap 100 100" - ]; - - byte[] streamData = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission - at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) - at android.os.Parcel.createException(Parcel.java:2357) - at android.os.Parcel.readException(Parcel.java:2340) - at android.os.Parcel.readException(Parcel.java:2282) - at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) - at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) - at com.android.commands.input.Input.injectMotionEvent(Input.java:397) - at com.android.commands.input.Input.access$200(Input.java:41) - at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) - at com.android.commands.input.Input$InputTap.run(Input.java:217) - at com.android.commands.input.Input.onRun(Input.java:107) - at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) - at com.android.commands.input.Input.main(Input.java:71) - at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) - at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) -Caused by: android.os.RemoteException: Remote stack trace: - at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) - at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) - at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) - at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)"u8.ToArray(); - using MemoryStream shellStream = new(streamData); - - JavaException exception = Assert.Throws(() => - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.Click(Device, 100, 100))); - - Assert.Equal("SecurityException", exception.JavaName); - Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); - Assert.Equal(@" at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) - at android.os.Parcel.createException(Parcel.java:2357) - at android.os.Parcel.readException(Parcel.java:2340) - at android.os.Parcel.readException(Parcel.java:2282) - at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) - at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) - at com.android.commands.input.Input.injectMotionEvent(Input.java:397) - at com.android.commands.input.Input.access$200(Input.java:41) - at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) - at com.android.commands.input.Input$InputTap.run(Input.java:217) - at com.android.commands.input.Input.onRun(Input.java:107) - at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) - at com.android.commands.input.Input.main(Input.java:71) - at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) - at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) -Caused by: android.os.RemoteException: Remote stack trace: - at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) - at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) - at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) - at android.os.Binder.execTransactInternal(Binder.java:1165) - at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); - } - - /// - /// Tests the method. - /// - [Fact] - public void ClickCordsTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input tap 100 100" - ]; - - byte[] streamData = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"u8.ToArray(); - using MemoryStream shellStream = new(streamData); - - _ = Assert.Throws(() => - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.Click(Device, new Point(100, 100)))); - } - - /// - /// Tests the method. - /// - [Fact] - public void SwipeTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input swipe 100 200 300 400 500" - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.Swipe(Device, 100, 200, 300, 400, 500)); - } - - /// - /// Tests the method. - /// - [Fact] - public void SwipeElementTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input swipe 100 200 300 400 500" - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.Swipe(Device, new Element(TestClient, Device, new Rectangle(0, 0, 200, 400)), new Element(TestClient, Device, new Rectangle(0, 0, 600, 800)), 500)); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("21216 27761\r\n", true)] - [InlineData(" 21216 27761\r\n", true)] - [InlineData("12836\r\n", true)] - [InlineData(" \r\n", false)] - [InlineData("\r\n", false)] - [InlineData(" ", false)] - [InlineData("", false)] - public void IsAppRunningTest(string response, bool expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:pidof com.google.android.gms" - ]; - - byte[] streamData = Encoding.UTF8.GetBytes(response); - using MemoryStream shellStream = new(streamData); - - bool result = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.IsAppRunning(Device, "com.google.android.gms")); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("app.lawnchair", true)] - [InlineData("com.android.settings", true)] - [InlineData("com.google.android.gms", false)] - public void IsAppInForegroundTest(string packageName, bool expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity" - ]; - - byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - using MemoryStream shellStream = new(streamData); - - bool result = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.IsAppInForeground(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] - [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] - public void GetAppStatusTest(string packageName, string response, AppStatus expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity", - "host:transport:169.254.109.177:5555", - $"shell:pidof {packageName}" - ]; - - byte[] activityData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - using MemoryStream activityStream = new(activityData); - byte[] pidData = Encoding.UTF8.GetBytes(response); - using MemoryStream pidStream = new(pidData); - - AppStatus result = RunTest( - OkResponses(4), - NoResponseMessages, - requests, - [activityStream, pidStream], - () => TestClient.GetAppStatus(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Theory] - [InlineData("app.lawnchair", AppStatus.Foreground)] - [InlineData("com.android.settings", AppStatus.Foreground)] - public void GetAppStatusForegroundTest(string packageName, AppStatus expected) - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:dumpsys activity activities | grep mResumedActivity" - ]; - - byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} - mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray(); - using MemoryStream shellStream = new(streamData); - - AppStatus result = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.GetAppStatus(Device, packageName)); - - Assert.Equal(expected, result); - } - - /// - /// Tests the method. - /// - [Fact] - public void FindElementTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - using MemoryStream shellStream = new(streamData); - - Element element = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.FindElement(Device)); - - Assert.Equal(144, element.GetChildCount()); - Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", child.Text); - Assert.Equal("android.widget.TextView", child.Class); - Assert.Equal("com.bilibili.app.in", child.Package); - Assert.Equal("com.bilibili.app.in:id/header_info_name", child.ResourceID); - Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); - Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); - Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); - } - - /// - /// Tests the method. - /// - [Fact] - public void FindElementsTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:uiautomator dump /dev/tty" - ]; - - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - byte[] streamData = Encoding.UTF8.GetBytes(dump); - using MemoryStream shellStream = new(streamData); - - Element[] elements = RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.FindElements(Device).ToArray()); - - int childCount = elements.Length; - Array.ForEach(elements, x => childCount += x.GetChildCount()); - Assert.Equal(145, childCount); - Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; - Assert.Equal("where-where", element.Text); - Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); - } - - /// - /// Tests the method. - /// - [Fact] - public void SendKeyEventTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_MOVE_END" - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SendKeyEvent(Device, "KEYCODE_MOVE_END")); - } - - /// - /// Tests the method. - /// - [Fact] - public void SendTextTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input text Hello, World", - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.SendText(Device, "Hello, World")); - } - - /// - /// Tests the method. - /// - [Fact] - public void ClearInputTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_MOVE_END", - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL" - ]; - - using MemoryStream firstShellStream = new(); - using MemoryStream secondShellStream = new(); - - RunTest( - OkResponses(4), - NoResponseMessages, - requests, - [firstShellStream, secondShellStream], - () => TestClient.ClearInput(Device, 3)); - } - - /// - /// Tests the method. - /// - [Fact] - public void StartAppTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:monkey -p com.android.settings 1", - ]; - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - () => TestClient.StartApp(Device, "com.android.settings")); - } - - /// - /// Tests the method. - /// - [Fact] - public void StopAppTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:am force-stop com.android.settings", - ]; - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - () => TestClient.StopApp(Device, "com.android.settings")); - } - - /// - /// Tests the method. - /// - [Fact] - public void ClickBackButtonTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_BACK" - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickBackButton(Device)); - } - - /// - /// Tests the method. - /// - [Fact] - public void ClickHomeButtonTest() - { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "shell:input keyevent KEYCODE_HOME" - ]; - - using MemoryStream shellStream = new(); - - RunTest( - OkResponses(2), - NoResponseMessages, - requests, - [shellStream], - () => TestClient.ClickHomeButton(Device)); - } - private void RunConnectTest(Action test, string connectString) { string[] requests = [$"host:connect:{connectString}"]; diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs new file mode 100644 index 00000000..2516cc7b --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs @@ -0,0 +1,454 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading; +using System.Xml; +using Xunit; + +namespace AdvancedSharpAdbClient.DeviceCommands.Tests +{ + public partial class DeviceExtensionsTests + { + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenStringAsyncTest() + { + string dump = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); + string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = dump; + + string xml = await new DeviceClient(client, Device).DumpScreenStringAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(cleanDump, xml); + } + + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenStringMIUIAsyncTest() + { + string miuidump = await File.ReadAllTextAsync(@"Assets/dumpscreen_miui.txt"); + string cleanMIUIDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_miui_clean.txt"); + + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = miuidump; + + string miuiXml = await new DeviceClient(client, Device).DumpScreenStringAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(cleanMIUIDump, miuiXml); + } + + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenStringEmptyAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = string.Empty; + + string emptyXml = await new DeviceClient(client, Device).DumpScreenStringAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.True(string.IsNullOrEmpty(emptyXml)); + } + + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenStringErrorAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen_error.txt"); + + _ = await Assert.ThrowsAsync(() => new DeviceClient(client, Device).DumpScreenStringAsync()); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); + + XmlDocument xml = await new DeviceClient(client, Device).DumpScreenAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc, xml); + } + +#if WINDOWS10_0_17763_0_OR_GREATER + /// + /// Tests the method. + /// + [Fact] + public async void DumpScreenWinRTAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); + + Windows.Data.Xml.Dom.XmlDocument xml = await new DeviceClient(client, Device).DumpScreenWinRTAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + Windows.Data.Xml.Dom.XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc.InnerText, xml.InnerText); + } + +#endif + + /// + /// Tests the method. + /// + [Fact] + public async void ClickAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input tap 100 100"] = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission + at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) + at android.os.Parcel.createException(Parcel.java:2357) + at android.os.Parcel.readException(Parcel.java:2340) + at android.os.Parcel.readException(Parcel.java:2282) + at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) + at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) + at com.android.commands.input.Input.injectMotionEvent(Input.java:397) + at com.android.commands.input.Input.access$200(Input.java:41) + at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) + at com.android.commands.input.Input$InputTap.run(Input.java:217) + at com.android.commands.input.Input.onRun(Input.java:107) + at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) + at com.android.commands.input.Input.main(Input.java:71) + at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) +Caused by: android.os.RemoteException: Remote stack trace: + at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) + at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) + at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) + at android.os.Binder.execTransactInternal(Binder.java:1165) + at android.os.Binder.execTransact(Binder.java:1134)"; + + JavaException exception = await Assert.ThrowsAsync(() => new DeviceClient(client, Device).ClickAsync(100, 100)); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input tap 100 100", client.ReceivedCommands[0]); + + Assert.Equal("SecurityException", exception.JavaName); + Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); + Assert.Equal(@" at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) + at android.os.Parcel.createException(Parcel.java:2357) + at android.os.Parcel.readException(Parcel.java:2340) + at android.os.Parcel.readException(Parcel.java:2282) + at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) + at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) + at com.android.commands.input.Input.injectMotionEvent(Input.java:397) + at com.android.commands.input.Input.access$200(Input.java:41) + at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) + at com.android.commands.input.Input$InputTap.run(Input.java:217) + at com.android.commands.input.Input.onRun(Input.java:107) + at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) + at com.android.commands.input.Input.main(Input.java:71) + at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) +Caused by: android.os.RemoteException: Remote stack trace: + at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) + at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) + at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) + at android.os.Binder.execTransactInternal(Binder.java:1165) + at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); + } + + /// + /// Tests the method. + /// + [Fact] + public async void ClickCordsAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input tap 100 100"] = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"; + + _ = await Assert.ThrowsAsync(() => new DeviceClient(client, Device).ClickAsync(new Point(100, 100))); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input tap 100 100", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SwipeAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + await new DeviceClient(client, Device).SwipeAsync(100, 200, 300, 400, 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SwipeElementAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + await new DeviceClient(client, Device).SwipeAsync(new Element(client, Device, new Rectangle(0, 0, 200, 400)), new Element(client, Device, new Rectangle(0, 0, 600, 800)), 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("21216 27761\r\n", true)] + [InlineData(" 21216 27761\r\n", true)] + [InlineData("12836\r\n", true)] + [InlineData(" \r\n", false)] + [InlineData("\r\n", false)] + [InlineData(" ", false)] + [InlineData("", false)] + public async void IsAppRunningAsyncTest(string response, bool expected) + { + DummyAdbClient client = new(); + client.Commands["shell:pidof com.google.android.gms"] = response; + + bool result = await new DeviceClient(client, Device).IsAppRunningAsync("com.google.android.gms"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:pidof com.google.android.gms", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", true)] + [InlineData("com.android.settings", true)] + [InlineData("com.google.android.gms", false)] + public async void IsAppInForegroundAsyncTest(string packageName, bool expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + + bool result = await new DeviceClient(client, Device).IsAppInForegroundAsync(packageName); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] + [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] + public async void GetAppStatusAsyncTest(string packageName, string response, AppStatus expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + client.Commands[$"shell:pidof {packageName}"] = response; + + AppStatus result = await new DeviceClient(client, Device).GetAppStatusAsync(packageName); + + Assert.Equal(2, client.ReceivedCommands.Count); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + Assert.Equal($"shell:pidof {packageName}", client.ReceivedCommands[1]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", AppStatus.Foreground)] + [InlineData("com.android.settings", AppStatus.Foreground)] + public async void GetAppStatusForegroundAsyncTest(string packageName, AppStatus expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + + AppStatus result = await new DeviceClient(client, Device).GetAppStatusAsync(packageName); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Fact] + public async void FindElementAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + Element element = await new DeviceClient(client, Device).FindElementAsync(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(144, element.GetChildCount()); + Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", child.Text); + Assert.Equal("android.widget.TextView", child.Class); + Assert.Equal("com.bilibili.app.in", child.Package); + Assert.Equal("com.bilibili.app.in:id/header_info_name", child.ResourceID); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); + Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); + } + + /// + /// Tests the method. + /// + [Fact] + public async void FindElementsAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + Element[] elements = (await new DeviceClient(client, Device).FindElementsAsync()).ToArray(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + int childCount = elements.Length; + Array.ForEach(elements, x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Text); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); + } + + /// + /// Tests the method. + /// + [Fact] + public async void FindAsyncElementsTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + List elements = []; + await foreach (Element value in new DeviceClient(client, Device).FindAsyncElements()) + { + elements.Add(value); + } + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + int childCount = elements.Count; + elements.ForEach(x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Text); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SendKeyEventAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_MOVE_END"] = string.Empty; + + await new DeviceClient(client, Device).SendKeyEventAsync("KEYCODE_MOVE_END"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_MOVE_END", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void SendTextAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input text Hello, World"] = string.Empty; + + await new DeviceClient(client, Device).SendTextAsync("Hello, World"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input text Hello, World", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void StartAppAsyncTest() + { + DummyAdbClient client = new(); + + await new DeviceClient(client, Device).StartAppAsync("com.android.settings"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:monkey -p com.android.settings 1", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void StopAppAsyncTest() + { + DummyAdbClient client = new(); + + await new DeviceClient(client, Device).StopAppAsync("com.android.settings"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:am force-stop com.android.settings", client.ReceivedCommands[0]); + } + } +} diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs new file mode 100644 index 00000000..cd5e348b --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs @@ -0,0 +1,442 @@ +using NSubstitute; +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Xml; +using Xunit; + +namespace AdvancedSharpAdbClient.DeviceCommands.Tests +{ + public partial class DeviceClientTexts + { + protected static DeviceData Device { get; } = new() + { + Serial = "169.254.109.177:5555", + State = DeviceState.Online + }; + + [Fact] + public void ConstructorNullTest() + { + _ = Assert.Throws(() => new DeviceClient(null, null)); + _ = Assert.Throws(() => new DeviceClient(null, new DeviceData())); + _ = Assert.Throws(() => new DeviceClient(Substitute.For(), null)); + _ = Assert.Throws(() => new DeviceClient(Substitute.For(), new DeviceData())); + } + + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenStringTest() + { + string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); + string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = dump; + + string xml = new DeviceClient(client, Device).DumpScreenString(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(cleanDump, xml); + } + + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenStringMIUITest() + { + string miuidump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); + string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); + + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = miuidump; + + string miuiXml = new DeviceClient(client, Device).DumpScreenString(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(cleanMIUIDump, miuiXml); + } + + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenStringEmptyTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = string.Empty; + + string emptyXml = new DeviceClient(client, Device).DumpScreenString(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.True(string.IsNullOrEmpty(emptyXml)); + } + + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenStringErrorTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen_error.txt"); + + _ = Assert.Throws(() => new DeviceClient(client, Device).DumpScreenString()); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + XmlDocument xml = new DeviceClient(client, Device).DumpScreen(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc, xml); + } + +#if WINDOWS10_0_17763_0_OR_GREATER + /// + /// Tests the method. + /// + [Fact] + public void DumpScreenWinRTTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + Windows.Data.Xml.Dom.XmlDocument xml = new DeviceClient(client, Device).DumpScreenWinRT(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + Windows.Data.Xml.Dom.XmlDocument doc = new(); + doc.LoadXml(cleanDump); + + Assert.Equal(doc.InnerText, xml.InnerText); + } + +#endif + + /// + /// Tests the method. + /// + [Fact] + public void ClickTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input tap 100 100"] = @"java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission + at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) + at android.os.Parcel.createException(Parcel.java:2357) + at android.os.Parcel.readException(Parcel.java:2340) + at android.os.Parcel.readException(Parcel.java:2282) + at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) + at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) + at com.android.commands.input.Input.injectMotionEvent(Input.java:397) + at com.android.commands.input.Input.access$200(Input.java:41) + at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) + at com.android.commands.input.Input$InputTap.run(Input.java:217) + at com.android.commands.input.Input.onRun(Input.java:107) + at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) + at com.android.commands.input.Input.main(Input.java:71) + at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) +Caused by: android.os.RemoteException: Remote stack trace: + at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) + at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) + at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) + at android.os.Binder.execTransactInternal(Binder.java:1165) + at android.os.Binder.execTransact(Binder.java:1134)"; + + JavaException exception = Assert.Throws(() => new DeviceClient(client, Device).Click(100, 100)); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input tap 100 100", client.ReceivedCommands[0]); + + Assert.Equal("SecurityException", exception.JavaName); + Assert.Equal("Injecting to another application requires INJECT_EVENTS permission", exception.Message); + Assert.Equal(@" at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) + at android.os.Parcel.createException(Parcel.java:2357) + at android.os.Parcel.readException(Parcel.java:2340) + at android.os.Parcel.readException(Parcel.java:2282) + at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:946) + at android.hardware.input.InputManager.injectInputEvent(InputManager.java:907) + at com.android.commands.input.Input.injectMotionEvent(Input.java:397) + at com.android.commands.input.Input.access$200(Input.java:41) + at com.android.commands.input.Input$InputTap.sendTap(Input.java:223) + at com.android.commands.input.Input$InputTap.run(Input.java:217) + at com.android.commands.input.Input.onRun(Input.java:107) + at com.android.internal.os.BaseCommand.run(BaseCommand.java:60) + at com.android.commands.input.Input.main(Input.java:71) + at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438) +Caused by: android.os.RemoteException: Remote stack trace: + at com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:677) + at com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:651) + at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:430) + at android.os.Binder.execTransactInternal(Binder.java:1165) + at android.os.Binder.execTransact(Binder.java:1134)", exception.JavaStackTrace, ignoreLineEndingDifferences: true); + } + + /// + /// Tests the method. + /// + [Fact] + public void ClickCordsTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input tap 100 100"] = "Error: Injecting to another application requires INJECT_EVENTS permission\r\n"; + + _ = Assert.Throws(() => new DeviceClient(client, Device).Click(new Point(100, 100))); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input tap 100 100", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void SwipeTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + new DeviceClient(client, Device).Swipe(100, 200, 300, 400, 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void SwipeElementTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + new DeviceClient(client, Device).Swipe(new Element(client, Device, new Rectangle(0, 0, 200, 400)), new Element(client, Device, new Rectangle(0, 0, 600, 800)), 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("21216 27761\r\n", true)] + [InlineData(" 21216 27761\r\n", true)] + [InlineData("12836\r\n", true)] + [InlineData(" \r\n", false)] + [InlineData("\r\n", false)] + [InlineData(" ", false)] + [InlineData("", false)] + public void IsAppRunningTest(string response, bool expected) + { + DummyAdbClient client = new(); + client.Commands["shell:pidof com.google.android.gms"] = response; + + bool result = new DeviceClient(client, Device).IsAppRunning("com.google.android.gms"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:pidof com.google.android.gms", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", true)] + [InlineData("com.android.settings", true)] + [InlineData("com.google.android.gms", false)] + public void IsAppInForegroundTest(string packageName, bool expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + + bool result = new DeviceClient(client, Device).IsAppInForeground(packageName); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("com.google.android.gms", "21216 27761\r\n", AppStatus.Background)] + [InlineData("com.android.gallery3d", "\r\n", AppStatus.Stopped)] + public void GetAppStatusTest(string packageName, string response, AppStatus expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + client.Commands[$"shell:pidof {packageName}"] = response; + + AppStatus result = new DeviceClient(client, Device).GetAppStatus(packageName); + + Assert.Equal(2, client.ReceivedCommands.Count); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + Assert.Equal($"shell:pidof {packageName}", client.ReceivedCommands[1]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Theory] + [InlineData("app.lawnchair", AppStatus.Foreground)] + [InlineData("com.android.settings", AppStatus.Foreground)] + public void GetAppStatusForegroundTest(string packageName, AppStatus expected) + { + DummyAdbClient client = new(); + client.Commands["shell:dumpsys activity activities | grep mResumedActivity"] = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029} + mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"; + + AppStatus result = new DeviceClient(client, Device).GetAppStatus(packageName); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:dumpsys activity activities | grep mResumedActivity", client.ReceivedCommands[0]); + + Assert.Equal(expected, result); + } + + /// + /// Tests the method. + /// + [Fact] + public void FindElementTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + Element element = new DeviceClient(client, Device).FindElement(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + Assert.Equal(144, element.GetChildCount()); + Element child = element[0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", child.Text); + Assert.Equal("android.widget.TextView", child.Class); + Assert.Equal("com.bilibili.app.in", child.Package); + Assert.Equal("com.bilibili.app.in:id/header_info_name", child.ResourceID); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), child.Bounds); + Assert.Equal(child, element.FindDescendantOrSelf(x => x.Text == "where-where")); + Assert.Equal(2, element.FindDescendants().Where(x => x.Text == "where-where").Count()); + } + + /// + /// Tests the method. + /// + [Fact] + public void FindElementsTest() + { + DummyAdbClient client = new(); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + + Element[] elements = new DeviceClient(client, Device).FindElements().ToArray(); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); + + int childCount = elements.Length; + Array.ForEach(elements, x => childCount += x.GetChildCount()); + Assert.Equal(145, childCount); + Element element = elements[0][0][0][0][0][0][0][0][0][2][1][0][0]; + Assert.Equal("where-where", element.Text); + Assert.Equal(Rectangle.FromLTRB(45, 889, 427, 973), element.Bounds); + } + + /// + /// Tests the method. + /// + [Fact] + public void SendKeyEventTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_MOVE_END"] = string.Empty; + + new DeviceClient(client, Device).SendKeyEvent("KEYCODE_MOVE_END"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_MOVE_END", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void SendTextTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input text Hello, World"] = string.Empty; + + new DeviceClient(client, Device).SendText("Hello, World"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input text Hello, World", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void StartAppTest() + { + DummyAdbClient client = new(); + + new DeviceClient(client, Device).StartApp("com.android.settings"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:monkey -p com.android.settings 1", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void StopAppTest() + { + DummyAdbClient client = new(); + + new DeviceClient(client, Device).StopApp("com.android.settings"); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:am force-stop com.android.settings", client.ReceivedCommands[0]); + } + } +} diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index e07e8b09..cb6496bf 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -8,6 +8,53 @@ namespace AdvancedSharpAdbClient.DeviceCommands.Tests { public partial class DeviceExtensionsTests { + /// + /// Tests the method. + /// + [Fact] + public async void ClearInputAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_MOVE_END"] = string.Empty; + client.Commands["shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL"] = string.Empty; + + await client.ClearInputAsync(Device, 3); + + Assert.Equal(2, client.ReceivedCommands.Count); + Assert.Equal("shell:input keyevent KEYCODE_MOVE_END", client.ReceivedCommands[0]); + Assert.Equal("shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL", client.ReceivedCommands[1]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void ClickBackButtonAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_BACK"] = string.Empty; + + await client.ClickBackButtonAsync(Device); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_BACK", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public async void ClickHomeButtonAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_HOME"] = string.Empty; + + await client.ClickHomeButtonAsync(Device); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_HOME", client.ReceivedCommands[0]); + } + [Fact] public async void StatAsyncTest() { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index d96a0bcf..a417066f 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -10,6 +10,59 @@ namespace AdvancedSharpAdbClient.DeviceCommands.Tests /// public partial class DeviceExtensionsTests { + protected static DeviceData Device { get; } = new() + { + Serial = "169.254.109.177:5555", + State = DeviceState.Online + }; + + /// + /// Tests the method. + /// + [Fact] + public void ClearInputTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_MOVE_END"] = string.Empty; + client.Commands["shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL"] = string.Empty; + + client.ClearInput(Device, 3); + + Assert.Equal(2, client.ReceivedCommands.Count); + Assert.Equal("shell:input keyevent KEYCODE_MOVE_END", client.ReceivedCommands[0]); + Assert.Equal("shell:input keyevent KEYCODE_DEL KEYCODE_DEL KEYCODE_DEL", client.ReceivedCommands[1]); + } + + /// + /// Tests the method. + /// + [Fact] + public void ClickBackButtonTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_BACK"] = string.Empty; + + client.ClickBackButton(Device); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_BACK", client.ReceivedCommands[0]); + } + + /// + /// Tests the method. + /// + [Fact] + public void ClickHomeButtonTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input keyevent KEYCODE_HOME"] = string.Empty; + + client.ClickHomeButton(Device); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input keyevent KEYCODE_HOME", client.ReceivedCommands[0]); + } + [Fact] public void StatTest() { @@ -19,15 +72,13 @@ public void StatTest() ISyncService mock = Substitute.For(); mock.Stat("/test").Returns(stats); - DeviceData device = new(); - Factories.SyncServiceFactory = (c, d) => { Factories.Reset(); return mock; }; - Assert.Equal(stats, client.Stat(device, "/test")); + Assert.Equal(stats, client.Stat(Device, "/test")); } [Fact] @@ -37,9 +88,7 @@ public void GetEnvironmentVariablesTest() adbClient.Commands[$"shell:{EnvironmentVariablesReceiver.PrintEnvCommand}"] = "a=b"; - DeviceData device = new(); - - Dictionary variables = adbClient.GetEnvironmentVariables(device); + Dictionary variables = adbClient.GetEnvironmentVariables(Device); Assert.NotNull(variables); Assert.Single(variables.Keys); Assert.True(variables.ContainsKey("a")); @@ -54,11 +103,7 @@ public void UninstallPackageTests() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm uninstall com.example"] = "Success"; - DeviceData device = new() - { - State = DeviceState.Online - }; - adbClient.UninstallPackage(device, "com.example"); + adbClient.UninstallPackage(Device, "com.example"); Assert.Equal(2, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); @@ -299,11 +344,7 @@ public void GetPackageVersionTest(string command, int versionCode, string versio adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands[$"shell:dumpsys package {packageName}"] = command; - DeviceData device = new() - { - State = DeviceState.Online - }; - VersionInfo version = adbClient.GetPackageVersion(device, packageName); + VersionInfo version = adbClient.GetPackageVersion(Device, packageName); Assert.Equal(versionCode, version.VersionCode); Assert.Equal(versionName, version.VersionName); @@ -342,8 +383,7 @@ public void ListProcessesTest() 3 (ksoftirqd/0) S 2 0 0 0 -1 69238848 0 0 0 0 0 23 0 0 20 0 1 0 7 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744071579284070 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; - DeviceData device = new(); - AndroidProcess[] processes = adbClient.ListProcesses(device).ToArray(); + AndroidProcess[] processes = adbClient.ListProcesses(Device).ToArray(); Assert.Equal(3, processes.Length); Assert.Equal("init", processes[0].Name); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs index 99b8e753..17cb9016 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/AndroidProcessTests.cs @@ -7,7 +7,7 @@ using System; using Xunit; -namespace AdvancedSharpAdbClient.Models.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs index 3d93c4fa..34b2e050 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Models/VersionInfoTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Models.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Models.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs index 88bd4b29..77e931ad 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/EnvironmentVariablesReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs index da7f71b9..3798d827 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/GetPropReceiverTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { public class GetPropReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs index a0871234..47bfcc54 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/InstallOutputReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { public class InstallOutputReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs index 1e2086fa..122b3ff0 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/PackageManagerReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { public class PackageManagerReceiverTests { diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs index 80fb796d..e1b03ca9 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/ProcessOutputReceiverTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs index 879856d1..55213e4e 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs @@ -8,7 +8,7 @@ using System.IO; using Xunit; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands.Tests +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers.Tests { /// /// Tests the class. diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index bd6717d8..c5c07644 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; namespace AdvancedSharpAdbClient.Tests { @@ -139,14 +137,6 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket #region Not Implemented - void IAdbClient.Click(DeviceData device, Point cords) => throw new NotImplementedException(); - - void IAdbClient.Click(DeviceData device, int x, int y) => throw new NotImplementedException(); - - Task IAdbClient.ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken) => throw new NotImplementedException(); - - Task IAdbClient.ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken) => throw new NotImplementedException(); - string IAdbClient.Connect(DnsEndPoint endpoint) => throw new NotImplementedException(); Task IAdbClient.ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); @@ -165,38 +155,10 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); - XmlDocument IAdbClient.DumpScreen(DeviceData device) => throw new NotImplementedException(); - - Task IAdbClient.DumpScreenAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - - string IAdbClient.DumpScreenString(DeviceData device) => throw new NotImplementedException(); - - Task IAdbClient.DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - -#if WINDOWS10_0_17763_0_OR_GREATER - Windows.Data.Xml.Dom.XmlDocument IAdbClient.DumpScreenWinRT(DeviceData device) => throw new NotImplementedException(); - - Task IAdbClient.DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); -#endif - - IAsyncEnumerable IAdbClient.FindAsyncElements(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - - Element IAdbClient.FindElement(DeviceData device, string xpath, TimeSpan timeout) => throw new NotImplementedException(); - - Task IAdbClient.FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - - IEnumerable IAdbClient.FindElements(DeviceData device, string xpath, TimeSpan timeout) => throw new NotImplementedException(); - - Task> IAdbClient.FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken) => throw new NotImplementedException(); - int IAdbClient.GetAdbVersion() => throw new NotImplementedException(); Task IAdbClient.GetAdbVersionAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - AppStatus IAdbClient.GetAppStatus(DeviceData device, string packageName) => throw new NotImplementedException(); - - Task IAdbClient.GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - IEnumerable IAdbClient.GetDevices() => throw new NotImplementedException(); Task> IAdbClient.GetDevicesAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); @@ -233,14 +195,6 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); - bool IAdbClient.IsAppInForeground(DeviceData device, string packageName) => throw new NotImplementedException(); - - Task IAdbClient.IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - - bool IAdbClient.IsAppRunning(DeviceData device, string packageName) => throw new NotImplementedException(); - - Task IAdbClient.IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - void IAdbClient.KillAdb() => throw new NotImplementedException(); Task IAdbClient.KillAdbAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); @@ -285,30 +239,6 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) => throw new NotImplementedException(); - void IAdbClient.SendKeyEvent(DeviceData device, string key) => throw new NotImplementedException(); - - Task IAdbClient.SendKeyEventAsync(DeviceData device, string key, CancellationToken cancellationToken) => throw new NotImplementedException(); - - void IAdbClient.SendText(DeviceData device, string text) => throw new NotImplementedException(); - - Task IAdbClient.SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken) => throw new NotImplementedException(); - - void IAdbClient.StartApp(DeviceData device, string packageName) => throw new NotImplementedException(); - - Task IAdbClient.StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - - void IAdbClient.StopApp(DeviceData device, string packageName) => throw new NotImplementedException(); - - Task IAdbClient.StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken) => throw new NotImplementedException(); - - void IAdbClient.Swipe(DeviceData device, Element first, Element second, long speed) => throw new NotImplementedException(); - - void IAdbClient.Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) => throw new NotImplementedException(); - - Task IAdbClient.SwipeAsync(DeviceData device, Element first, Element second, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); - - Task IAdbClient.SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken) => throw new NotImplementedException(); - void IAdbClient.Uninstall(DeviceData device, string packageName, params string[] arguments) => throw new NotImplementedException(); Task IAdbClient.UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index be8e0a92..5b38106f 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -3,6 +3,7 @@ using System.Drawing.Imaging; using System.IO; using System.Net; +using System.Runtime.Versioning; using Xunit; namespace AdvancedSharpAdbClient.Models.Tests @@ -24,6 +25,9 @@ public void ConstructorNullTest() } [Fact] +#if WINDOWS + [SupportedOSPlatform("windows")] +#endif public void RefreshTest() { DeviceData device = new() diff --git a/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs index bf4ebe71..8876232c 100644 --- a/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient.Tests/Properties/GlobalUsings.cs @@ -1,12 +1,11 @@ #region AdvancedSharpAdbClient -global using AdvancedSharpAdbClient.DeviceCommands; +global using AdvancedSharpAdbClient.DeviceCommands.Models; +global using AdvancedSharpAdbClient.DeviceCommands.Receivers; global using AdvancedSharpAdbClient.Exceptions; global using AdvancedSharpAdbClient.Logs; global using AdvancedSharpAdbClient.Models; -global using AdvancedSharpAdbClient.Models.DeviceCommands; global using AdvancedSharpAdbClient.Polyfills; global using AdvancedSharpAdbClient.Receivers; -global using AdvancedSharpAdbClient.Receivers.DeviceCommands; #endregion #region AdvancedSharpAdbClient.Tests diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index c2144b98..19d094cb 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -10,11 +10,8 @@ using System.IO; using System.Linq; using System.Net; -using System.Runtime.CompilerServices; using System.Text; -using System.Text.RegularExpressions; using System.Threading; -using System.Xml; namespace AdvancedSharpAdbClient { @@ -703,421 +700,6 @@ public async Task> GetFeatureSetAsync(DeviceData device, Can IEnumerable featureList = features.Trim().Split('\n', ','); return featureList; } - - /// - public async Task DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync("shell:uiautomator dump /dev/tty", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string xmlString = - reader.ReadToEnd() - .Replace("Events injected: 1\r\n", string.Empty) - .Replace("UI hierchary dumped to: /dev/tty", string.Empty) - .Trim(); - - if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" - public async Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); - XmlDocument doc = new(); - if (!string.IsNullOrEmpty(xmlString)) - { - doc.LoadXml(xmlString); - return doc; - } - return null; - } - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER - /// - public async Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - string xmlString = await DumpScreenStringAsync(device, cancellationToken).ConfigureAwait(false); - Windows.Data.Xml.Dom.XmlDocument doc = new(); - if (!string.IsNullOrEmpty(xmlString)) - { - doc.LoadXml(xmlString); - return doc; - } - return null; - } -#endif - - /// - public async Task ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input tap {cords.X} {cords.Y}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public async Task ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input tap {x} {y}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public async Task SwipeAsync(DeviceData device, Element first, Element second, long speed, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input swipe {x1} {y1} {x2} {y2} {speed}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public async Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:pidof {packageName}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string? result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()).ConfigureAwait(false); - bool intParsed = int.TryParse(result, out int pid); - return intParsed && pid > 0; - } - - /// - public async Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:dumpsys activity activities | grep mResumedActivity", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); - return result.Contains(packageName); - } - - /// - public async Task GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) - { - // Check if the app is in foreground - bool currentApp = await IsAppInForegroundAsync(device, packageName, cancellationToken).ConfigureAwait(false); - if (currentApp) - { - return AppStatus.Foreground; - } - - // Check if the app is running in background - bool isAppRunning = await IsAppRunningAsync(device, packageName, cancellationToken).ConfigureAwait(false); - return isAppRunning ? AppStatus.Background : AppStatus.Stopped; - } - - /// - public async Task FindElementAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) - { - try - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); - if (doc != null) - { - XmlNode? xmlNode = doc.SelectSingleNode(xpath); - if (xmlNode != null) - { - Element? element = Element.FromXmlNode(this, device, xmlNode); - if (element != null) - { - return element; - } - } - } - } - catch (XmlException) - { - // Ignore XmlException and try again - } - if (cancellationToken == default) { break; } - } - } - catch (Exception e) - { - // If a cancellation was requested, this main loop is interrupted with an exception - // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. - // In all other cases, something went wrong, and we want to report it to the user. - if (!cancellationToken.IsCancellationRequested) - { - throw new ShellCommandUnresponsiveException(e); - } - } - return null; - } - - /// - public async Task> FindElementsAsync(DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) - { - try - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - XmlDocument? doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); - if (doc != null) - { - XmlNodeList? xmlNodes = doc.SelectNodes(xpath); - if (xmlNodes != null) - { - static IEnumerable FindElements(IAdbClient client, DeviceData device, XmlNodeList xmlNodes) - { - for (int i = 0; i < xmlNodes.Count; i++) - { - Element? element = Element.FromXmlNode(client, device, xmlNodes[i]); - if (element != null) - { - yield return element; - } - } - } - return FindElements(this, device, xmlNodes); - } - } - } - catch (XmlException) - { - // Ignore XmlException and try again - } - if (cancellationToken == default) { break; } - } - } - catch (Exception e) - { - // If a cancellation was requested, this main loop is interrupted with an exception - // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. - // In all other cases, something went wrong, and we want to report it to the user. - if (!cancellationToken.IsCancellationRequested) - { - throw new ShellCommandUnresponsiveException(e); - } - } - return Enumerable.Empty(); - } - -#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - /// - public async IAsyncEnumerable FindAsyncElements(DeviceData device, string xpath = "hierarchy/node", [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - while (!cancellationToken.IsCancellationRequested) - { - XmlDocument? doc = null; - - try - { - doc = await DumpScreenAsync(device, cancellationToken).ConfigureAwait(false); - } - catch (XmlException) - { - // Ignore XmlException and try again - } - catch (Exception e) - { - // If a cancellation was requested, this main loop is interrupted with an exception - // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. - // In all other cases, something went wrong, and we want to report it to the user. - if (!cancellationToken.IsCancellationRequested) - { - throw new ShellCommandUnresponsiveException(e); - } - } - - if (doc != null) - { - XmlNodeList? xmlNodes = doc.SelectNodes(xpath); - if (xmlNodes != null) - { - for (int i = 0; i < xmlNodes.Count; i++) - { - Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); - if (element != null) - { - yield return element; - } - } - break; - } - } - - if (cancellationToken == default) { break; } - } - } -#endif - - /// - public async Task SendKeyEventAsync(DeviceData device, string key, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input keyevent {key}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new InvalidKeyEventException("KeyEvent is invalid"); - } - } - - /// - public async Task SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = await reader.ReadToEndAsync(cancellationToken).ContinueWith(x => x.Result.TrimStart()).ConfigureAwait(false); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new InvalidTextException(); - } - } - - /// - public async Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:monkey -p {packageName} 1", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - } - - /// - public async Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); - - await socket.SendAdbRequestAsync($"shell:am force-stop {packageName}", cancellationToken).ConfigureAwait(false); - _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); - } } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 85e36cff..adccb905 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -12,9 +12,7 @@ using System.Linq; using System.Net; using System.Text; -using System.Text.RegularExpressions; using System.Threading; -using System.Xml; namespace AdvancedSharpAdbClient { @@ -851,355 +849,6 @@ public IEnumerable GetFeatureSet(DeviceData device) return featureList; } - /// - public string DumpScreenString(DeviceData device) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest("shell:uiautomator dump /dev/tty"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string xmlString = reader.ReadToEnd() - .Replace("Events injected: 1\r\n", string.Empty) - .Replace("UI hierchary dumped to: /dev/tty", string.Empty) - .Trim(); - - if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" - public XmlDocument? DumpScreen(DeviceData device) - { - EnsureDevice(device); - XmlDocument doc = new(); - string xmlString = DumpScreenString(device); - if (!string.IsNullOrEmpty(xmlString)) - { - doc.LoadXml(xmlString); - return doc; - } - return null; - } - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER - /// - public Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT(DeviceData device) - { - EnsureDevice(device); - Windows.Data.Xml.Dom.XmlDocument doc = new(); - string xmlString = DumpScreenString(device); - if (!string.IsNullOrEmpty(xmlString)) - { - doc.LoadXml(xmlString); - return doc; - } - return null; - } -#endif - - /// - public void Click(DeviceData device, Point cords) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input tap {cords.X} {cords.Y}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public void Click(DeviceData device, int x, int y) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input tap {x} {y}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public void Swipe(DeviceData device, Element first, Element second, long speed) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input swipe {x1} {y1} {x2} {y2} {speed}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new ElementNotFoundException("Coordinates of element is invalid"); - } - } - - /// - public bool IsAppRunning(DeviceData device, string packageName) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:pidof {packageName}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string? result = reader.ReadToEnd().TrimStart().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); - bool intParsed = int.TryParse(result, out int pid); - return intParsed && pid > 0; - } - - /// - public bool IsAppInForeground(DeviceData device, string packageName) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:dumpsys activity activities | grep mResumedActivity"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd(); - return result.Contains(packageName); - } - - /// - public AppStatus GetAppStatus(DeviceData device, string packageName) - { - EnsureDevice(device); - - // Check if the app is in foreground - bool currentApp = IsAppInForeground(device, packageName); - if (currentApp) - { - return AppStatus.Foreground; - } - - // Check if the app is running in background - bool isAppRunning = IsAppRunning(device, packageName); - return isAppRunning ? AppStatus.Background : AppStatus.Stopped; - } - - /// - public Element? FindElement(DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) - { - EnsureDevice(device); - Stopwatch stopwatch = new(); - stopwatch.Start(); - do - { - try - { - XmlDocument? doc = DumpScreen(device); - if (doc != null) - { - XmlNode? xmlNode = doc.SelectSingleNode(xpath); - if (xmlNode != null) - { - Element? element = Element.FromXmlNode(this, device, xmlNode); - if (element != null) - { - return element; - } - } - } - } - catch (XmlException) - { - // Ignore XmlException and try again - } - if (timeout == default) { break; } - } - while (stopwatch.Elapsed < timeout); - return null; - } - - /// - public IEnumerable FindElements(DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) - { - EnsureDevice(device); - Stopwatch stopwatch = new(); - stopwatch.Start(); - do - { - XmlDocument? doc = null; - - try - { - doc = DumpScreen(device); - } - catch (XmlException) - { - // Ignore XmlException and try again - } - - if (doc != null) - { - XmlNodeList? xmlNodes = doc.SelectNodes(xpath); - if (xmlNodes != null) - { - for (int i = 0; i < xmlNodes.Count; i++) - { - Element? element = Element.FromXmlNode(this, device, xmlNodes[i]); - if (element != null) - { - yield return element; - } - } - break; - } - } - - if (timeout == default) { break; } - } - while (stopwatch.Elapsed < timeout); - } - - /// - public void SendKeyEvent(DeviceData device, string key) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input keyevent {key}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new InvalidKeyEventException("KeyEvent is invalid"); - } - } - - /// - public void SendText(DeviceData device, string text) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:input text {text}"); - _ = socket.ReadAdbResponse(); - - using StreamReader reader = new(socket.GetShellStream(), Encoding); - string result = reader.ReadToEnd().TrimStart(); - - if (result.StartsWith("java.lang.")) - { - throw JavaException.Parse(result); - } - else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR - { - throw new InvalidTextException(); - } - } - - /// - public void StartApp(DeviceData device, string packageName) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:monkey -p {packageName} 1"); - _ = socket.ReadAdbResponse(); - } - - /// - public void StopApp(DeviceData device, string packageName) - { - EnsureDevice(device); - - using IAdbSocket socket = adbSocketFactory(EndPoint); - socket.SetDevice(device); - - socket.SendAdbRequest($"shell:am force-stop {packageName}"); - _ = socket.ReadAdbResponse(); - } - /// /// Sets default encoding (default - UTF8). /// @@ -1220,13 +869,6 @@ protected static void EnsureDevice([NotNull] DeviceData? device) throw new ArgumentOutOfRangeException(nameof(device), "You must specific a serial number for the device"); } } - -#if NET7_0_OR_GREATER - [GeneratedRegex("<\\?xml(.?)*")] - private static partial Regex GetXmlRegex(); -#else - private static Regex GetXmlRegex() => new("<\\?xml(.?)*"); -#endif } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs new file mode 100644 index 00000000..dba0cb40 --- /dev/null +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs @@ -0,0 +1,453 @@ +#if HAS_TASK +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; + +namespace AdvancedSharpAdbClient.DeviceCommands +{ + public partial class DeviceClient + { + /// + /// Gets the current device screen snapshot asynchronously. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return a containing current hierarchy. + /// Failed if start with ERROR or java.lang.Exception. + public async Task DumpScreenStringAsync(CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, "uiautomator dump /dev/tty", receiver, cancellationToken).ConfigureAwait(false); + + string xmlString = + receiver.ToString() + .Replace("Events injected: 1\r\n", string.Empty) + .Replace("UI hierchary dumped to: /dev/tty", string.Empty) + .Trim(); + + if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" + /// Gets the current device screen snapshot asynchronously. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return a containing current hierarchy. + public async Task DumpScreenAsync(CancellationToken cancellationToken = default) + { + string xmlString = await DumpScreenStringAsync(cancellationToken).ConfigureAwait(false); + XmlDocument doc = new(); + if (!string.IsNullOrEmpty(xmlString)) + { + doc.LoadXml(xmlString); + return doc; + } + return null; + } + +#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER + /// + /// Gets the current device screen snapshot asynchronously. + /// + /// A which can be used to cancel the asynchronous operation. + /// A which return a containing current hierarchy. + public async Task DumpScreenWinRTAsync(CancellationToken cancellationToken = default) + { + string xmlString = await DumpScreenStringAsync(cancellationToken).ConfigureAwait(false); + Windows.Data.Xml.Dom.XmlDocument doc = new(); + if (!string.IsNullOrEmpty(xmlString)) + { + doc.LoadXml(xmlString); + return doc; + } + return null; + } +#endif + + /// + /// Clicks on the specified coordinates. + /// + /// The to click. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task ClickAsync(Point cords, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input tap {cords.X} {cords.Y}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Clicks on the specified coordinates. + /// + /// The X co-ordinate to click. + /// The Y co-ordinate to click. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task ClickAsync(int x, int y, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input tap {x} {y}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from first element to second element Specify the speed in ms. + /// + /// The start element. + /// The end element. + /// The time spent in swiping. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task SwipeAsync(Element first, Element second, long speed, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. + /// + /// The start X co-ordinate. + /// The start Y co-ordinate. + /// The end X co-ordinate. + /// The end Y co-ordinate. + /// The time spent in swiping. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task SwipeAsync(int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {x1} {y1} {x2} {y2} {speed}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Check if the app is running in foreground. + /// + /// The package name of the app to check. + /// A which can be used to cancel the asynchronous operation. + /// A which return the result. if the app is running in foreground; otherwise, . + public async Task IsAppRunningAsync(string packageName, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"pidof {packageName}", receiver, cancellationToken).ConfigureAwait(false); + + string? result = receiver.ToString().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + bool intParsed = int.TryParse(result, out int pid); + return intParsed && pid > 0; + } + + /// + /// Check if the app is running in background. + /// + /// The package name of the app to check. + /// A which can be used to cancel the asynchronous operation. + /// A which return the result. if the app is running in background; otherwise, . + public async Task IsAppInForegroundAsync(string packageName, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"dumpsys activity activities | grep mResumedActivity", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString(); + return result.Contains(packageName); + } + + /// + /// Get the of the app. + /// + /// The package name of the app to check. + /// A which can be used to cancel the asynchronous operation. + /// A which return the of the app. Foreground, stopped or running in background. + public async Task GetAppStatusAsync(string packageName, CancellationToken cancellationToken = default) + { + // Check if the app is in foreground + bool currentApp = await IsAppInForegroundAsync(packageName, cancellationToken).ConfigureAwait(false); + if (currentApp) + { + return AppStatus.Foreground; + } + + // Check if the app is running in background + bool isAppRunning = await IsAppRunningAsync(packageName, cancellationToken).ConfigureAwait(false); + return isAppRunning ? AppStatus.Background : AppStatus.Stopped; + } + + /// + /// Get element by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// The xpath of the elements. + /// A which can be used to cancel the asynchronous operation. + /// Only check once if . Or it will continue check until is . + /// A which return the of . + public async Task FindElementAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + XmlDocument? doc = await DumpScreenAsync(cancellationToken).ConfigureAwait(false); + if (doc != null) + { + XmlNode? xmlNode = doc.SelectSingleNode(xpath); + if (xmlNode != null) + { + Element? element = Element.FromXmlNode(AdbClient, Device, xmlNode); + if (element != null) + { + return element; + } + } + } + } + catch (XmlException) + { + // Ignore XmlException and try again + } + if (cancellationToken == default) { break; } + } + } + catch (Exception e) + { + // If a cancellation was requested, this main loop is interrupted with an exception + // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. + // In all other cases, something went wrong, and we want to report it to the user. + if (!cancellationToken.IsCancellationRequested) + { + throw new ShellCommandUnresponsiveException(e); + } + } + return null; + } + + /// + /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// The xpath of the elements. + /// A which can be used to cancel the asynchronous operation. + /// Only check once if . Or it will continue check until is . + /// A which return the of has got. + public async Task> FindElementsAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + XmlDocument? doc = await DumpScreenAsync(cancellationToken).ConfigureAwait(false); + if (doc != null) + { + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); + if (xmlNodes != null) + { + static IEnumerable FindElements(IAdbClient client, DeviceData device, XmlNodeList xmlNodes) + { + for (int i = 0; i < xmlNodes.Count; i++) + { + Element? element = Element.FromXmlNode(client, device, xmlNodes[i]); + if (element != null) + { + yield return element; + } + } + } + return FindElements(AdbClient, Device, xmlNodes); + } + } + } + catch (XmlException) + { + // Ignore XmlException and try again + } + if (cancellationToken == default) { break; } + } + } + catch (Exception e) + { + // If a cancellation was requested, this main loop is interrupted with an exception + // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. + // In all other cases, something went wrong, and we want to report it to the user. + if (!cancellationToken.IsCancellationRequested) + { + throw new ShellCommandUnresponsiveException(e); + } + } + return Enumerable.Empty(); + } + +#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// + /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// The xpath of the elements. + /// A which can be used to cancel the asynchronous operation. + /// Only check once if . Or it will continue check until is . + /// A which return the of has got. + public async IAsyncEnumerable FindAsyncElements(string xpath = "hierarchy/node", [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + while (!cancellationToken.IsCancellationRequested) + { + XmlDocument? doc = null; + + try + { + doc = await DumpScreenAsync(cancellationToken).ConfigureAwait(false); + } + catch (XmlException) + { + // Ignore XmlException and try again + } + catch (Exception e) + { + // If a cancellation was requested, this main loop is interrupted with an exception + // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. + // In all other cases, something went wrong, and we want to report it to the user. + if (!cancellationToken.IsCancellationRequested) + { + throw new ShellCommandUnresponsiveException(e); + } + } + + if (doc != null) + { + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); + if (xmlNodes != null) + { + for (int i = 0; i < xmlNodes.Count; i++) + { + Element? element = Element.FromXmlNode(AdbClient, Device, xmlNodes[i]); + if (element != null) + { + yield return element; + } + } + break; + } + } + + if (cancellationToken == default) { break; } + } + } +#endif + + /// + /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. + /// + /// The key event to send. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task SendKeyEventAsync(string key, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input keyevent {key}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidKeyEventException("KeyEvent is invalid"); + } + } + + /// + /// Send text to device. Doesn't support Russian. + /// + /// The text to send. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task SendTextAsync(string text, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input text {text}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidTextException(); + } + } + + /// + /// Start an Android application on device. + /// + /// The package name of the application to start. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public Task StartAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"monkey -p {packageName} 1", cancellationToken); + + /// + /// Stop an Android application on device. + /// + /// The package name of the application to stop. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public Task StopAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"am force-stop {packageName}", cancellationToken); + } +} +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs new file mode 100644 index 00000000..82c2a6fc --- /dev/null +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs @@ -0,0 +1,402 @@ +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; + +namespace AdvancedSharpAdbClient.DeviceCommands +{ + /// + /// A class which contains methods for interacting with an Android device by + /// + public partial class DeviceClient + { + /// + /// Initializes a new instance of the class. + /// + /// The to use to communicate with the Android Debug Bridge. + /// The device on which to process command. + public DeviceClient(IAdbClient client, DeviceData device) + { + ExceptionExtensions.ThrowIfNull(client); + ExceptionExtensions.ThrowIfNull(device); + if (string.IsNullOrEmpty(device.Serial)) + { + throw new ArgumentOutOfRangeException(nameof(device), "You must specific a serial number for the device"); + } + Device = device; + AdbClient = client; + } + + /// + /// Gets the device. + /// + public DeviceData Device { get; init; } + + /// + /// The to use when communicating with the device. + /// + public IAdbClient AdbClient { get; init; } + + /// + /// Gets the current device screen snapshot. + /// + /// A containing current hierarchy. + /// Failed if start with ERROR or java.lang.Exception. + public string DumpScreenString() + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, "uiautomator dump /dev/tty", receiver); + + string xmlString = + receiver.ToString() + .Replace("Events injected: 1\r\n", string.Empty) + .Replace("UI hierchary dumped to: /dev/tty", string.Empty) + .Trim(); + + if (string.IsNullOrEmpty(xmlString) || xmlString.StartsWith(" + /// Gets the current device screen snapshot. + /// + /// A containing current hierarchy. + public XmlDocument? DumpScreen() + { + XmlDocument doc = new(); + string xmlString = DumpScreenString(); + if (!string.IsNullOrEmpty(xmlString)) + { + doc.LoadXml(xmlString); + return doc; + } + return null; + } + +#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER + /// + /// Gets the current device screen snapshot. + /// + /// A containing current hierarchy. + public Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT() + { + Windows.Data.Xml.Dom.XmlDocument doc = new(); + string xmlString = DumpScreenString(); + if (!string.IsNullOrEmpty(xmlString)) + { + doc.LoadXml(xmlString); + return doc; + } + return null; + } +#endif + + /// + /// Clicks on the specified coordinates. + /// + /// The to click. + public void Click(Point cords) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input tap {cords.X} {cords.Y}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Clicks on the specified coordinates. + /// + /// The X co-ordinate to click. + /// The Y co-ordinate to click. + public void Click(int x, int y) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input tap {x} {y}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from first element to second element Specify the speed in ms. + /// + /// The start element. + /// The end element. + /// The time spent in swiping. + public void Swipe(Element first, Element second, long speed) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. + /// + /// The start X co-ordinate. + /// The start Y co-ordinate. + /// The end X co-ordinate. + /// The end Y co-ordinate. + /// The time spent in swiping. + public void Swipe(int x1, int y1, int x2, int y2, long speed) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input swipe {x1} {y1} {x2} {y2} {speed}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Check if the app is running in foreground. + /// + /// The package name of the app to check. + /// if the app is running in foreground; otherwise, . + public bool IsAppRunning(string packageName) + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"pidof {packageName}", receiver); + + string? result = receiver.ToString().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + bool intParsed = int.TryParse(result, out int pid); + return intParsed && pid > 0; + } + + /// + /// Check if the app is running in background. + /// + /// The package name of the app to check. + /// if the app is running in background; otherwise, . + public bool IsAppInForeground(string packageName) + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"dumpsys activity activities | grep mResumedActivity", receiver); + + string result = receiver.ToString(); + return result.Contains(packageName); + } + + /// + /// Get the of the app. + /// + /// The package name of the app to check. + /// The of the app. Foreground, stopped or running in background. + public AppStatus GetAppStatus(string packageName) + { + // Check if the app is in foreground + bool currentApp = IsAppInForeground(packageName); + if (currentApp) + { + return AppStatus.Foreground; + } + + // Check if the app is running in background + bool isAppRunning = IsAppRunning(packageName); + return isAppRunning ? AppStatus.Background : AppStatus.Stopped; + } + + /// + /// Get element by xpath. You can specify the waiting time in timeout. + /// + /// The xpath of the element. + /// The timeout for waiting the element. + /// Only check once if or . + /// The of . + public Element? FindElement(string xpath = "hierarchy/node", TimeSpan timeout = default) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + do + { + try + { + XmlDocument? doc = DumpScreen(); + if (doc != null) + { + XmlNode? xmlNode = doc.SelectSingleNode(xpath); + if (xmlNode != null) + { + Element? element = Element.FromXmlNode(AdbClient, Device, xmlNode); + if (element != null) + { + return element; + } + } + } + } + catch (XmlException) + { + // Ignore XmlException and try again + } + if (timeout == default) { break; } + } + while (stopwatch.Elapsed < timeout); + return null; + } + + /// + /// Get elements by xpath. You can specify the waiting time in timeout. + /// + /// The xpath of the elements. + /// The timeout for waiting the elements. + /// Only check once if or . + /// The of has got. + public IEnumerable FindElements(string xpath = "hierarchy/node", TimeSpan timeout = default) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + do + { + XmlDocument? doc = null; + + try + { + doc = DumpScreen(); + } + catch (XmlException) + { + // Ignore XmlException and try again + } + + if (doc != null) + { + XmlNodeList? xmlNodes = doc.SelectNodes(xpath); + if (xmlNodes != null) + { + for (int i = 0; i < xmlNodes.Count; i++) + { + Element? element = Element.FromXmlNode(AdbClient, Device, xmlNodes[i]); + if (element != null) + { + yield return element; + } + } + break; + } + } + + if (timeout == default) { break; } + } + while (stopwatch.Elapsed < timeout); + } + + /// + /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. + /// + /// The key event to send. + public void SendKeyEvent(string key) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input keyevent {key}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidKeyEventException("KeyEvent is invalid"); + } + } + + /// + /// Send text to device. Doesn't support Russian. + /// + /// The text to send. + public void SendText(string text) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input text {text}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidTextException(); + } + } + + /// + /// Start an Android application on device. + /// + /// The package name of the application to start. + public void StartApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"monkey -p {packageName} 1"); + + /// + /// Stop an Android application on device. + /// + /// The package name of the application to stop. + public void StopApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"am force-stop {packageName}"); + + /// + /// Deconstruct the class. + /// + /// The to use to communicate with the Android Debug Bridge. + /// The device on which to process command. + public void Deconstruct(out IAdbClient client, out DeviceData device) + { + client = AdbClient; + device = Device; + } + +#if NET7_0_OR_GREATER + [GeneratedRegex("<\\?xml(.?)*")] + private static partial Regex GetXmlRegex(); +#else + private static Regex GetXmlRegex() => new("<\\?xml(.?)*"); +#endif + } +} diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index c4fa69d2..61dbc968 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -37,6 +37,39 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); + /// + /// Clear the input text. The input should be in focus. Use if the element isn't focused. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The length of text to clear. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static async Task ClearInputAsync(this IAdbClient client, DeviceData device, int charCount, CancellationToken cancellationToken = default) + { + DeviceClient deviceClient = new(client, device); + await deviceClient.SendKeyEventAsync("KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); + await deviceClient.SendKeyEventAsync(StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); + } + + /// + /// Click BACK button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click BACK button. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_BACK", cancellationToken); + + /// + /// Click HOME button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_HOME", cancellationToken); + /// /// Gets the file statistics of a given file. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index b3e0f4fb..3da7bbd5 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -35,6 +35,33 @@ public static void ExecuteShellCommand(this IAdbClient client, DeviceData device public static void ExecuteShellCommand(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver) => client.ExecuteRemoteCommand(command, device, receiver, AdbClient.Encoding); + /// + /// Clear the input text. The input should be in focus. Use if the element isn't focused. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The length of text to clear. + public static void ClearInput(this IAdbClient client, DeviceData device, int charCount) + { + DeviceClient deviceClient = new(client, device); + deviceClient.SendKeyEvent("KEYCODE_MOVE_END"); + deviceClient.SendKeyEvent(StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); + } + + /// + /// Click BACK button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click BACK button. + public static void ClickBackButton(this IAdbClient client, DeviceData device) => new DeviceClient(client, device).SendKeyEvent("KEYCODE_BACK"); + + /// + /// Click HOME button. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + public static void ClickHomeButton(this IAdbClient client, DeviceData device) => new DeviceClient(client, device).SendKeyEvent("KEYCODE_HOME"); + /// /// Gets the file statistics of a given file. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs index 408a5f4d..107c105b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/AndroidProcess.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Represents a process running on an Android device. diff --git a/AdvancedSharpAdbClient/Models/Element.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs similarity index 83% rename from AdvancedSharpAdbClient/Models/Element.cs rename to AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs index 454a89a5..c9a6bb4d 100644 --- a/AdvancedSharpAdbClient/Models/Element.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs @@ -9,7 +9,7 @@ using System.Threading; using System.Xml; -namespace AdvancedSharpAdbClient.Models +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Implement of screen element, likes Selenium. @@ -222,7 +222,22 @@ static IEnumerable FindElements(IAdbClient client, DeviceData device, W /// /// Clicks on this coordinates. /// - public void Click() => Client.Click(Device, Center); + public void Click() + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + Client.ExecuteShellCommand(Device, $"input tap {Center.X} {Center.Y}", receiver); + + string result = receiver.ToString(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } /// /// Send text to device. Doesn't support Russian. @@ -231,17 +246,30 @@ static IEnumerable FindElements(IAdbClient client, DeviceData device, W public void SendText(string text) { Click(); - Client.SendText(Device, text); + + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + Client.ExecuteShellCommand(Device, $"input text {text}", receiver); + + string result = receiver.ToString(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidTextException(); + } } /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// [MemberNotNull(nameof(Text))] public void ClearInput() => ClearInput(Text!.Length); /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// /// The length of text to clear. public void ClearInput(int charCount) @@ -255,8 +283,22 @@ public void ClearInput(int charCount) /// Clicks on this coordinates. /// /// A which can be used to cancel the asynchronous operation. - public Task ClickAsync(CancellationToken cancellationToken = default) => - Client.ClickAsync(Device, Center, cancellationToken); + public async Task ClickAsync(CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + await Client.ExecuteShellCommandAsync(Device, $"input tap {Center.X} {Center.Y}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } /// /// Send text to device. Doesn't support Russian. @@ -267,11 +309,24 @@ public Task ClickAsync(CancellationToken cancellationToken = default) => public async Task SendTextAsync(string text, CancellationToken cancellationToken = default) { await ClickAsync(cancellationToken).ConfigureAwait(false); - await Client.SendTextAsync(Device, text, cancellationToken).ConfigureAwait(false); + + ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; + await Client.ExecuteShellCommandAsync(Device, $"input text {text}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new InvalidTextException(); + } } /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. @@ -280,7 +335,7 @@ public Task ClearInputAsync(CancellationToken cancellationToken = default) => ClearInputAsync(Text!.Length, cancellationToken); /// - /// Clear the input text. Use if the element is focused. + /// Clear the input text. Use if the element is focused. /// /// The length of text to clear. /// A which can be used to cancel the asynchronous operation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs index 671963bb..1b7d224f 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/AndroidProcessState.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Represents the state of a process running on an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs index 696278b7..710c9839 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs @@ -2,7 +2,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Represents the state of the installation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs index d93bc175..225d9941 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PerProcessFlags.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Per process flags. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs index d003c714..105eedfb 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs @@ -4,7 +4,7 @@ using System; -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Represents the state of the installation for . diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs index 91d43186..644e8e94 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/NamespaceDoc.cs @@ -5,13 +5,13 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// - /// The classes in this namespace provide models for . + /// The classes in this namespace provide models for . /// /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. [CompilerGenerated] [EditorBrowsable(EditorBrowsableState.Never)] - internal class NamespaceDoc : Models.NamespaceDoc { } + internal class NamespaceDoc : AdvancedSharpAdbClient.Models.NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs index a52af84b..61ad2c8e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/VersionInfo.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient.Models.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Models { /// /// Represents a version of an Android application. diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 09c7aac8..4ed37369 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -110,7 +110,7 @@ public PackageManager(IAdbClient client, DeviceData device, Func /// The to use when communicating with the device. /// - protected IAdbClient AdbClient { get; init; } + public IAdbClient AdbClient { get; init; } /// /// Refreshes the packages. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs index adeb7b5b..288a0046 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/EnvironmentVariablesReceiver.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Processes the output of the printenv command, which dumps all environment variables of an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs index 0a3dc218..8117f9fe 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/GetPropReceiver.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Parses the output of the getprop command, which lists all properties of an Android device. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs index c8d7e346..9f1f8c6e 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InfoOutputReceiver.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Processes command line output of a adb shell command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs index 71d25ae7..909bf6ef 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/InstallOutputReceiver.cs @@ -4,7 +4,7 @@ using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Processes output of the pm install command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs index 8c2de411..c29a9c31 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/NamespaceDoc.cs @@ -5,13 +5,13 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// - /// The classes in this namespace provide receivers for . + /// The classes in this namespace provide receivers for . /// /// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. [CompilerGenerated] [EditorBrowsable(EditorBrowsableState.Never)] - internal class NamespaceDoc : Receivers.NamespaceDoc { } + internal class NamespaceDoc : AdvancedSharpAdbClient.Receivers.NamespaceDoc { } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs index 44c8f8a7..0a4308d0 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/PackageManagerReceiver.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Parses the output of the various pm commands. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs index 7f51dc66..9b2a700c 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/ProcessOutputReceiver.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Parses the output of a cat /proc/[pid]/stat command. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs index c9d4d37c..91481939 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Receivers/VersionInfoReceiver.cs @@ -5,7 +5,7 @@ using System; using System.Text.RegularExpressions; -namespace AdvancedSharpAdbClient.Receivers.DeviceCommands +namespace AdvancedSharpAdbClient.DeviceCommands.Receivers { /// /// Processes command line output of the dumpsys package command. diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index be2808eb..b17914a8 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -324,38 +324,6 @@ public static Task InstallCreateAsync(this IAdbClient client, DeviceData /// A which represents the asynchronous operation. public static Task UninstallAsync(this IAdbClient client, DeviceData device, string packageName, params string[] arguments) => client.UninstallAsync(device, packageName, default, arguments); - - /// - /// Clear the input text. The input should be in focus. Use if the element isn't focused. - /// - /// An instance of a class that implements the interface. - /// The device on which to clear the input text. - /// The length of text to clear. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - public static async Task ClearInputAsync(this IAdbClient client, DeviceData device, int charCount, CancellationToken cancellationToken = default) - { - await client.SendKeyEventAsync(device, "KEYCODE_MOVE_END", cancellationToken).ConfigureAwait(false); - await client.SendKeyEventAsync(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount)), cancellationToken).ConfigureAwait(false); - } - - /// - /// Click BACK button. - /// - /// An instance of a class that implements the interface. - /// The device on which to click BACK button. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => client.SendKeyEventAsync(device, "KEYCODE_BACK", cancellationToken); - - /// - /// Click HOME button. - /// - /// An instance of a class that implements the interface. - /// The device on which to click HOME button. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => client.SendKeyEventAsync(device, "KEYCODE_HOME", cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs index 7cb1158b..20f79380 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.cs @@ -3,7 +3,6 @@ // using System; -using System.Linq; using System.Net; namespace AdvancedSharpAdbClient @@ -225,31 +224,5 @@ public static string Connect(this IAdbClient client, IPEndPoint endpoint) => /// The results from adb. public static string Connect(this IAdbClient client, string host, int port = AdbClient.DefaultPort) => client.Connect(Extensions.CreateDnsEndPoint(host, port)); - - /// - /// Clear the input text. The input should be in focus. Use if the element isn't focused. - /// - /// An instance of a class that implements the interface. - /// The device on which to clear the input text. - /// The length of text to clear. - public static void ClearInput(this IAdbClient client, DeviceData device, int charCount) - { - client.SendKeyEvent(device, "KEYCODE_MOVE_END"); - client.SendKeyEvent(device, StringExtensions.Join(" ", Enumerable.Repeat("KEYCODE_DEL", charCount))); - } - - /// - /// Click BACK button. - /// - /// An instance of a class that implements the interface. - /// The device on which to click BACK button. - public static void ClickBackButton(this IAdbClient client, DeviceData device) => client.SendKeyEvent(device, "KEYCODE_BACK"); - - /// - /// Click HOME button. - /// - /// An instance of a class that implements the interface. - /// The device on which to click HOME button. - public static void ClickHomeButton(this IAdbClient client, DeviceData device) => client.SendKeyEvent(device, "KEYCODE_HOME"); } } diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 6d3859fb..120065df 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -7,10 +7,8 @@ using System.Collections.Generic; using System.IO; using System.Net; -using System.Runtime.CompilerServices; using System.Text; using System.Threading; -using System.Xml; namespace AdvancedSharpAdbClient { @@ -384,185 +382,6 @@ public partial interface IAdbClient /// A which return the list of all features supported by the current device. Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken); - /// - /// Gets the current device screen snapshot asynchronously. - /// - /// The device for which to get the screen snapshot. - /// A which can be used to cancel the asynchronous operation. - /// A which return a containing current hierarchy. - /// Failed if start with ERROR or java.lang.Exception. - Task DumpScreenStringAsync(DeviceData device, CancellationToken cancellationToken); - - /// - /// Gets the current device screen snapshot asynchronously. - /// - /// The device for which to get the screen snapshot. - /// A which can be used to cancel the asynchronous operation. - /// A which return a containing current hierarchy. - Task DumpScreenAsync(DeviceData device, CancellationToken cancellationToken); - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER - /// - /// Gets the current device screen snapshot asynchronously. - /// - /// The device for which to get the screen snapshot. - /// A which can be used to cancel the asynchronous operation. - /// A which return a containing current hierarchy. - Task DumpScreenWinRTAsync(DeviceData device, CancellationToken cancellationToken) -#if WINDOWS10_0_17763_0_OR_GREATER - => DumpScreenAsync(device, cancellationToken).ContinueWith(x => - { - Windows.Data.Xml.Dom.XmlDocument doc = new(); - doc.LoadXml(x.Result?.OuterXml); - return doc; - })! -#endif - ; -#endif - - /// - /// Clicks on the specified coordinates. - /// - /// The device on which to click. - /// The to click. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task ClickAsync(DeviceData device, Point cords, CancellationToken cancellationToken); - - /// - /// Clicks on the specified coordinates. - /// - /// The device on which to click. - /// The X co-ordinate to click. - /// The Y co-ordinate to click. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task ClickAsync(DeviceData device, int x, int y, CancellationToken cancellationToken); - - /// - /// Generates a swipe gesture from first element to second element Specify the speed in ms. - /// - /// The device on which to swipe. - /// The start element. - /// The end element. - /// The time spent in swiping. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task SwipeAsync(DeviceData device, Element first, Element second, long speed, CancellationToken cancellationToken); - - /// - /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. - /// - /// The device on which to swipe. - /// The start X co-ordinate. - /// The start Y co-ordinate. - /// The end X co-ordinate. - /// The end Y co-ordinate. - /// The time spent in swiping. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken); - - /// - /// Check if the app is running in foreground. - /// - /// The device on which to check. - /// The package name of the app to check. - /// A which can be used to cancel the asynchronous operation. - /// A which return the result. if the app is running in foreground; otherwise, . - Task IsAppInForegroundAsync(DeviceData device, string packageName, CancellationToken cancellationToken); - - /// - /// Check if the app is running in background. - /// - /// The device on which to check. - /// The package name of the app to check. - /// A which can be used to cancel the asynchronous operation. - /// A which return the result. if the app is running in background; otherwise, . - Task IsAppRunningAsync(DeviceData device, string packageName, CancellationToken cancellationToken); - - /// - /// Get the of the app. - /// - /// The device on which to get status. - /// The package name of the app to check. - /// A which can be used to cancel the asynchronous operation. - /// A which return the of the app. Foreground, stopped or running in background. - Task GetAppStatusAsync(DeviceData device, string packageName, CancellationToken cancellationToken); - - /// - /// Get element by xpath asynchronously. You can specify the waiting time in timeout. - /// - /// - /// - /// A which can be used to cancel the asynchronous operation. - /// Only check once if . Or it will continue check until is . - /// A which return the of . - Task FindElementAsync(DeviceData device, string xpath, CancellationToken cancellationToken); - - /// - /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. - /// - /// The device on which to get elements. - /// The xpath of the elements. - /// A which can be used to cancel the asynchronous operation. - /// Only check once if . Or it will continue check until is . - /// A which return the of has got. - Task> FindElementsAsync(DeviceData device, string xpath, CancellationToken cancellationToken); - -#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - /// - /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. - /// - /// The device on which to get elements. - /// The xpath of the elements. - /// A which can be used to cancel the asynchronous operation. - /// Only check once if . Or it will continue check until is . - /// A which return the of has got. - async IAsyncEnumerable FindAsyncElements(DeviceData device, string xpath, [EnumeratorCancellation] CancellationToken cancellationToken) - { - foreach (Element element in await FindElementsAsync(device, xpath, cancellationToken).ConfigureAwait(false)) - { - yield return element; - } - } -#endif - - /// - /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. - /// - /// The device on which to send key event. - /// The key event to send. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task SendKeyEventAsync(DeviceData device, string key, CancellationToken cancellationToken); - - /// - /// Send text to device. Doesn't support Russian. - /// - /// The device on which to send text. - /// The text to send. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task SendTextAsync(DeviceData device, string text, CancellationToken cancellationToken); - - /// - /// Start an Android application on device. - /// - /// The device on which to start an application. - /// The package name of the application to start. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken); - - /// - /// Stop an Android application on device. - /// - /// The device on which to stop an application. - /// The package name of the application to stop. - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. - Task StopAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 8edbc4c7..592f359d 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -8,7 +8,6 @@ using System.IO; using System.Net; using System.Text; -using System.Xml; namespace AdvancedSharpAdbClient { @@ -382,146 +381,6 @@ public partial interface IAdbClient /// The device for which to get the list of features supported. /// A list of all features supported by the current device. IEnumerable GetFeatureSet(DeviceData device); - - /// - /// Gets the current device screen snapshot. - /// - /// The device for which to get the screen snapshot. - /// A containing current hierarchy. - /// Failed if start with ERROR or java.lang.Exception. - string DumpScreenString(DeviceData device); - - /// - /// Gets the current device screen snapshot. - /// - /// The device for which to get the screen snapshot. - /// A containing current hierarchy. - XmlDocument? DumpScreen(DeviceData device); - -#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER - /// - /// Gets the current device screen snapshot. - /// - /// The device for which to get the screen snapshot. - /// A containing current hierarchy. - Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT(DeviceData device) -#if WINDOWS10_0_17763_0_OR_GREATER - { - Windows.Data.Xml.Dom.XmlDocument doc = new(); - doc.LoadXml(DumpScreen(device)?.OuterXml); - return doc; - } -#else - ; -#endif -#endif - - /// - /// Clicks on the specified coordinates. - /// - /// The device on which to click. - /// The to click. - void Click(DeviceData device, Point cords); - - /// - /// Clicks on the specified coordinates. - /// - /// The device on which to click. - /// The X co-ordinate to click. - /// The Y co-ordinate to click. - void Click(DeviceData device, int x, int y); - - /// - /// Generates a swipe gesture from first element to second element Specify the speed in ms. - /// - /// The device on which to swipe. - /// The start element. - /// The end element. - /// The time spent in swiping. - void Swipe(DeviceData device, Element first, Element second, long speed); - - /// - /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. - /// - /// The device on which to swipe. - /// The start X co-ordinate. - /// The start Y co-ordinate. - /// The end X co-ordinate. - /// The end Y co-ordinate. - /// The time spent in swiping. - void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed); - - /// - /// Check if the app is running in foreground. - /// - /// The device on which to check. - /// The package name of the app to check. - /// if the app is running in foreground; otherwise, . - bool IsAppInForeground(DeviceData device, string packageName); - - /// - /// Check if the app is running in background. - /// - /// The device on which to check. - /// The package name of the app to check. - /// if the app is running in background; otherwise, . - bool IsAppRunning(DeviceData device, string packageName); - - /// - /// Get the of the app. - /// - /// The device on which to get status. - /// The package name of the app to check. - /// The of the app. Foreground, stopped or running in background. - AppStatus GetAppStatus(DeviceData device, string packageName); - - /// - /// Get element by xpath. You can specify the waiting time in timeout. - /// - /// The device on which to get element. - /// The xpath of the element. - /// The timeout for waiting the element. - /// Only check once if or . - /// The of . - Element? FindElement(DeviceData device, string xpath, TimeSpan timeout = default); - - /// - /// Get elements by xpath. You can specify the waiting time in timeout. - /// - /// The device on which to get elements. - /// The xpath of the elements. - /// The timeout for waiting the elements. - /// Only check once if or . - /// The of has got. - IEnumerable FindElements(DeviceData device, string xpath, TimeSpan timeout = default); - - /// - /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. - /// - /// The device on which to send key event. - /// The key event to send. - void SendKeyEvent(DeviceData device, string key); - - /// - /// Send text to device. Doesn't support Russian. - /// - /// The device on which to send text. - /// The text to send. - void SendText(DeviceData device, string text); - - /// - /// Start an Android application on device. - /// - /// The device on which to start an application. - /// The package name of the application to start. - void StartApp(DeviceData device, string packageName); - - /// - /// Stop an Android application on device. - /// - /// The device on which to stop an application. - /// The package name of the application to stop. - void StopApp(DeviceData device, string packageName); } /// diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index c4d2f724..4fd4524f 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -1,12 +1,11 @@ #region AdvancedSharpAdbClient -global using AdvancedSharpAdbClient.DeviceCommands; +global using AdvancedSharpAdbClient.DeviceCommands.Models; +global using AdvancedSharpAdbClient.DeviceCommands.Receivers; global using AdvancedSharpAdbClient.Exceptions; global using AdvancedSharpAdbClient.Logs; global using AdvancedSharpAdbClient.Models; -global using AdvancedSharpAdbClient.Models.DeviceCommands; global using AdvancedSharpAdbClient.Polyfills; global using AdvancedSharpAdbClient.Receivers; -global using AdvancedSharpAdbClient.Receivers.DeviceCommands; #endregion #if NET diff --git a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs index 04abfc35..250efec1 100644 --- a/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ConsoleOutputReceiver.cs @@ -35,7 +35,7 @@ public partial class ConsoleOutputReceiver(ILogger? logge /// Gets a that represents the current . /// /// A that represents the current . - public override string ToString() => output.ToString(); + public override string ToString() => TrimLines ? output.ToString().Trim() : output.ToString(); /// protected override void ThrowOnError(string line) diff --git a/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs b/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs index e09e7456..5ee94d07 100644 --- a/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs +++ b/AdvancedSharpAdbClient/Receivers/ShellOutputReceiver.cs @@ -2,8 +2,6 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // -using System.Collections.Generic; - namespace AdvancedSharpAdbClient.Receivers { /// From 8701df38cde223329601716a81ab8d540e5563c1 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Fri, 8 Dec 2023 23:04:47 +0800 Subject: [PATCH 03/12] Remove useless stream extension --- AdvancedSharpAdbClient/AdbClient.Async.cs | 14 ++- AdvancedSharpAdbClient/AdbClient.cs | 14 ++- AdvancedSharpAdbClient/Models/ShellStream.cs | 7 +- .../Polyfills/Extensions/StreamExtensions.cs | 116 +----------------- AdvancedSharpAdbClient/SyncService.Async.cs | 2 +- AdvancedSharpAdbClient/SyncService.cs | 2 +- 6 files changed, 29 insertions(+), 126 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 19d094cb..1b37462d 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -455,14 +455,17 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken byte[] buffer = new byte[32 * 1024]; int read = 0; +#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { -#if HAS_BUFFERS await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); + } #else + while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0) + { await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); -#endif } +#endif read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = @@ -616,14 +619,17 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam byte[] buffer = new byte[32 * 1024]; int read = 0; +#if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { -#if HAS_BUFFERS await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); + } #else + while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0) + { await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); -#endif } +#endif read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index adccb905..dcc9f17b 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -602,14 +602,17 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) byte[] buffer = new byte[32 * 1024]; int read = 0; +#if HAS_BUFFERS while ((read = apk.Read(buffer)) > 0) { -#if HAS_BUFFERS socket.Send(buffer.AsSpan(0, read)); + } #else + while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) + { socket.Send(buffer, read); -#endif } +#endif read = socket.Read(buffer); string value = @@ -762,14 +765,17 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s byte[] buffer = new byte[32 * 1024]; int read = 0; +#if HAS_BUFFERS while ((read = apk.Read(buffer)) > 0) { -#if HAS_BUFFERS socket.Send(buffer.AsSpan(0, read)); + } #else + while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) + { socket.Send(buffer, read); -#endif } +#endif read = socket.Read(buffer); string value = diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index e3a89064..5e533385 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Threading; namespace AdvancedSharpAdbClient.Models @@ -210,7 +211,7 @@ public override int Read(byte[] buffer, int offset, int count) } byte[] miniBuffer = new byte[1]; - int miniRead = Inner.Read(miniBuffer, 1); + int miniRead = Inner.Read(miniBuffer, 0, 1); if (miniRead == 0) { @@ -411,7 +412,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke continue; } - int miniRead = await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); + int miniRead = await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); if (miniRead == 0) { @@ -431,7 +432,7 @@ async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToke // we need to read one more byte from the inner stream. if (read > 0 && buffer[offset + read - 1] == 0x0d) { - _ = await Inner.ReadAsync(miniBuffer, 1, cancellationToken).ConfigureAwait(false); + _ = await Inner.ReadAsync(miniBuffer, 0, 1, cancellationToken).ConfigureAwait(false); int nextByte = miniBuffer[0]; if (nextByte == 0x0a) diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs index 57e545a6..83c7173c 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs @@ -1,4 +1,5 @@ -// +#if NET35 +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // @@ -13,116 +14,6 @@ namespace AdvancedSharpAdbClient.Polyfills /// public static class StreamExtensions { - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// The stream from which to read data. - /// An array of bytes. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The total number of bytes read into the buffer. This can be less than the size of the buffer if that many bytes are not currently available, - /// or zero (0) if the buffer's length is zero or the end of the stream has been reached. - public static int Read(this Stream stream, byte[] buffer) => -#if HAS_BUFFERS - stream.Read(buffer); -#else - stream.Read(buffer, 0, buffer.Length); -#endif - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// The stream from which to read data. - /// An array of bytes. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The maximum number of bytes to read. - /// The total number of bytes read into the buffer. This can be less than the size of the buffer if that many bytes are not currently available, - /// or zero (0) if the buffer's length is zero or the end of the stream has been reached. - public static int Read(this Stream stream, byte[] buffer, int count) => - stream.Read(buffer, 0, count); - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// The stream from which to read data. - /// An array of bytes. This method copies the contents of this region to the current stream. - public static void Write(this Stream stream, byte[] buffer) => -#if HAS_BUFFERS - stream.Write(buffer); -#else - stream.Write(buffer, 0, buffer.Length); -#endif - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// The stream from which to read data. - /// An array of bytes. This method copies bytes from buffer to the current stream. - /// The number of bytes to be written to the current stream. - public static void Write(this Stream stream, byte[] buffer, int count) => - stream.Write(buffer, 0, count); - -#if HAS_TASK - /// - /// Asynchronously reads a sequence of bytes from the current stream, within monitors cancellation requests. - /// - /// The stream from which to read data. - /// The buffer to write the data into. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous read operation. The value of the TResult parameter - /// contains the total number of bytes read into the buffer. The result value can be less than the number - /// of bytes requested if the number of bytes currently available is less than the requested number, - /// or it can be 0 (zero) if length of the buffer is 0 or if the end of the stream has been reached. - /// Cancelling the task will also close the stream. - public static Task ReadAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default) => -#if HAS_BUFFERS - stream.ReadAsync(buffer, cancellationToken).AsTask(); -#else - stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); -#endif - - /// - /// Asynchronously reads a sequence of bytes from the current stream, advances the position - /// within the stream by the number of bytes read, and monitors cancellation requests. - /// - /// The stream from which to read data. - /// The buffer to write the data into. - /// The maximum number of bytes to read. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous read operation. The value of the TResult parameter - /// contains the total number of bytes read into the buffer. The result value can be less than the number - /// of bytes requested if the number of bytes currently available is less than the requested number, - /// or it can be 0 (zero) if is 0 or if the end of the stream has been reached. - /// Cancelling the task will also close the stream. - public static Task ReadAsync(this Stream stream, byte[] buffer, int count, CancellationToken cancellationToken = default) => - stream.ReadAsync(buffer, 0, count, cancellationToken); - - /// - /// Asynchronously writes a sequence of bytes to the current stream, within this stream by the number of bytes written. - /// - /// The stream from which to write data. - /// The buffer to write data from. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous write operation. - /// Cancelling the task will also close the stream. - public static Task WriteAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default) => -#if HAS_BUFFERS - stream.WriteAsync(buffer, cancellationToken).AsTask(); -#else - stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken); -#endif - - /// - /// Asynchronously writes a sequence of bytes to the current stream, advances the current position - /// within this stream by the number of bytes written, and monitors cancellation requests. - /// - /// The stream from which to write data. - /// The buffer to write data from. - /// The maximum number of bytes to write. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous write operation. - /// Cancelling the task will also close the stream. - public static Task WriteAsync(this Stream stream, byte[] buffer, int count, CancellationToken cancellationToken = default) => - stream.WriteAsync(buffer, 0, count, cancellationToken); - -#if NET35 /// /// Asynchronously reads a sequence of bytes from the current stream, advances the position /// within the stream by the number of bytes read, and monitors cancellation requests. @@ -222,7 +113,6 @@ public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int return taskCompletionSource.Task; } -#endif -#endif } } +#endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 27677b60..5acffb50 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -186,7 +186,7 @@ public virtual async Task PullAsync(string remoteFilePath, Stream stream, IProgr await stream.WriteAsync(buffer.AsMemory(0, size), cancellationToken).ConfigureAwait(false); #else await Socket.ReadAsync(buffer, size, cancellationToken).ConfigureAwait(false); - await stream.WriteAsync(buffer, size, cancellationToken).ConfigureAwait(false); + await stream.WriteAsync(buffer, 0, size, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += size; diff --git a/AdvancedSharpAdbClient/SyncService.cs b/AdvancedSharpAdbClient/SyncService.cs index 06465a80..38f7a119 100644 --- a/AdvancedSharpAdbClient/SyncService.cs +++ b/AdvancedSharpAdbClient/SyncService.cs @@ -273,7 +273,7 @@ public virtual void Pull(string remoteFilePath, Stream stream, IProgress Date: Fri, 8 Dec 2023 23:45:44 +0800 Subject: [PATCH 04/12] Add more extension of DeviceExtensions --- .../DeviceCommands/DeviceClientTexts.Async.cs | 15 +++ .../DeviceCommands/DeviceClientTexts.cs | 15 +++ .../DeviceCommands/DeviceClient.Async.cs | 29 ++++- .../DeviceCommands/DeviceClient.cs | 27 +++- .../DeviceCommands/DeviceExtensions.Async.cs | 120 +++++++++++++++++- .../DeviceCommands/DeviceExtensions.cs | 106 +++++++++++++++- AdvancedSharpAdbClient/Models/ShellStream.cs | 1 - 7 files changed, 304 insertions(+), 9 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs index 2516cc7b..95521865 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs @@ -218,6 +218,21 @@ public async void SwipeAsyncTest() Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); } + /// + /// Tests the method. + /// + [Fact] + public async void SwipePointAsyncTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + await new DeviceClient(client, Device).SwipeAsync(new Point(100, 200), new Point(300, 400), 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs index cd5e348b..08544aec 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs @@ -232,6 +232,21 @@ public void SwipeTest() Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); } + /// + /// Tests the method. + /// + [Fact] + public void SwipePointTest() + { + DummyAdbClient client = new(); + client.Commands["shell:input swipe 100 200 300 400 500"] = string.Empty; + + new DeviceClient(client, Device).Swipe(new Point(100, 200), new Point(300, 400), 500); + + Assert.Single(client.ReceivedCommands); + Assert.Equal("shell:input swipe 100 200 300 400 500", client.ReceivedCommands[0]); + } + /// /// Tests the method. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs index dba0cb40..c5f4ae54 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs @@ -125,7 +125,7 @@ public async Task ClickAsync(int x, int y, CancellationToken cancellationToken = } /// - /// Generates a swipe gesture from first element to second element Specify the speed in ms. + /// Generates a swipe gesture from first element to second element. Specify the speed in ms. /// /// The start element. /// The end element. @@ -150,7 +150,32 @@ public async Task SwipeAsync(Element first, Element second, long speed, Cancella } /// - /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. + /// Generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. + /// + /// The start element. + /// The end element. + /// The time spent in swiping. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public async Task SwipeAsync(Point first, Point second, long speed, CancellationToken cancellationToken = default) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {first.X} {first.Y} {second.X} {second.Y} {speed}", receiver, cancellationToken).ConfigureAwait(false); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from co-ordinates [x1, y1] to [x2, y2] with speed. Specify the speed in ms. /// /// The start X co-ordinate. /// The start Y co-ordinate. diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs index 82c2a6fc..0c5185ee 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs @@ -147,7 +147,7 @@ public void Click(int x, int y) } /// - /// Generates a swipe gesture from first element to second element Specify the speed in ms. + /// Generates a swipe gesture from first element to second element. Specify the speed in ms. /// /// The start element. /// The end element. @@ -170,7 +170,30 @@ public void Swipe(Element first, Element second, long speed) } /// - /// Generates a swipe gesture from co-ordinates x1,y1 to x2,y2 with speed Specify the speed in ms. + /// Generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. + /// + /// The start . + /// The end . + /// The time spent in swiping. + public void Swipe(Point first, Point second, long speed) + { + ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; + AdbClient.ExecuteShellCommand(Device, $"input swipe {first.X} {first.Y} {second.X} {second.Y} {speed}", receiver); + + string result = receiver.ToString().Trim(); + + if (result.StartsWith("java.lang.")) + { + throw JavaException.Parse(result); + } + else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR + { + throw new ElementNotFoundException("Coordinates of element is invalid"); + } + } + + /// + /// Generates a swipe gesture from co-ordinates [x1, y1] to [x2, y2] with speed. Specify the speed in ms. /// /// The start X co-ordinate. /// The start Y co-ordinate. diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 61dbc968..434f8b48 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text; using System.Threading; +using System.Xml; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -37,6 +38,97 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver, CancellationToken cancellationToken = default) => client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); + /// + /// Gets the current device screen snapshot asynchronously. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// A which can be used to cancel the asynchronous operation. + /// A which return a containing current hierarchy. + public static Task DumpScreenAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).DumpScreenAsync(cancellationToken); + + /// + /// Clicks on the specified coordinates. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The to click. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task ClickAsync(this IAdbClient client, DeviceData device, Point cords, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).ClickAsync(cords, cancellationToken); + + /// + /// Generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The start element. + /// The end element. + /// The time spent in swiping. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task SwipeAsync(this IAdbClient client, DeviceData device, Point first, Point second, long speed, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).SwipeAsync(first, second, speed, cancellationToken); + + /// + /// Get the of the app. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The package name of the app to check. + /// A which can be used to cancel the asynchronous operation. + /// A which return the of the app. Foreground, stopped or running in background. + public static Task GetAppStatusAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).GetAppStatusAsync(packageName, cancellationToken); + + /// + /// Get element by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The xpath of the elements. + /// A which can be used to cancel the asynchronous operation. + /// Only check once if . Or it will continue check until is . + /// A which return the of . + public static Task FindElementAsync(this IAdbClient client, DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) => + new DeviceClient(client, device).FindElementAsync(xpath, cancellationToken); + + /// + /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The xpath of the elements. + /// A which can be used to cancel the asynchronous operation. + /// Only check once if . Or it will continue check until is . + /// A which return the of has got. + public static Task> FindElementsAsync(this IAdbClient client, DeviceData device, string xpath = "hierarchy/node", CancellationToken cancellationToken = default) => + new DeviceClient(client, device).FindElementsAsync(xpath, cancellationToken); + + /// + /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The key event to send. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task SendKeyEventAsync(this IAdbClient client, DeviceData device, string key, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).SendKeyEventAsync(key, cancellationToken); + + /// + /// Send text to device. Doesn't support Russian. + /// + /// The to use when executing the command. + /// The device on which to run the command. + /// The text to send. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task SendTextAsync(this IAdbClient client, DeviceData device, string text, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).SendTextAsync(text, cancellationToken); + /// /// Clear the input text. The input should be in focus. Use if the element isn't focused. /// @@ -59,7 +151,8 @@ public static async Task ClearInputAsync(this IAdbClient client, DeviceData devi /// The device on which to click BACK button. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_BACK", cancellationToken); + public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_BACK", cancellationToken); /// /// Click HOME button. @@ -68,7 +161,30 @@ public static async Task ClearInputAsync(this IAdbClient client, DeviceData devi /// The device on which to click HOME button. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_HOME", cancellationToken); + public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => + new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_HOME", cancellationToken); + + /// + /// Start an Android application on device. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// The package name of the application to start. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task StartAppAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken = default) => + client.ExecuteShellCommandAsync(device, $"monkey -p {packageName} 1", cancellationToken); + + /// + /// Stop an Android application on device. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// The package name of the application to stop. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + public static Task StopAppAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken = default) => + client.ExecuteShellCommandAsync(device, $"am force-stop {packageName}", cancellationToken); /// /// Gets the file statistics of a given file. diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 3da7bbd5..986f7938 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Xml; namespace AdvancedSharpAdbClient.DeviceCommands { @@ -35,6 +36,87 @@ public static void ExecuteShellCommand(this IAdbClient client, DeviceData device public static void ExecuteShellCommand(this IAdbClient client, DeviceData device, string command, IShellOutputReceiver receiver) => client.ExecuteRemoteCommand(command, device, receiver, AdbClient.Encoding); + /// + /// Gets the current device screen snapshot. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// A containing current hierarchy. + public static XmlDocument? DumpScreen(this IAdbClient client, DeviceData device) => + new DeviceClient(client, device).DumpScreen(); + + /// + /// Clicks on the specified coordinates. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The to click. + public static void Click(this IAdbClient client, DeviceData device, Point cords) => + new DeviceClient(client, device).Click(cords); + + /// + /// Generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The start . + /// The end . + /// The time spent in swiping. + public static void Swipe(this IAdbClient client, DeviceData device, Point first, Point second, long speed) => + new DeviceClient(client, device).Swipe(first, second, speed); + + /// + /// Get the of the app. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The package name of the app to check. + /// The of the app. Foreground, stopped or running in background. + public static AppStatus GetAppStatus(this IAdbClient client, DeviceData device, string packageName) => + new DeviceClient(client, device).GetAppStatus(packageName); + + /// + /// Get element by xpath. You can specify the waiting time in timeout. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The xpath of the element. + /// The timeout for waiting the element. + /// Only check once if or . + /// The of . + public static Element? FindElement(this IAdbClient client, DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) => + new DeviceClient(client, device).FindElement(xpath, timeout); + + /// + /// Get elements by xpath. You can specify the waiting time in timeout. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The xpath of the elements. + /// The timeout for waiting the elements. + /// Only check once if or . + /// The of has got. + public static IEnumerable FindElements(this IAdbClient client, DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default) => + new DeviceClient(client, device).FindElements(xpath, timeout); + + /// + /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The key event to send. + public static void SendKeyEvent(this IAdbClient client, DeviceData device, string key) => + new DeviceClient(client, device).SendKeyEvent(key); + + /// + /// Send text to device. Doesn't support Russian. + /// + /// An instance of a class that implements the interface. + /// The device on which to clear the input text. + /// The text to send. + public static void SendText(this IAdbClient client, DeviceData device, string text) => + new DeviceClient(client, device).SendText(text); + /// /// Clear the input text. The input should be in focus. Use if the element isn't focused. /// @@ -53,14 +135,34 @@ public static void ClearInput(this IAdbClient client, DeviceData device, int cha /// /// An instance of a class that implements the interface. /// The device on which to click BACK button. - public static void ClickBackButton(this IAdbClient client, DeviceData device) => new DeviceClient(client, device).SendKeyEvent("KEYCODE_BACK"); + public static void ClickBackButton(this IAdbClient client, DeviceData device) => + new DeviceClient(client, device).SendKeyEvent("KEYCODE_BACK"); /// /// Click HOME button. /// /// An instance of a class that implements the interface. /// The device on which to click HOME button. - public static void ClickHomeButton(this IAdbClient client, DeviceData device) => new DeviceClient(client, device).SendKeyEvent("KEYCODE_HOME"); + public static void ClickHomeButton(this IAdbClient client, DeviceData device) => + new DeviceClient(client, device).SendKeyEvent("KEYCODE_HOME"); + + /// + /// Start an Android application on device. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// The package name of the application to start. + public static void StartApp(this IAdbClient client, DeviceData device, string packageName) => + client.ExecuteShellCommand(device, $"monkey -p {packageName} 1"); + + /// + /// Stop an Android application on device. + /// + /// An instance of a class that implements the interface. + /// The device on which to click HOME button. + /// The package name of the application to stop. + public static void StopApp(this IAdbClient client, DeviceData device, string packageName) => + client.ExecuteShellCommand(device, $"am force-stop {packageName}"); /// /// Gets the file statistics of a given file. diff --git a/AdvancedSharpAdbClient/Models/ShellStream.cs b/AdvancedSharpAdbClient/Models/ShellStream.cs index 5e533385..2fe5a3f3 100644 --- a/AdvancedSharpAdbClient/Models/ShellStream.cs +++ b/AdvancedSharpAdbClient/Models/ShellStream.cs @@ -5,7 +5,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Threading; namespace AdvancedSharpAdbClient.Models From 4ef49fc76530453cc4eabf8a70119fe27a1bcf3a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 9 Dec 2023 00:18:37 +0800 Subject: [PATCH 05/12] Add `Asynchronously` in async methods summary --- .../AdbCommandLineClient.Async.cs | 10 +- AdvancedSharpAdbClient/AdbSocket.Async.cs | 14 +-- .../DeviceCommands/DeviceExtensions.Async.cs | 50 +++++----- .../DeviceCommands/Models/Element.cs | 8 +- .../DeviceCommands/PackageManager.Async.cs | 28 +++--- AdvancedSharpAdbClient/DeviceMonitor.Async.cs | 4 +- .../Extensions/AdbClientExtensions.Async.cs | 42 ++++---- .../Interfaces/IAdbClient.Async.cs | 59 ++++++------ .../Interfaces/IAdbCommandLineClient.Async.cs | 4 +- .../Interfaces/IAdbServer.Async.cs | 11 ++- .../Interfaces/IAdbSocket.Async.cs | 28 +++--- .../Interfaces/IDeviceMonitor.Async.cs | 4 +- .../Interfaces/ISyncService.Async.cs | 14 +-- .../Interfaces/ITcpSocket.Async.cs | 10 +- .../Logs/LogReader.Async.cs | 2 +- .../Extensions/EnumerableExtensions.cs | 4 +- AdvancedSharpAdbClient/SyncService.Async.cs | 3 +- AdvancedSharpAdbClient/TcpSocket.Async.cs | 2 +- README.md | 95 +++++++++---------- 19 files changed, 194 insertions(+), 198 deletions(-) diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index 05906f5f..f3c1dd28 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -15,7 +15,7 @@ namespace AdvancedSharpAdbClient public partial class AdbCommandLineClient { /// - /// Queries adb for its version number and checks it against . + /// Asynchronously queries adb for its version number and checks it against . /// /// A which can be used to cancel the asynchronous operation. /// A which return a object that contains the version number of the Android Command Line client. @@ -38,7 +38,7 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio } /// - /// Starts the adb server by running the adb start-server command. + /// Asynchronously starts the adb server by running the adb start-server command. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. @@ -82,7 +82,7 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = } /// - /// Runs the adb.exe process, invoking a specific , + /// Asynchronously runs the adb.exe process, invoking a specific , /// and reads the standard output and standard error output. /// /// The adb.exe command to invoke, such as version or start-server. @@ -102,7 +102,7 @@ protected virtual async Task RunAdbProcessAsync(string command, ICollection - /// Runs the adb.exe process, invoking a specific , + /// Asynchronously runs the adb.exe process, invoking a specific , /// and reads the standard output and standard error output. /// /// The adb.exe command to invoke, such as version or start-server. @@ -121,7 +121,7 @@ protected virtual async Task RunAdbProcessInnerAsync(string command, IColle } /// - /// Runs process, invoking a specific command, and reads the standard output and standard error output. + /// Asynchronously runs process, invoking a specific command, and reads the standard output and standard error output. /// /// The return code of the process. #if !HAS_PROCESS diff --git a/AdvancedSharpAdbClient/AdbSocket.Async.cs b/AdvancedSharpAdbClient/AdbSocket.Async.cs index a079fcbf..d775aa2c 100644 --- a/AdvancedSharpAdbClient/AdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/AdbSocket.Async.cs @@ -14,7 +14,7 @@ namespace AdvancedSharpAdbClient public partial class AdbSocket { /// - /// Reconnects the to the same endpoint it was initially connected to. + /// Asynchronously reconnects the to the same endpoint it was initially connected to. /// Use this when the socket was disconnected by adb and you have restarted adb. /// /// A which can be used to cancel the asynchronous task. @@ -354,11 +354,11 @@ public virtual async ValueTask ReadAsync(Memory data, CancellationTok #endif /// - /// Write until all data in "data" is written or the connection fails or times out. + /// Asynchronously write until all data in "data" is written or the connection fails or times out. /// /// The data to send. /// A which can be used to cancel the asynchronous task. - /// Returns if all data was written; otherwise, . + /// A which return if all data was written; otherwise, . /// This uses the default time out value. protected virtual async Task WriteAsync(byte[] data, CancellationToken cancellationToken = default) { @@ -377,11 +377,11 @@ protected virtual async Task WriteAsync(byte[] data, CancellationToken can #if HAS_BUFFERS /// - /// Write until all data in "data" is written or the connection fails or times out. + /// Asynchronously write until all data in "data" is written or the connection fails or times out. /// /// The data to send. /// A which can be used to cancel the asynchronous task. - /// Returns if all data was written; otherwise, . + /// A which return if all data was written; otherwise, . /// This uses the default time out value. protected virtual async ValueTask WriteAsync(Memory data, CancellationToken cancellationToken = default) { @@ -400,10 +400,10 @@ protected virtual async ValueTask WriteAsync(Memory data, Cancellati #endif /// - /// Reads the response from ADB after a command. + /// Asynchronously reads the response from ADB after a command. /// /// A which can be used to cancel the asynchronous task. - /// A that represents the response received from ADB. + /// A which return a that represents the response received from ADB. protected virtual async Task ReadAdbResponseInnerAsync(CancellationToken cancellationToken = default) { byte[] reply = new byte[4]; diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index 434f8b48..aa570fc8 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -16,7 +16,7 @@ namespace AdvancedSharpAdbClient.DeviceCommands public static partial class DeviceExtensions { /// - /// Executes a shell command on the device. + /// Asynchronously executes a shell command on the device. /// /// The to use when executing the command. /// The device on which to run the command. @@ -27,7 +27,7 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d client.ExecuteRemoteCommandAsync(command, device, AdbClient.Encoding, cancellationToken); /// - /// Executes a shell command on the device. + /// Asynchronously executes a shell command on the device. /// /// The to use when executing the command. /// The device on which to run the command. @@ -39,7 +39,7 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken); /// - /// Gets the current device screen snapshot asynchronously. + /// Asynchronously gets the current device screen snapshot asynchronously. /// /// The to use when executing the command. /// The device on which to run the command. @@ -49,7 +49,7 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d new DeviceClient(client, device).DumpScreenAsync(cancellationToken); /// - /// Clicks on the specified coordinates. + /// Asynchronously clicks on the specified coordinates. /// /// The to use when executing the command. /// The device on which to run the command. @@ -60,7 +60,7 @@ public static Task ClickAsync(this IAdbClient client, DeviceData device, Point c new DeviceClient(client, device).ClickAsync(cords, cancellationToken); /// - /// Generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. + /// Asynchronously generates a swipe gesture from first coordinates to second coordinates. Specify the speed in ms. /// /// The to use when executing the command. /// The device on which to run the command. @@ -73,7 +73,7 @@ public static Task SwipeAsync(this IAdbClient client, DeviceData device, Point f new DeviceClient(client, device).SwipeAsync(first, second, speed, cancellationToken); /// - /// Get the of the app. + /// Asynchronously get the of the app. /// /// The to use when executing the command. /// The device on which to run the command. @@ -84,7 +84,7 @@ public static Task GetAppStatusAsync(this IAdbClient client, DeviceDa new DeviceClient(client, device).GetAppStatusAsync(packageName, cancellationToken); /// - /// Get element by xpath asynchronously. You can specify the waiting time in timeout. + /// Asynchronously get element by xpath asynchronously. You can specify the waiting time in timeout. /// /// The to use when executing the command. /// The device on which to run the command. @@ -96,7 +96,7 @@ public static Task GetAppStatusAsync(this IAdbClient client, DeviceDa new DeviceClient(client, device).FindElementAsync(xpath, cancellationToken); /// - /// Get elements by xpath asynchronously. You can specify the waiting time in timeout. + /// Asynchronously get elements by xpath asynchronously. You can specify the waiting time in timeout. /// /// The to use when executing the command. /// The device on which to run the command. @@ -108,7 +108,7 @@ public static Task> FindElementsAsync(this IAdbClient clien new DeviceClient(client, device).FindElementsAsync(xpath, cancellationToken); /// - /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. + /// Asynchronously send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. /// /// The to use when executing the command. /// The device on which to run the command. @@ -119,7 +119,7 @@ public static Task SendKeyEventAsync(this IAdbClient client, DeviceData device, new DeviceClient(client, device).SendKeyEventAsync(key, cancellationToken); /// - /// Send text to device. Doesn't support Russian. + /// Asynchronously send text to device. Doesn't support Russian. /// /// The to use when executing the command. /// The device on which to run the command. @@ -130,7 +130,7 @@ public static Task SendTextAsync(this IAdbClient client, DeviceData device, stri new DeviceClient(client, device).SendTextAsync(text, cancellationToken); /// - /// Clear the input text. The input should be in focus. Use if the element isn't focused. + /// Asynchronously clear the input text. The input should be in focus. Use if the element isn't focused. /// /// An instance of a class that implements the interface. /// The device on which to clear the input text. @@ -145,7 +145,7 @@ public static async Task ClearInputAsync(this IAdbClient client, DeviceData devi } /// - /// Click BACK button. + /// Asynchronously click BACK button. /// /// An instance of a class that implements the interface. /// The device on which to click BACK button. @@ -155,7 +155,7 @@ public static Task ClickBackButtonAsync(this IAdbClient client, DeviceData devic new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_BACK", cancellationToken); /// - /// Click HOME button. + /// Asynchronously click HOME button. /// /// An instance of a class that implements the interface. /// The device on which to click HOME button. @@ -165,7 +165,7 @@ public static Task ClickHomeButtonAsync(this IAdbClient client, DeviceData devic new DeviceClient(client, device).SendKeyEventAsync("KEYCODE_HOME", cancellationToken); /// - /// Start an Android application on device. + /// Asynchronously start an Android application on device. /// /// An instance of a class that implements the interface. /// The device on which to click HOME button. @@ -176,7 +176,7 @@ public static Task StartAppAsync(this IAdbClient client, DeviceData device, stri client.ExecuteShellCommandAsync(device, $"monkey -p {packageName} 1", cancellationToken); /// - /// Stop an Android application on device. + /// Asynchronously stop an Android application on device. /// /// An instance of a class that implements the interface. /// The device on which to click HOME button. @@ -187,7 +187,7 @@ public static Task StopAppAsync(this IAdbClient client, DeviceData device, strin client.ExecuteShellCommandAsync(device, $"am force-stop {packageName}", cancellationToken); /// - /// Gets the file statistics of a given file. + /// Asynchronously gets the file statistics of a given file. /// /// The to use when executing the command. /// The device on which to look for the file. @@ -201,7 +201,7 @@ public static async Task StatAsync(this IAdbClient client, Devic } /// - /// Lists the contents of a directory on the device. + /// Asynchronously lists the contents of a directory on the device. /// /// The to use when executing the command. /// The device on which to list the directory. @@ -215,7 +215,7 @@ public static async Task> ListAsync(this IAdbClient client, } /// - /// Pulls (downloads) a file from the remote device. + /// Asynchronously pulls (downloads) a file from the remote device. /// /// The to use when executing the command. /// The device on which to pull the file. @@ -233,7 +233,7 @@ public static async Task PullAsync(this IAdbClient client, DeviceData device, } /// - /// Pushes (uploads) a file to the remote device. + /// Asynchronously pushes (uploads) a file to the remote device. /// /// The to use when executing the command. /// The device on which to put the file. @@ -253,7 +253,7 @@ public static async Task PushAsync(this IAdbClient client, DeviceData device, } /// - /// Gets the property of a device. + /// Asynchronously gets the property of a device. /// /// The connection to the adb server. /// The device for which to get the property. @@ -268,7 +268,7 @@ public static async Task GetPropertyAsync(this IAdbClient client, Device } /// - /// Gets the properties of a device. + /// Asynchronously gets the properties of a device. /// /// The connection to the adb server. /// The device for which to list the properties. @@ -282,7 +282,7 @@ public static async Task> GetPropertiesAsync(this IAd } /// - /// Gets the environment variables currently defined on a device. + /// Asynchronously gets the environment variables currently defined on a device. /// /// The connection to the adb server. /// The device for which to list the environment variables. @@ -296,7 +296,7 @@ public static async Task> GetEnvironmentVariablesAsyn } /// - /// Uninstalls a package from the device. + /// Asynchronously uninstalls a package from the device. /// /// The connection to the adb server. /// The device on which to uninstall the package. @@ -310,7 +310,7 @@ public static Task UninstallPackageAsync(this IAdbClient client, DeviceData devi } /// - /// Requests the version information from the device. + /// Asynchronously requests the version information from the device. /// /// The connection to the adb server. /// The device on which to uninstall the package. @@ -324,7 +324,7 @@ public static Task GetPackageVersionAsync(this IAdbClient client, D } /// - /// Lists all processes running on the device. + /// Asynchronously lists all processes running on the device. /// /// A connection to ADB. /// The device on which to list the processes that are running. diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs index c9a6bb4d..532e4142 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs @@ -280,7 +280,7 @@ public void ClearInput(int charCount) #if HAS_TASK /// - /// Clicks on this coordinates. + /// Asynchronously clicks on this coordinates. /// /// A which can be used to cancel the asynchronous operation. public async Task ClickAsync(CancellationToken cancellationToken = default) @@ -301,7 +301,7 @@ public async Task ClickAsync(CancellationToken cancellationToken = default) } /// - /// Send text to device. Doesn't support Russian. + /// Asynchronously send text to device. Doesn't support Russian. /// /// The text to send. /// A which can be used to cancel the asynchronous operation. @@ -326,7 +326,7 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken } /// - /// Clear the input text. Use if the element is focused. + /// Asynchronously clear the input text. Use if the element is focused. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. @@ -335,7 +335,7 @@ public Task ClearInputAsync(CancellationToken cancellationToken = default) => ClearInputAsync(Text!.Length, cancellationToken); /// - /// Clear the input text. Use if the element is focused. + /// Asynchronously clear the input text. Use if the element is focused. /// /// The length of text to clear. /// A which can be used to cancel the asynchronous operation. diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 1f157221..348588c0 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -15,7 +15,7 @@ namespace AdvancedSharpAdbClient.DeviceCommands public partial class PackageManager { /// - /// Refreshes the packages. + /// Asynchronously refreshes the packages. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. @@ -39,7 +39,7 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d } /// - /// Installs an Android application on device. + /// Asynchronously installs an Android application on device. /// /// The absolute file system path to file on local host to install. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. @@ -65,7 +65,7 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => } /// - /// Installs the application package that was pushed to a temporary location on the device. + /// Asynchronously installs the application package that was pushed to a temporary location on the device. /// /// absolute file path to package file on device. /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. @@ -101,7 +101,7 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, IProg } /// - /// Installs Android multiple application on device. + /// Asynchronously installs Android multiple application on device. /// /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. @@ -174,7 +174,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => } /// - /// Installs Android multiple application on device. + /// Asynchronously installs Android multiple application on device. /// /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. @@ -239,7 +239,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => } /// - /// Installs the multiple application package that was pushed to a temporary location on the device. + /// Asynchronously installs the multiple application package that was pushed to a temporary location on the device. /// /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. @@ -287,7 +287,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) } /// - /// Installs the multiple application package that was pushed to a temporary location on the device. + /// Asynchronously installs the multiple application package that was pushed to a temporary location on the device. /// /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. @@ -331,7 +331,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) } /// - /// Uninstalls a package from the device. + /// Asynchronously ininstalls a package from the device. /// /// The name of the package to uninstall. /// The arguments to pass to pm uninstall. @@ -340,7 +340,7 @@ public Task UninstallPackageAsync(string packageName, params string[] arguments) UninstallPackageAsync(packageName, default, arguments); /// - /// Uninstalls a package from the device. + /// Asynchronously uninstalls a package from the device. /// /// The name of the package to uninstall. /// A which can be used to cancel the asynchronous operation. @@ -372,7 +372,7 @@ public virtual async Task UninstallPackageAsync(string packageName, Cancellation } /// - /// Requests the version information from the device. + /// Asynchronously requests the version information from the device. /// /// The name of the package from which to get the application version. /// A which can be used to cancel the asynchronous operation. @@ -387,7 +387,7 @@ public virtual async Task GetVersionInfoAsync(string packageName, C } /// - /// Pushes a file to device + /// Asynchronously pushes a file to device /// /// The absolute path to file on local host. /// An optional parameter which, when specified, returns progress notifications. @@ -440,7 +440,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa } /// - /// Remove a file from device. + /// Asynchronously remove a file from device. /// /// Path on device of file to remove. /// A which can be used to cancel the asynchronous operation. @@ -461,7 +461,7 @@ protected virtual async Task RemoveRemotePackageAsync(string remoteFilePath, Can } /// - /// Like "install", but starts an install session. + /// Like "install", but starts an install session asynchronously. /// /// The absolute package name of the base app. /// A which can be used to cancel the asynchronous operation. @@ -503,7 +503,7 @@ protected virtual async Task CreateInstallSessionAsync(string? packageNa } /// - /// Write an apk into the given install session. + /// Asynchronously write an apk into the given install session. /// /// The session ID of the install session. /// The name of the application. diff --git a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs index 1f6f6b2a..23edc8e7 100644 --- a/AdvancedSharpAdbClient/DeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/DeviceMonitor.Async.cs @@ -45,7 +45,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau } /// - /// Stops the monitoring + /// Asynchronously stops the monitoring /// protected virtual async Task DisposeAsyncCore() { @@ -102,7 +102,7 @@ public async Task DisposeAsync() #endif /// - /// Monitors the devices. This connects to the Debug Bridge + /// Asynchronously monitors the devices. This connects to the Debug Bridge /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs index b17914a8..a562a3e5 100644 --- a/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs +++ b/AdvancedSharpAdbClient/Extensions/AdbClientExtensions.Async.cs @@ -15,7 +15,7 @@ namespace AdvancedSharpAdbClient public static partial class AdbClientExtensions { /// - /// Asks the ADB server to forward local connections from + /// Asynchronously asks the ADB server to forward local connections from /// to the address on the . /// /// An instance of a class that implements the interface. @@ -32,7 +32,7 @@ public static Task CreateForwardAsync(this IAdbClient client, DeviceData de client.CreateForwardAsync(device, local.ToString(), remote.ToString(), allowRebind, cancellationToken); /// - /// Creates a port forwarding between a local and a remote port. + /// Asynchronously creates a port forwarding between a local and a remote port. /// /// An instance of a class that implements the interface. /// The device to which to forward the connections. @@ -47,7 +47,7 @@ public static Task CreateForwardAsync(this IAdbClient client, DeviceData de client.CreateForwardAsync(device, $"tcp:{localPort}", $"tcp:{remotePort}", true, cancellationToken); /// - /// Forwards a remote Unix socket to a local TCP socket. + /// Asynchronously forwards a remote Unix socket to a local TCP socket. /// /// An instance of a class that implements the interface. /// The device to which to forward the connections. @@ -63,7 +63,7 @@ public static Task CreateForwardAsync(this IAdbClient client, DeviceData de client.CreateForwardAsync(device, $"tcp:{localPort}", $"local:{remoteSocket}", true, cancellationToken); /// - /// Asks the ADB server to reverse forward local connections from + /// Asynchronously asks the ADB server to reverse forward local connections from /// to the address on the . /// /// An instance of a class that implements the interface. @@ -80,7 +80,7 @@ public static Task CreateReverseForwardAsync(this IAdbClient client, Device client.CreateReverseForwardAsync(device, remote.ToString(), local.ToString(), allowRebind, cancellationToken); /// - /// Remove a reverse port forwarding between a remote and a local port. + /// Asynchronously remove a reverse port forwarding between a remote and a local port. /// /// An instance of a class that implements the interface. /// The device on which to remove the reverse port forwarding @@ -91,7 +91,7 @@ public static Task RemoveReverseForwardAsync(this IAdbClient client, DeviceData client.RemoveReverseForwardAsync(device, remote.ToString(), cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// An instance of a class that implements the interface. /// The target of command, such as shell, remount, dev, tcp, local, @@ -104,7 +104,7 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ client.ExecuteServerCommandAsync(target, command, receiver, AdbClient.Encoding, cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// An instance of a class that implements the interface. /// The target of command, such as shell, remount, dev, tcp, local, @@ -117,7 +117,7 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ client.ExecuteServerCommandAsync(target, command, socket, AdbClient.Encoding, cancellationToken); /// - /// Executes a command on the device. + /// Asynchronously executes a command on the device. /// /// An instance of a class that implements the interface. /// The command to execute. @@ -128,7 +128,7 @@ public static Task ExecuteRemoteCommandAsync(this IAdbClient client, string comm client.ExecuteRemoteCommandAsync(command, device, AdbClient.Encoding, cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// An instance of a class that implements the interface. /// The target of command, such as shell, remount, dev, tcp, local, @@ -140,7 +140,7 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ client.ExecuteServerCommandAsync(target, command, AdbClient.Encoding, cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// An instance of a class that implements the interface. /// The target of command, such as shell, remount, dev, tcp, local, @@ -154,7 +154,7 @@ public static Task ExecuteServerCommandAsync(this IAdbClient client, string targ client.ExecuteServerCommandAsync(target, command, socket, receiver, AdbClient.Encoding, cancellationToken); /// - /// Executes a command on the device. + /// Asynchronously executes a command on the device. /// /// An instance of a class that implements the interface. /// The command to execute. @@ -177,7 +177,7 @@ public static Task RunLogServiceAsync(this IAdbClient client, DeviceData device, client.RunLogServiceAsync(device, messageSink, default, logNames); /// - /// Reboots the specified adb socket address. + /// Asynchronously reboots the specified adb socket address. /// /// An instance of a class that implements the interface. /// The device. @@ -186,7 +186,7 @@ public static Task RunLogServiceAsync(this IAdbClient client, DeviceData device, public static Task RebootAsync(this IAdbClient client, DeviceData device, CancellationToken cancellationToken = default) => client.RebootAsync(string.Empty, device, cancellationToken); /// - /// Pair with a device for secure TCP/IP communication. + /// Asynchronously pair with a device for secure TCP/IP communication. /// /// An instance of a class that implements the interface. /// The IP address of the remote device. @@ -199,7 +199,7 @@ public static Task PairAsync(this IAdbClient client, IPAddress address, : client.PairAsync(new IPEndPoint(address, AdbClient.DefaultPort), code, cancellationToken); /// - /// Pair with a device for secure TCP/IP communication. + /// Asynchronously pair with a device for secure TCP/IP communication. /// /// An instance of a class that implements the interface. /// The DNS endpoint at which the adb server on the device is running. @@ -212,7 +212,7 @@ public static Task PairAsync(this IAdbClient client, IPEndPoint endpoint : client.PairAsync(new DnsEndPoint(endpoint.Address.ToString(), endpoint.Port), code, cancellationToken); /// - /// Pair with a device for secure TCP/IP communication. + /// Asynchronously pair with a device for secure TCP/IP communication. /// /// An instance of a class that implements the interface. /// The host address of the remote device. @@ -223,7 +223,7 @@ public static Task PairAsync(this IAdbClient client, string host, string client.PairAsync(Extensions.CreateDnsEndPoint(host, AdbClient.DefaultPort), code, cancellationToken); /// - /// Pair with a device for secure TCP/IP communication. + /// Asynchronously pair with a device for secure TCP/IP communication. /// /// An instance of a class that implements the interface. /// The host address of the remote device. @@ -235,7 +235,7 @@ public static Task PairAsync(this IAdbClient client, string host, int po client.PairAsync(Extensions.CreateDnsEndPoint(host, port), code, cancellationToken); /// - /// Connect to a device via TCP/IP. + /// Asynchronously connect to a device via TCP/IP. /// /// An instance of a class that implements the interface. /// The IP address of the remote device. @@ -247,7 +247,7 @@ public static Task ConnectAsync(this IAdbClient client, IPAddress addres : client.ConnectAsync(new IPEndPoint(address, AdbClient.DefaultPort), cancellationToken); /// - /// Connect to a device via TCP/IP. + /// Asynchronously connect to a device via TCP/IP. /// /// An instance of a class that implements the interface. /// The IP endpoint at which the adb server on the device is running. @@ -259,7 +259,7 @@ public static Task ConnectAsync(this IAdbClient client, IPEndPoint endpo : client.ConnectAsync(new DnsEndPoint(endpoint.Address.ToString(), endpoint.Port), cancellationToken); /// - /// Connect to a device via TCP/IP. + /// Asynchronously connect to a device via TCP/IP. /// /// An instance of a class that implements the interface. /// The host address of the remote device. @@ -304,7 +304,7 @@ public static Task InstallMultipleAsync(this IAdbClient client, DeviceData devic client.InstallMultipleAsync(device, baseAPK, splitAPKs, default, arguments); /// - /// Like "install", but starts an install session. + /// Like "install", but starts an install session ssynchronously. /// /// An instance of a class that implements the interface. /// The device on which to install the application. @@ -315,7 +315,7 @@ public static Task InstallCreateAsync(this IAdbClient client, DeviceData client.InstallCreateAsync(device, packageName, default, arguments); /// - /// Uninstalls an Android application on an device. + /// Asynchronously uninstalls an Android application on an device. /// /// An instance of a class that implements the interface. /// The device on which to install the application. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 120065df..5163d153 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -15,30 +15,29 @@ namespace AdvancedSharpAdbClient public partial interface IAdbClient { /// - /// Ask the ADB server for its internal version number. + /// Asynchronously ask the ADB server for its internal version number. /// /// A which can be used to cancel the asynchronous operation. /// A which return the ADB version number. Task GetAdbVersionAsync(CancellationToken cancellationToken); /// - /// Ask the ADB server to quit immediately. This is used when the - /// ADB client detects that an obsolete server is running after an - /// upgrade. + /// Asynchronously ask the ADB server to quit immediately. This is used when the + /// ADB client detects that an obsolete server is running after an upgrade. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. Task KillAdbAsync(CancellationToken cancellationToken); /// - /// Gets the devices that are available for communication. + /// Asynchronously gets the devices that are available for communication. /// /// A which can be used to cancel the asynchronous operation. /// A which return the list of devices that are connected. Task> GetDevicesAsync(CancellationToken cancellationToken); /// - /// Asks the ADB server to forward local connections from + /// Asynchronously asks the ADB server to forward local connections from /// to the address on the . /// /// The device on which to forward the connections. @@ -76,7 +75,7 @@ public partial interface IAdbClient Task CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken); /// - /// Asks the ADB server to reverse forward local connections from + /// Asynchronously asks the ADB server to reverse forward local connections from /// to the address on the . /// /// The device on which to reverse forward the connections. @@ -114,7 +113,7 @@ public partial interface IAdbClient Task CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken); /// - /// Remove a reverse port forwarding between a remote and a local port. + /// Asynchronously remove a reverse port forwarding between a remote and a local port. /// /// The device on which to remove the reverse port forwarding /// Specification of the remote that was forwarded @@ -123,7 +122,7 @@ public partial interface IAdbClient Task RemoveReverseForwardAsync(DeviceData device, string remote, CancellationToken cancellationToken); /// - /// Removes all reverse forwards for a given device. + /// Asynchronously removes all reverse forwards for a given device. /// /// The device on which to remove all reverse port forwarding /// A which can be used to cancel the asynchronous operation. @@ -131,7 +130,7 @@ public partial interface IAdbClient Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationToken cancellationToken); /// - /// Remove a port forwarding between a local and a remote port. + /// Asynchronously remove a port forwarding between a local and a remote port. /// /// The device on which to remove the port forwarding. /// Specification of the local port that was forwarded. @@ -140,7 +139,7 @@ public partial interface IAdbClient Task RemoveForwardAsync(DeviceData device, int localPort, CancellationToken cancellationToken); /// - /// Removes all forwards for a given device. + /// Asynchronously removes all forwards for a given device. /// /// The device on which to remove the port forwarding. /// A which can be used to cancel the asynchronous operation. @@ -148,7 +147,7 @@ public partial interface IAdbClient Task RemoveAllForwardsAsync(DeviceData device, CancellationToken cancellationToken); /// - /// List all existing forward connections from this server. + /// Asynchronously list all existing forward connections from this server. /// /// The device for which to list the existing forward connections. /// A which can be used to cancel the asynchronous operation. @@ -164,7 +163,7 @@ public partial interface IAdbClient Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// The target of command, such as shell, remount, dev, tcp, local, /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. @@ -175,7 +174,7 @@ public partial interface IAdbClient Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// The target of command, such as shell, remount, dev, tcp, local, /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. @@ -187,7 +186,7 @@ public partial interface IAdbClient Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken); /// - /// Executes a command on the device. + /// Asynchronously executes a command on the device. /// /// The command to execute. /// The device on which to run the command. @@ -197,7 +196,7 @@ public partial interface IAdbClient Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// The target of command, such as shell, remount, dev, tcp, local, /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. @@ -209,7 +208,7 @@ public partial interface IAdbClient Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken); /// - /// Executes a command on the adb server. + /// Asynchronously executes a command on the adb server. /// /// The target of command, such as shell, remount, dev, tcp, local, /// localreserved, localabstract, jdwp, track-jdwp, sync, reverse and so on. @@ -222,7 +221,7 @@ public partial interface IAdbClient Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken); /// - /// Executes a command on the device. + /// Asynchronously executes a command on the device. /// /// The command to execute. /// The device on which to run the command. @@ -233,7 +232,7 @@ public partial interface IAdbClient Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken); /// - /// Gets the frame buffer from the specified end point. + /// Asynchronously gets the frame buffer from the specified end point. /// /// The device for which to get the framebuffer. /// A that can be used to cancel the asynchronous task. @@ -253,7 +252,7 @@ public partial interface IAdbClient Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames); /// - /// Reboots the specified device in to the specified mode. + /// Asynchronously reboots the specified device in to the specified mode. /// /// The mode into which to reboot the device. /// The device to reboot. @@ -262,7 +261,7 @@ public partial interface IAdbClient Task RebootAsync(string into, DeviceData device, CancellationToken cancellationToken); /// - /// Pair with a device for secure TCP/IP communication + /// Asynchronously pair with a device for secure TCP/IP communication /// /// The DNS endpoint at which the adb server on the device is running. /// The pairing code. @@ -271,7 +270,7 @@ public partial interface IAdbClient Task PairAsync(DnsEndPoint endpoint, string code, CancellationToken cancellationToken); /// - /// Connect to a device via TCP/IP. + /// Asynchronously connect to a device via TCP/IP. /// /// The DNS endpoint at which the adb server on the device is running. /// A which can be used to cancel the asynchronous operation. @@ -279,7 +278,7 @@ public partial interface IAdbClient Task ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken); /// - /// Disconnects a remote device from this local ADB server. + /// Asynchronously disconnects a remote device from this local ADB server. /// /// The endpoint of the remote device to disconnect. /// A which can be used to cancel the asynchronous operation. @@ -287,7 +286,7 @@ public partial interface IAdbClient Task DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken); /// - /// Restarts the ADB daemon running on the device with root privileges. + /// Asynchronously restarts the ADB daemon running on the device with root privileges. /// /// The device on which to restart ADB with root privileges. /// A which can be used to cancel the asynchronous operation. @@ -295,7 +294,7 @@ public partial interface IAdbClient Task RootAsync(DeviceData device, CancellationToken cancellationToken); /// - /// Restarts the ADB daemon running on the device without root privileges. + /// Asynchronously restarts the ADB daemon running on the device without root privileges. /// /// The device on which to restart ADB without root privileges. /// A which can be used to cancel the asynchronous operation. @@ -335,7 +334,7 @@ public partial interface IAdbClient Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments); /// - /// Like "install", but starts an install session. + /// Like "install", but starts an install session asynchronously. /// /// The device on which to install the application. /// The package name of the baseAPK to install. @@ -345,7 +344,7 @@ public partial interface IAdbClient Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments); /// - /// Write an apk into the given install session. + /// Asynchronously write an apk into the given install session. /// /// The device on which to install the application. /// A which represents the application to install. @@ -356,7 +355,7 @@ public partial interface IAdbClient Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken); /// - /// Commit the given active install session, installing the app. + /// Asynchronously commit the given active install session, installing the app. /// /// The device on which to install the application. /// The session ID of the install session. @@ -365,7 +364,7 @@ public partial interface IAdbClient Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken); /// - /// Uninstalls an Android application on an device. + /// Asynchronously uninstalls an Android application on an device. /// /// The device on which to install the application. /// The name of the package to uninstall. @@ -375,7 +374,7 @@ public partial interface IAdbClient Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments); /// - /// Lists all features supported by the current device. + /// Asynchronously lists all features supported by the current device. /// /// The device for which to get the list of features supported. /// A which can be used to cancel the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs index c8495c71..bb9e5577 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs @@ -11,14 +11,14 @@ namespace AdvancedSharpAdbClient public partial interface IAdbCommandLineClient { /// - /// Queries adb for its version number and checks it against . + /// Asynchronously queries adb for its version number and checks it against . /// /// A which can be used to cancel the asynchronous operation. /// A which return a object that contains the version number of the Android Command Line client. Task GetVersionAsync(CancellationToken cancellationToken); /// - /// Starts the adb server by running the adb start-server command. + /// Asynchronously starts the adb server by running the adb start-server command. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs index 49fa3840..2d908bf6 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbServer.Async.cs @@ -10,7 +10,7 @@ namespace AdvancedSharpAdbClient public partial interface IAdbServer { /// - /// Starts the adb server if it was not previously running. + /// Asynchronously starts the adb server if it was not previously running. /// /// The path to the adb.exe executable that can be used to start the adb server. /// If this path is not provided, this method will throw an exception if the server @@ -47,7 +47,7 @@ public partial interface IAdbServer Task StartServerAsync(string adbPath, bool restartServerIfNewer, CancellationToken cancellationToken); /// - /// Restarts the adb server if it suddenly became unavailable. Call this class if, for example, + /// Asynchronously restarts the adb server if it suddenly became unavailable. Call this class if, for example, /// you receive an with the flag /// set to - a clear indicating the ADB server died. /// @@ -58,7 +58,7 @@ public partial interface IAdbServer Task RestartServerAsync(CancellationToken cancellationToken); /// - /// Restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, + /// Asynchronously restarts the adb server with new adb path if it suddenly became unavailable. Call this class if, for example, /// you receive an with the flag /// set to - a clear indicating the ADB server died. /// @@ -72,12 +72,13 @@ public partial interface IAdbServer Task RestartServerAsync(string adbPath, CancellationToken cancellationToken); /// - /// Stop the adb server asynchronously. + /// Asynchronously stop the adb server asynchronously. /// + /// A which represents the asynchronous operation. Task StopServerAsync(CancellationToken cancellationToken); /// - /// Gets the status of the adb server asynchronously. + /// Asynchronously gets the status of the adb server asynchronously. /// /// A which can be used to cancel the asynchronous operation. /// A which return a object that describes the status of the adb server. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs index 27b8165e..e9082641 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbSocket.Async.cs @@ -13,7 +13,7 @@ namespace AdvancedSharpAdbClient public partial interface IAdbSocket { /// - /// Reconnects the to the same endpoint it was initially connected to. + /// Asynchronously reconnects the to the same endpoint it was initially connected to. /// Use this when the socket was disconnected by adb and you have restarted adb. /// /// Force reconnect whatever the socket is connected or not. @@ -22,7 +22,7 @@ public partial interface IAdbSocket Task ReconnectAsync(bool isForce, CancellationToken cancellationToken); /// - /// Sends the specified number of bytes of data to a , + /// Asynchronously sends the specified number of bytes of data to a , /// /// A array that acts as a buffer, containing the data to send. /// A that can be used to cancel the task. @@ -30,7 +30,7 @@ public partial interface IAdbSocket Task SendAsync(byte[] data, CancellationToken cancellationToken); /// - /// Sends the specified number of bytes of data to a , + /// Asynchronously sends the specified number of bytes of data to a , /// /// A array that acts as a buffer, containing the data to send. /// A that can be used to cancel the task. @@ -39,7 +39,7 @@ public partial interface IAdbSocket Task SendAsync(byte[] data, int length, CancellationToken cancellationToken); /// - /// Sends the specified number of bytes of data to a , + /// Asynchronously sends the specified number of bytes of data to a , /// /// A array that acts as a buffer, containing the data to send. /// The index of the first byte in the array to send. @@ -49,7 +49,7 @@ public partial interface IAdbSocket Task SendAsync(byte[] data, int offset, int length, CancellationToken cancellationToken); /// - /// Sends a sync request to the device. + /// Asynchronously sends a sync request to the device. /// /// The command to send. /// The path of the file on which the command should operate. @@ -59,7 +59,7 @@ public partial interface IAdbSocket Task SendSyncRequestAsync(SyncCommand command, string path, int permissions, CancellationToken cancellationToken); /// - /// Sends a sync request to the device. + /// Asynchronously sends a sync request to the device. /// /// The command to send. /// The path of the file on which the command should operate. @@ -68,7 +68,7 @@ public partial interface IAdbSocket Task SendSyncRequestAsync(SyncCommand command, string path, CancellationToken cancellationToken); /// - /// Sends a sync request to the device. + /// Asynchronously sends a sync request to the device. /// /// The command to send. /// The length of the data packet that follows. @@ -86,7 +86,7 @@ public partial interface IAdbSocket Task SendAdbRequestAsync(string request, CancellationToken cancellationToken); /// - /// Reads a from an instance when + /// Asynchronously reads a from an instance when /// the connection is in sync mode. /// /// The buffer to store the read data into. @@ -95,7 +95,7 @@ public partial interface IAdbSocket Task ReadAsync(byte[] data, CancellationToken cancellationToken); /// - /// Receives data from a into a receive buffer. + /// Asynchronously receives data from a into a receive buffer. /// /// An array of type that is the storage location for the received data. /// The number of bytes to receive. @@ -112,7 +112,7 @@ public partial interface IAdbSocket Task ReadStringAsync(CancellationToken cancellationToken); /// - /// Reads a from an instance when + /// Asynchronously reads a from an instance when /// the connection is in sync mode. /// /// A that can be used to cancel the task. @@ -120,7 +120,7 @@ public partial interface IAdbSocket Task ReadSyncStringAsync(CancellationToken cancellationToken); /// - /// Reads the response to a sync command. + /// Asynchronously reads the response to a sync command. /// /// A that can be used to cancel the task. /// A that represents the asynchronous operation. The return value of the task is the response that was sent by the device. @@ -136,7 +136,7 @@ public partial interface IAdbSocket #if HAS_BUFFERS /// - /// Sends the specified number of bytes of data to a , + /// Asynchronously sends the specified number of bytes of data to a , /// /// A array that acts as a buffer, containing the data to send. /// A that can be used to cancel the task. @@ -144,7 +144,7 @@ public partial interface IAdbSocket public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) => new(SendAsync(data.ToArray(), cancellationToken)); /// - /// Receives data from a into a receive buffer. + /// Asynchronously receives data from a into a receive buffer. /// /// An array of type Byte that is the storage location for the received data. /// A that can be used to cancel the task. @@ -166,7 +166,7 @@ public ValueTask ReadAsync(Memory data, CancellationToken cancellatio #endif /// - /// Ask to switch the connection to the device/emulator identified by + /// Asynchronously ask to switch the connection to the device/emulator identified by /// . After this request, every client request will /// be sent directly to the adbd daemon running on the device. /// diff --git a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs index dfbe9b64..90dc5d74 100644 --- a/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IDeviceMonitor.Async.cs @@ -10,14 +10,14 @@ namespace AdvancedSharpAdbClient public partial interface IDeviceMonitor { /// - /// Starts the monitoring. + /// Asynchronously starts the monitoring. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. Task StartAsync(CancellationToken cancellationToken); /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting + /// Asynchronously performs application-defined tasks associated with freeing, releasing, or resetting /// unmanaged resources asynchronously. /// /// A that represents the asynchronous dispose operation. diff --git a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs index 221bffa5..f2ad8da4 100644 --- a/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ISyncService.Async.cs @@ -14,7 +14,7 @@ namespace AdvancedSharpAdbClient public partial interface ISyncService { /// - /// Pushes (uploads) a file to the remote device. + /// Asynchronously pushes (uploads) a file to the remote device. /// /// A that contains the contents of the file. /// The path, on the device, to which to push the file. @@ -26,7 +26,7 @@ public partial interface ISyncService Task PushAsync(Stream stream, string remotePath, int permissions, DateTimeOffset timestamp, IProgress? progress, CancellationToken cancellationToken); /// - /// Pulls (downloads) a file from the remote device. + /// Asynchronously pulls (downloads) a file from the remote device. /// /// The path, on the device, of the file to pull. /// A that will receive the contents of the file. @@ -36,7 +36,7 @@ public partial interface ISyncService Task PullAsync(string remotePath, Stream stream, IProgress? progress, CancellationToken cancellationToken); /// - /// Returns information about a file on the device. + /// Asynchronously returns information about a file on the device. /// /// The path of the file on the device. /// A that can be used to cancel the task. @@ -44,7 +44,7 @@ public partial interface ISyncService Task StatAsync(string remotePath, CancellationToken cancellationToken); /// - /// Lists the contents of a directory on the device. + /// Asynchronously lists the contents of a directory on the device. /// /// The path to the directory on the device. /// A that can be used to cancel the task. @@ -53,7 +53,7 @@ public partial interface ISyncService #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// - /// Lists the contents of a directory on the device. + /// Asynchronously lists the contents of a directory on the device. /// /// The path to the directory on the device. /// A that can be used to cancel the task. @@ -68,14 +68,14 @@ async IAsyncEnumerable GetDirectoryAsyncListing(string remotePat #endif /// - /// Opens this connection. + /// Asynchronously opens this connection. /// /// A that can be used to cancel the task. /// A which represents the asynchronous operation. Task OpenAsync(CancellationToken cancellationToken); /// - /// Reopen this connection. Use this when the socket was disconnected by adb and you have restarted adb. + /// Asynchronously reopen this connection. Use this when the socket was disconnected by adb and you have restarted adb. /// /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. diff --git a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs index 71eef16d..451a5f4c 100644 --- a/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/ITcpSocket.Async.cs @@ -21,7 +21,7 @@ public partial interface ITcpSocket Task ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken); /// - /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the + /// Asynchronously re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the /// socket to disconnect. /// /// Force reconnect whatever the socket is connected or not. @@ -64,7 +64,7 @@ public partial interface ITcpSocket Task SendAsync(byte[] buffer, int offset, int size, SocketFlags socketFlags, CancellationToken cancellationToken); /// - /// Receives the specified number of bytes from a bound + /// Asynchronously receives the specified number of bytes from a bound /// using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. @@ -75,7 +75,7 @@ public partial interface ITcpSocket Task ReceiveAsync(byte[] buffer, SocketFlags socketFlags, CancellationToken cancellationToken); /// - /// Receives the specified number of bytes from a bound + /// Asynchronously receives the specified number of bytes from a bound /// using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. @@ -87,7 +87,7 @@ public partial interface ITcpSocket Task ReceiveAsync(byte[] buffer, int size, SocketFlags socketFlags, CancellationToken cancellationToken); /// - /// Receives the specified number of bytes from a bound + /// Asynchronously receives the specified number of bytes from a bound /// into the specified offset position of the receive buffer, using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. @@ -111,7 +111,7 @@ public partial interface ITcpSocket public ValueTask SendAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, CancellationToken cancellationToken) => new(ReceiveAsync(buffer.ToArray(), socketFlags, cancellationToken)); /// - /// Receives the specified number of bytes from a bound + /// Asynchronously receives the specified number of bytes from a bound /// using the specified SocketFlags. /// /// An array of type Byte that is the storage location for received data. diff --git a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs index d95fb309..9d09e5e5 100644 --- a/AdvancedSharpAdbClient/Logs/LogReader.Async.cs +++ b/AdvancedSharpAdbClient/Logs/LogReader.Async.cs @@ -13,7 +13,7 @@ namespace AdvancedSharpAdbClient.Logs public partial class LogReader { /// - /// Reads the next from the stream. + /// Asynchronously reads the next from the stream. /// /// A which can be used to cancel the asynchronous operation. /// A which return a new object. diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs index 4ee7fabf..13eb90fc 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs @@ -44,7 +44,7 @@ public static void AddRange(this ICollection source, IEnumerab #if HAS_TASK /// - /// Creates an array from a . + /// Asynchronously creates an array from a . /// /// The type of the elements of . /// An to create an array from. @@ -53,7 +53,7 @@ public static Task ToArrayAsync(this Task x.Result.ToArray()); /// - /// Creates an array from a . + /// Asynchronously creates an array from a . /// /// The type of the elements of . /// An to create an array from. diff --git a/AdvancedSharpAdbClient/SyncService.Async.cs b/AdvancedSharpAdbClient/SyncService.Async.cs index 5acffb50..856906da 100644 --- a/AdvancedSharpAdbClient/SyncService.Async.cs +++ b/AdvancedSharpAdbClient/SyncService.Async.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -307,7 +306,7 @@ public virtual async IAsyncEnumerable GetDirectoryAsyncListing(s #endif /// - /// Reads the statistics of a file from the socket. + /// Asynchronously reads the statistics of a file from the socket. /// /// A that can be used to cancel the task. /// A which return a object that contains information about the file. diff --git a/AdvancedSharpAdbClient/TcpSocket.Async.cs b/AdvancedSharpAdbClient/TcpSocket.Async.cs index 897280e8..df211af0 100644 --- a/AdvancedSharpAdbClient/TcpSocket.Async.cs +++ b/AdvancedSharpAdbClient/TcpSocket.Async.cs @@ -52,7 +52,7 @@ public virtual Task ReconnectAsync(bool isForce, CancellationToken cancellationT } /// - /// Re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the + /// Asynchronously re-establishes the connection to a remote host. Assumes you have resolved the reason that caused the /// socket to disconnect. /// /// A which can be used to cancel the asynchronous task. diff --git a/README.md b/README.md index bcf64b1a..2c07d7e1 100644 --- a/README.md +++ b/README.md @@ -60,15 +60,15 @@ If you want to automate 2 or more devices at the same time, you must remember: 1 You can look at the examples to understand more ```cs -static AdbClient client; - -static DeviceData device; +static AdbClient adbClient; +static DeviceData deviceData; static void Main(string[] args) { - client = new AdbClient(); - client.Connect("127.0.0.1:62001"); - device = client.GetDevices().FirstOrDefault(); // Get first connected device + adbClient = new AdbClient(); + adbClient.Connect("127.0.0.1:62001"); + device = adbClient.GetDevices().FirstOrDefault(); // Get first connected device + ... } ``` @@ -78,29 +78,27 @@ static void Main(string[] args) You can find the element on the screen by xpath ```cs -static AdbClient client; - -static DeviceData device; +static DeviceClient deviceClient; static void Main(string[] args) { - client = new AdbClient(); - client.Connect("127.0.0.1:62001"); - device = client.GetDevices().FirstOrDefault(); - Element element = client.FindElement(device, "//node[@text='Login']"); + ... + deviceClient = new DeviceClient(adbClient, deviceData); + Element element = deviceClient.FindElement("//node[@text='Login']"); + ... } ``` You can also specify the waiting time for the element ```cs -Element element = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(5)); +Element element = deviceClient.FindElement("//node[@text='Login']", TimeSpan.FromSeconds(5)); ``` And you can find several elements ```cs -Element[] element = client.FindElements(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(5)); +Element[] element = deviceClient.FindElements("//node[@resource-id='Login']", TimeSpan.FromSeconds(5)); ``` ### Getting element attributes @@ -110,7 +108,7 @@ You can get all element attributes static void Main(string[] args) { ... - Element element = client.FindElement(device, "//node[@resource-id='Login']", TimeSpan.FromSeconds(3)); + Element element = deviceClient.FindElement("//node[@resource-id='Login']", TimeSpan.FromSeconds(3)); string eltext = element.Attributes["text"]; string bounds = element.Attributes["bounds"]; ... @@ -125,7 +123,7 @@ You can click on the x and y coordinates static void Main(string[] args) { ... - client.Click(device, 600, 600); // Click on the coordinates (600;600) + deviceClient.Click(600, 600); // Click on the coordinates (600;600) ... } ``` @@ -136,7 +134,7 @@ Or on the element(need xpath) static void Main(string[] args) { ... - Element element = client.FindElement(device, "//node[@text='Login']", TimeSpan.FromSeconds(3)); + Element element = deviceClient.FindElement("//node[@text='Login']", TimeSpan.FromSeconds(3)); element.Click(); // Click on element by xpath //node[@text='Login'] ... } @@ -163,9 +161,9 @@ You can swipe from one element to another static void Main(string[] args) { ... - Element first = client.FindElement(device, "//node[@text='Login']"); - Element second = client.FindElement(device, "//node[@text='Password']"); - client.Swipe(device, first, second, 100); // Swipe 100 ms + Element first = deviceClient.FindElement("//node[@text='Login']"); + Element second = deviceClient.FindElement("//node[@text='Password']"); + deviceClient.Swipe(first, second, 100); // Swipe 100 ms ... } ``` @@ -176,8 +174,7 @@ Or swipe by coordinates static void Main(string[] args) { ... - device = client.GetDevices().FirstOrDefault(); - client.Swipe(device, 600, 1000, 600, 500, 100); // Swipe from (600;1000) to (600;500) on 100 ms + deviceClient.Swipe(600, 1000, 600, 500, 100); // Swipe from (600;1000) to (600;500) on 100 ms ... } ``` @@ -187,9 +184,9 @@ The Swipe() method throw ElementNotFoundException if the element is not found ```cs try { - client.Swipe(device, 0x2232323, 0x954, 0x9128, 0x11111, 200); + deviceClient.Swipe(0x2232323, 0x954, 0x9128, 0x11111, 200); ... - client.Swipe(device, first, second, 200); + deviceClient.Swipe(first, second, 200); } catch (Exception ex) { @@ -207,7 +204,7 @@ The text field should be in focus static void Main(string[] args) { ... - client.SendText(device, "text"); // Send text to device + deviceClient.SendText("text"); // Send text to device ... } ``` @@ -218,7 +215,7 @@ You can also send text to the element (clicks on the element and sends the text) static void Main(string[] args) { ... - client.FindElement(device, "//node[@resource-id='Login']").SendText("text"); // Send text to the element by xpath //node[@resource-id='Login'] + deviceClient.FindElement("//node[@resource-id='Login']").SendText("text"); // Send text to the element by xpath //node[@resource-id='Login'] ... } ``` @@ -228,7 +225,7 @@ The SendText() method throw InvalidTextException if text is incorrect ```cs try { - client.SendText(device, null); + deviceClient.SendText(null); } catch (Exception ex) { @@ -249,7 +246,7 @@ The text field should be in focus static void Main(string[] args) { ... - client.ClearInput(device, 25); // The second argument is to specify the maximum number of characters to be erased + adbClient.ClearInput(deviceData, 25); // The second argument is to specify the maximum number of characters to be erased ... } ``` @@ -260,7 +257,7 @@ static void Main(string[] args) static void Main(string[] args) { ... - client.FindElement(device, "//node[@resource-id='Login']").ClearInput(); // Get element text attribute and remove text length symbols + deviceClient.FindElement("//node[@resource-id='Login']").ClearInput(); // Get element text attribute and remove text length symbols ... } ``` @@ -273,7 +270,7 @@ You can see key events here https://developer.android.com/reference/android/view static void Main(string[] args) { ... - client.SendKeyEvent(device, "KEYCODE_TAB"); + deviceClient.SendKeyEvent("KEYCODE_TAB"); ... } ``` @@ -283,7 +280,7 @@ The SendKeyEvent method throw InvalidKeyEventException if key event is incorrect ```cs try { - client.SendKeyEvent(device, null); + deviceClient.SendKeyEvent(null); } catch (Exception ex) { @@ -297,9 +294,9 @@ catch (Exception ex) static void Main(string[] args) { ... - client.ClickBackButton(device); // Click Back button + adbClient.ClickBackButton(device); // Click Back button ... - client.ClickHomeButton(device); // Click Home button + adbClient.ClickHomeButton(device); // Click Home button ... } ``` @@ -312,7 +309,7 @@ static void Main(string[] args) static void Main(string[] args) { ... - PackageManager manager = new PackageManager(client, device); + PackageManager manager = new PackageManager(adbClient, deviceData); manager.InstallPackage(@"C:\Users\me\Documents\mypackage.apk"); manager.UninstallPackage("com.android.app"); ... @@ -327,8 +324,8 @@ static void Main(string[] args) ... using (FileStream stream = File.OpenRead("Application.apk")) { - client.Install(device, stream); - client.Uninstall(device, "com.android.app"); + adbClient.Install(device, stream); + adbClient.Uninstall(device, "com.android.app"); } ... } @@ -339,7 +336,7 @@ static void Main(string[] args) static void Main(string[] args) { ... - PackageManager manager = new PackageManager(client, device); + PackageManager manager = new PackageManager(adbClient, deviceData); manager.InstallMultiplePackage(@"C:\Users\me\Documents\base.apk", new[] { @"C:\Users\me\Documents\split_1.apk", @"C:\Users\me\Documents\split_2.apk" }); // Install split app whith base app manager.InstallMultiplePackage(new[] { @"C:\Users\me\Documents\split_3.apk", @"C:\Users\me\Documents\split_4.apk" }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' ... @@ -352,8 +349,8 @@ Or you can use AdbClient.InstallMultiple static void Main(string[] args) { ... - client.InstallMultiple(device, File.OpenRead("base.apk"), new[] { File.OpenRead("split_1.apk"), File.OpenRead("split_2.apk") }); // Install split app whith base app - client.InstallMultiple(device, new[] { File.OpenRead("split_3.apk"), File.OpenRead("split_4.apk") }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' + adbClient.InstallMultiple(device, File.OpenRead("base.apk"), new[] { File.OpenRead("split_1.apk"), File.OpenRead("split_2.apk") }); // Install split app whith base app + adbClient.InstallMultiple(device, new[] { File.OpenRead("split_3.apk"), File.OpenRead("split_4.apk") }, "com.android.app"); // Add split app to base app which packagename is 'com.android.app' ... } ``` @@ -364,8 +361,8 @@ static void Main(string[] args) static void Main(string[] args) { ... - client.StartApp(device, "com.android.app"); - client.StopApp(device, "com.android.app"); // force-stop + deviceClient.StartApp("com.android.app"); + deviceClient.StopApp("com.android.app"); // force-stop ... } ``` @@ -376,9 +373,9 @@ static void Main(string[] args) static async void Main(string[] args) { ... - Image img = client.GetFrameBuffer(device, CancellationToken.None); // synchronously + Image img = adbClient.GetFrameBuffer(deviceData, CancellationToken.None); // synchronously ... - Image img = await client.GetFrameBufferAsync(device, CancellationToken.None); // asynchronously + Image img = await adbClient.GetFrameBufferAsync(deviceData, CancellationToken.None); // asynchronously ... } ``` @@ -389,7 +386,7 @@ static async void Main(string[] args) static void Main(string[] args) { ... - XmlDocument screen = client.DumpScreen(device); + XmlDocument screen = deviceClient.DumpScreen(); ... } ``` @@ -399,7 +396,7 @@ static void Main(string[] args) ```cs void DownloadFile() { - using (SyncService service = new SyncService(new AdbSocket(client.EndPoint), device)) + using (SyncService service = new SyncService(deviceData)) { using (FileStream stream = File.OpenWrite(@"C:\MyFile.txt")) { @@ -410,7 +407,7 @@ void DownloadFile() void UploadFile() { - using (SyncService service = new SyncService(new AdbSocket(client.EndPoint), device)) + using (SyncService service = new SyncService(deviceData)) { using (FileStream stream = File.OpenRead(@"C:\MyFile.txt")) { @@ -427,9 +424,9 @@ static async void Main(string[] args) { ... ConsoleOutputReceiver receiver = new ConsoleOutputReceiver(); - client.ExecuteRemoteCommand("echo Hello, World", device, receiver); // synchronously + adbClient.ExecuteRemoteCommand("echo Hello, World", device, receiver); // synchronously ... - await client.ExecuteRemoteCommandAsync("echo Hello, World", device, receiver, CancellationToken.None); // asynchronously + await adbClient.ExecuteRemoteCommandAsync("echo Hello, World", device, receiver, default); // asynchronously ... } ``` From 8504a87ba8072c076602eae9fc8bb667720e065f Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sat, 9 Dec 2023 21:42:36 +0800 Subject: [PATCH 06/12] Add progress in AdbClient Install method --- .../AdbClientTests.Async.cs | 101 ++++--- .../AdbClientTests.cs | 149 +++++++---- .../AdvancedSharpAdbClient.Tests.csproj | 4 +- ...pscreen_clean.txt => DumpScreen.Clean.xml} | 0 ...pscreen_error.txt => DumpScreen.Error.txt} | 0 .../Assets/DumpScreen.MIUI.txt | 31 +++ .../Assets/{gapps.txt => DumpSys.GApps.txt} | 0 ...umpsys_package.txt => DumpSys.Package.txt} | 0 ...-empty.bin => FramebufferHeader.Empty.bin} | Bin ...header-v1.bin => FramebufferHeader.V1.bin} | Bin ...header-v2.bin => FramebufferHeader.V2.bin} | Bin .../Assets/{testapp.apk => TestApp/base.apk} | Bin .../Assets/TestApp/split_config.arm64_v8a.apk | Bin 0 -> 559 bytes .../Assets/TestApp/split_config.xxhdpi.apk | Bin 0 -> 14764 bytes .../Assets/dumpscreen_miui.txt | 31 --- .../Assets/dumpscreen_miui_clean.txt | 1 - AdvancedSharpAdbClient.Tests/Assets/test.txt | 1 - .../DeviceCommands/DeviceClientTexts.Async.cs | 24 +- .../DeviceCommands/DeviceClientTexts.cs | 22 +- .../DeviceCommands/LinuxPathTests.cs | 4 +- .../PackageManagerTests.Async.cs | 105 ++++---- .../DeviceCommands/PackageManagerTests.cs | 103 ++++---- .../Receivers/VersionInfoReceiverTests.cs | 2 +- .../Dummys/DummyAdbClient.cs | 16 +- AdvancedSharpAdbClient.Tests/Logs/LogTests.cs | 8 +- .../Models/FramebufferHeaderTests.cs | 10 +- .../Models/FramebufferTests.cs | 8 +- .../SocketBasedTests.cs | 2 +- .../SyncServiceTests.Async.cs | 6 +- .../SyncServiceTests.cs | 6 +- AdvancedSharpAdbClient/AdbClient.Async.cs | 248 +++++++++++++----- AdvancedSharpAdbClient/AdbClient.cs | 226 ++++++++++++---- .../Enums/PackageInstallProgressState.cs | 5 + .../DeviceCommands/PackageManager.Async.cs | 6 + .../DeviceCommands/PackageManager.cs | 10 +- .../Extensions/AdbClientExtensions.Async.cs | 39 +-- .../Interfaces/IAdbClient.Async.cs | 16 +- .../Interfaces/IAdbClient.cs | 38 +-- 38 files changed, 748 insertions(+), 474 deletions(-) rename AdvancedSharpAdbClient.Tests/Assets/{dumpscreen_clean.txt => DumpScreen.Clean.xml} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{dumpscreen_error.txt => DumpScreen.Error.txt} (100%) create mode 100644 AdvancedSharpAdbClient.Tests/Assets/DumpScreen.MIUI.txt rename AdvancedSharpAdbClient.Tests/Assets/{gapps.txt => DumpSys.GApps.txt} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{dumpsys_package.txt => DumpSys.Package.txt} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{framebufferheader-empty.bin => FramebufferHeader.Empty.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{framebufferheader-v1.bin => FramebufferHeader.V1.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{framebufferheader-v2.bin => FramebufferHeader.V2.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{testapp.apk => TestApp/base.apk} (100%) create mode 100644 AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.arm64_v8a.apk create mode 100644 AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.xxhdpi.apk delete mode 100644 AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui.txt delete mode 100644 AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui_clean.txt delete mode 100644 AdvancedSharpAdbClient.Tests/Assets/test.txt diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index a991b284..09760d04 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -344,8 +344,8 @@ public async void GetFrameBufferAsyncTest() NoSyncRequests, NoSyncResponses, [ - await File.ReadAllBytesAsync("Assets/framebufferheader.bin"), - await File.ReadAllBytesAsync("Assets/framebuffer.bin") + await File.ReadAllBytesAsync("Assets/FramebufferHeader.bin"), + await File.ReadAllBytesAsync("Assets/Framebuffer.bin") ], null, () => TestClient.GetFrameBufferAsync(Device)); @@ -403,7 +403,7 @@ public async void RunLogServiceAsyncTest() ConsoleOutputReceiver receiver = new(); - await using FileStream stream = File.OpenRead("Assets/logcat.bin"); + await using FileStream stream = File.OpenRead("Assets/Logcat.bin"); await using ShellStream shellStream = new(stream, false); List logs = []; Action sink = logs.Add; @@ -640,43 +640,36 @@ public async void UnrootAsyncTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void InstallAsyncTest() { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "exec:cmd package 'install' -S 205774" - ]; - // The app data is sent in chunks of 32 kb List applicationDataChunks = []; - await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { - while (true) + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) { - byte[] buffer = new byte[32 * 1024]; - int read = await stream.ReadAsync(buffer); - - if (read == 0) - { - break; - } - else - { - buffer = buffer.Take(read).ToArray(); - applicationDataChunks.Add(buffer); - } + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); } } byte[] response = "Success\n"u8.ToArray(); - await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install' -S {stream.Length}" + ]; + await RunTestAsync( OkResponses(2), NoResponseMessages, @@ -685,7 +678,12 @@ await RunTestAsync( NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallAsync(Device, stream)); + () => TestClient.InstallAsync(Device, stream, + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); } } @@ -715,43 +713,44 @@ public async void InstallCreateAsyncTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void InstallWriteAsyncTest() { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "exec:cmd package 'install-write' -S 205774 936013062 base.apk" - ]; - // The app data is sent in chunks of 32 kb List applicationDataChunks = []; - await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { - while (true) + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) { - byte[] buffer = new byte[32 * 1024]; - int read = await stream.ReadAsync(buffer); - - if (read == 0) - { - break; - } - else - { - buffer = buffer.Take(read).ToArray(); - applicationDataChunks.Add(buffer); - } + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); } } - byte[] response = "Success: streamed 205774 bytes\n"u8.ToArray(); - - await using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {stream.Length} 936013062 base.apk" + ]; + + byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Length} bytes\n"); + + double temp = 0; + Progress progress = new(); + progress.ProgressChanged += (sender, args) => + { + Assert.True(temp <= args, $"{nameof(args)}: {args} is less than {temp}."); + temp = args; + }; + await RunTestAsync( OkResponses(2), NoResponseMessages, @@ -760,7 +759,7 @@ await RunTestAsync( NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062")); + () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062", progress)); } } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 200cabee..29aaad6f 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -455,8 +455,8 @@ public void GetFrameBufferTest() NoSyncRequests, NoSyncResponses, [ - File.ReadAllBytes("Assets/framebufferheader.bin"), - File.ReadAllBytes("Assets/framebuffer.bin") + File.ReadAllBytes("Assets/FramebufferHeader.bin"), + File.ReadAllBytes("Assets/Framebuffer.bin") ], null, () => TestClient.GetFrameBuffer(Device)); @@ -511,7 +511,7 @@ public void RunLogServiceTest() ConsoleOutputReceiver receiver = new(); - using FileStream stream = File.OpenRead("Assets/logcat.bin"); + using FileStream stream = File.OpenRead("Assets/Logcat.bin"); using ShellStream shellStream = new(stream, false); List logs = []; Action sink = logs.Add; @@ -748,43 +748,36 @@ public void UnrootTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void InstallTest() { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "exec:cmd package 'install' -S 205774" - ]; - // The app data is sent in chunks of 32 kb List applicationDataChunks = []; - using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { - while (true) - { - byte[] buffer = new byte[32 * 1024]; - int read = stream.Read(buffer.AsSpan(0, buffer.Length)); + byte[] buffer = new byte[32 * 1024]; + int read = 0; - if (read == 0) - { - break; - } - else - { - buffer = buffer.Take(read).ToArray(); - applicationDataChunks.Add(buffer); - } + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); } } byte[] response = "Success\n"u8.ToArray(); - using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install' -S {stream.Length}" + ]; + RunTest( OkResponses(2), NoResponseMessages, @@ -793,7 +786,12 @@ public void InstallTest() NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.Install(Device, stream)); + () => TestClient.Install(Device, stream, + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); } } @@ -823,43 +821,44 @@ public void InstallCreateTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void InstallWriteTest() { - string[] requests = - [ - "host:transport:169.254.109.177:5555", - "exec:cmd package 'install-write' -S 205774 936013062 base.apk" - ]; - // The app data is sent in chunks of 32 kb List applicationDataChunks = []; - using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { - while (true) - { - byte[] buffer = new byte[32 * 1024]; - int read = stream.Read(buffer.AsSpan(0, buffer.Length)); + byte[] buffer = new byte[32 * 1024]; + int read = 0; - if (read == 0) - { - break; - } - else - { - buffer = buffer.Take(read).ToArray(); - applicationDataChunks.Add(buffer); - } + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); } } - byte[] response = "Success: streamed 205774 bytes\n"u8.ToArray(); - - using (FileStream stream = File.OpenRead("Assets/testapp.apk")) + using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) { + string[] requests = + [ + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {stream.Length} 936013062 base.apk" + ]; + + byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Length} bytes\n"); + + double temp = 0; + Progress progress = new(); + progress.ProgressChanged += (sender, args) => + { + Assert.True(temp <= args, $"{nameof(args)}: {args} is less than {temp}."); + temp = args; + }; + RunTest( OkResponses(2), NoResponseMessages, @@ -868,7 +867,7 @@ public void InstallWriteTest() NoSyncResponses, [response], applicationDataChunks.ToArray(), - () => TestClient.InstallWrite(Device, stream, "base", "936013062")); + () => TestClient.InstallWrite(Device, stream, "base", "936013062", progress)); } } @@ -987,5 +986,53 @@ private void RunCreateForwardTest(Action test, string forwardString) requests, () => test(Device)); } + + private struct InstallProgress(params PackageInstallProgressState[] states) : IProgress + { + private PackageInstallProgressState? state; + private int packageFinished; + private int packageRequired; + private double uploadProgress; + + private int step = 0; + + public void Report(InstallProgressEventArgs value) + { + if (value.State == state) + { + Assert.True(uploadProgress <= value.UploadProgress, $"{nameof(value.UploadProgress)}: {value.UploadProgress} is less than {uploadProgress}."); + Assert.True(packageFinished <= value.PackageFinished, $"{nameof(value.PackageFinished)}: {value.PackageFinished} is less than {packageFinished}."); + } + else + { + Assert.Equal(states[step++], value.State); + } + + if (value.State is + PackageInstallProgressState.CreateSession + or PackageInstallProgressState.Installing + or PackageInstallProgressState.Finished) + { + Assert.Equal(0, value.UploadProgress); + Assert.Equal(0, value.PackageRequired); + Assert.Equal(0, value.PackageFinished); + } + else + { + if (packageRequired == 0) + { + packageRequired = value.PackageRequired; + } + else + { + Assert.Equal(packageRequired, value.PackageRequired); + } + } + + state = value.State; + packageFinished = value.PackageFinished; + uploadProgress = value.UploadProgress; + } + } } } diff --git a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj index e052c0a5..112bf97e 100644 --- a/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj +++ b/AdvancedSharpAdbClient.Tests/AdvancedSharpAdbClient.Tests.csproj @@ -29,8 +29,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_clean.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.Clean.xml similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/dumpscreen_clean.txt rename to AdvancedSharpAdbClient.Tests/Assets/DumpScreen.Clean.xml diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_error.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.Error.txt similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/dumpscreen_error.txt rename to AdvancedSharpAdbClient.Tests/Assets/DumpScreen.Error.txt diff --git a/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.MIUI.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.MIUI.txt new file mode 100644 index 00000000..03b7f30f --- /dev/null +++ b/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.MIUI.txt @@ -0,0 +1,31 @@ +java.io.FileNotFoundException: /data/system/theme_config/theme_compatibility.xml: open failed: ENOENT (No such file or directory) + at libcore.io.IoBridge.open(IoBridge.java:574) + at java.io.FileInputStream.(FileInputStream.java:160) + at java.io.FileInputStream.(FileInputStream.java:115) + at java.io.FileReader.(FileReader.java:60) + at miui.content.res.ThemeCompatibilityLoader.getVersion(ThemeCompatibilityLoader.java:108) + at miui.content.res.ThemeCompatibilityLoader.getConfigDocumentTree(ThemeCompatibilityLoader.java:126) + at miui.content.res.ThemeCompatibilityLoader.loadConfig(ThemeCompatibilityLoader.java:59) + at miui.content.res.ThemeCompatibility.(ThemeCompatibility.java:31) + at miui.content.res.ThemeCompatibility.isThemeEnabled(ThemeCompatibility.java:111) + at android.content.res.MiuiResourcesImpl.(MiuiResourcesImpl.java:41) + at android.content.res.MiuiResources.(MiuiResources.java:58) + at android.content.res.IMiuiResourceImpl.createResources(IMiuiResourceImpl.java:13) + at android.content.res.ThemeManagerStub.createMiuiResources(ThemeManagerStub.java:56) + at android.content.res.Resources.getSystem(Resources.java:235) + at android.util.MiuiMultiWindowAdapter.(MiuiMultiWindowAdapter.java:81) + at android.util.MiuiMultiWindowAdapter.getSize(MiuiMultiWindowAdapter.java:305) + at com.xiaomi.freeform.MiuiFreeformImpl.getSize(MiuiFreeformImpl.java:53) + at android.util.MiuiFreeformUtils.getSize(MiuiFreeformUtils.java:49) + at android.view.Display.getSize(Display.java:796) + at com.android.commands.uiautomator.DumpCommand.run(DumpCommand.java:110) + at com.android.commands.uiautomator.Launcher.main(Launcher.java:83) + at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:363) +Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory) + at libcore.io.Linux.open(Native Method) + at libcore.io.ForwardingOs.open(ForwardingOs.java:563) + at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274) + at libcore.io.IoBridge.open(IoBridge.java:560) + ... 22 more +UI hierchary dumped to: /dev/tty \ No newline at end of file diff --git a/AdvancedSharpAdbClient.Tests/Assets/gapps.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpSys.GApps.txt similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/gapps.txt rename to AdvancedSharpAdbClient.Tests/Assets/DumpSys.GApps.txt diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpsys_package.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpSys.Package.txt similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/dumpsys_package.txt rename to AdvancedSharpAdbClient.Tests/Assets/DumpSys.Package.txt diff --git a/AdvancedSharpAdbClient.Tests/Assets/framebufferheader-empty.bin b/AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.Empty.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/framebufferheader-empty.bin rename to AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.Empty.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/framebufferheader-v1.bin b/AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.V1.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/framebufferheader-v1.bin rename to AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.V1.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/framebufferheader-v2.bin b/AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.V2.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/framebufferheader-v2.bin rename to AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.V2.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/testapp.apk b/AdvancedSharpAdbClient.Tests/Assets/TestApp/base.apk similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/testapp.apk rename to AdvancedSharpAdbClient.Tests/Assets/TestApp/base.apk diff --git a/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.arm64_v8a.apk b/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.arm64_v8a.apk new file mode 100644 index 0000000000000000000000000000000000000000..35ef908416880f626b05f767ef5da347aced59dc GIT binary patch literal 559 zcmWIWW@cdk0uB(tpqy~=<8np@h6BtD48lOZV_r&8erAeqVqRuiYH^8PMQ%=M@M*8Z z0Rpz>FO_Q}nmahUxC>Qpg$Qcqwdx(2Yb~f^R+~8cnAxtfhvqxJyC0c4{{j0C<*Ij2 zrX9O-@AS#Jmc?n$(q`Q%_>joVbvq-Xq5J19&zqr1?G_4AM_N1W1>YYrPt<%ci=$Kg z(bOL*MH~;4Tlh>}zhwFs_8BrIep>S|xx@XV))%W80fq~dd|oQtwA=%YRFKd()G$C3VF$MP%RZ|16Oj@Xk}RDAxn-LbGLu^))&I@ zveb29_0*J0hCiLNYBqj(qBJiz>~p2F?Rnm7I}bDWT|6K-zqHCZ?Aoju=`LSZTr7Wa z`n`$o-iw=yHk`cN78IJ)e`C{>rlQvCuiLfHZ`Qfrrd??qx1Q^5>AN`(PY3CDTFd;o zK3lttQ-IgfI{lHh(btyMl6j^dTZ?_pr!7lA_$6fJysUf0ylU@_CWo6=hH2^lRGM6O z^k((4ckBP_m!-*Vj{5(xRdvt2$x%kv;{&`InZQwsBW59TFcM2V2Y9oxfutFM&=E-A J0456t1_0s0^vD1J literal 0 HcmV?d00001 diff --git a/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.xxhdpi.apk b/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.xxhdpi.apk new file mode 100644 index 0000000000000000000000000000000000000000..9dfb440260b4b60ca47c0f75a616bd8d01caa130 GIT binary patch literal 14764 zcmeHO3vdLI zS4TR<#%BjbL1#pzN~w=ZTYNE!b*e(G6dzR(7^StASYUi0wco${-^<>;_bv(85uNPJ zm)(EQ`M>j@_xb<5shc5xAaI>Zf5hY+>)Rg65Mq-Qq95d%aKO;Dz$HppYfz(R!NR6c z^X*$NeYZM$%X2IGMe?qDc-`3i?4ED-o8E8VBfGC$K5hF|l}2&J3o{SAoPE~S>%Kg- z?WSih`thMLi+9f)`_ywo3O5gYYx_s{e(~WwD-XT6Jm=$Je&r_*?`&WE2j4^e|0r8( zhUMOH`tWP}TzlPB9cQho7$bczv~~GXT{P3|o zE6khfudko`mscL|xABh8uU|O-%i79oe!ldUIX6~mr!TG9Q8O+$Z%Ebn4;Edw`pTD= zfBMKjqHQ0ZKhb>b_1XRbK7aV4>$I1SwFJR+cORhYHOHq z)61>r?XSve9eC!T`v*^6y#KASuYS5^ZO1e3OxTn8-#&N0c>a_pRvlP1zgPLe+n4OK40-!6Re(M`7=eC?%ocK>4U?4FxWdAH9;dlzpx zeCU_kch8o6K7U~I;iqmna@%`ZU){a^ygkLMKUh6%(i89ArtNy5;pWz+e~)_ljE%3o zQC`q@($4LR*WY;G`Zxai-e*hhZ~yGKckSr4XvOroUzFW-#R zDCfEVWbOZG?|myjURuZfYIUo){O_BZv&w}a^cB5?p+@y)!>>jQ6eH>{d%K6Q20^CC zva(u-^0(SyifnJ$HOat10O~7HG3d5=m z_&x9`P=HcHz*^uPpl=V92V4U@348*KK1GN*z+J#Lpm$FpegxbBya}9%k$X9?64(h0 z?Ipxjz#YI=p!aD)TngL<>;Q)J7D55m0{ei`SwdV5Yy`6UAPsm5IIS=21q|RmG#*ars76uHZRjCiD;}$jsBHFl?(n7!9)TD=f4b7pDPmic!AB0Gw9##t? z;h>l>A+=UmH?;=Mub3LL*9Uzez10`ijV2{jP-!Wamv>$`W~oukH@->SA%)5^+3?Rd*7^?sjH?~67nh6)n@0$-D9gnUXU z(x{|qr3Br2Gwky>s?ala>J>}7sHiAq`*uRJxU6%Uh7!>9<0>;*lgq5C@@ONfXS7u_ z{f(|di;B6Kj!(U~gw-cc_9)$tE~-c81P#489DoIpLHzH zn5UtZQb-R6dDV3)dX5xJ(W&GMD8>RV^4L{O&ph($pM^Rm5|aM)XK?#JRM&wLMq0sNZO=ug3tv zPG=fA&ISz)&cQ;{eA+Y*^OFd$iN$)vvLv>XohXaFt9fn_aa$sW)}$D*Ro1Z)doGr7 zb<;?>i+hg#XhaK#;}b2mu-kLZa}39}#MEVa?kFbqG%XyU^BKmlV~Mqkt*-XyH&b0m zI~lF)zD6Y+2&ul1+Q7SuHUFsVJ*SW-g+2a>!*dFy;AqkUWQ|pwxFMdS=NKeMP3dZN z3|dWGmUOi`4vwRSOqp_wu4&NJP{617O|3;Su`VTbuIHLU4Pr9{)CL6$NwWTh7k0{m z*jXpNA+@z#Axdk{%$aFu*l`K!dJv-`ss+OuToIflt;0DD5cWtMYanj`mtA&ToK`ob zCGEPQ*6{p>4YfGE4J|B8Q~Q8Qt%&yXwP;bTUJGgFBI|_Z_8sGAf>au`Q&?IfMx-gH z0n3vT2w0g)OVebs`!#rU2lnH*VfczGI;V!iSQt$k!2ab>OFKtVXLTVQy(sy!jjBRN zqoXTm(7+a4Qp)E**YX((7-nePLT;1d@yc* z*fvUtrHF+XqF!7rN<@(;#@_-+A;e1jVu3Jmj{rgBQSj7=+~2<|5Ah$mauM^1T@+9H z3PN`51@5yQ&j{UEX#;W314aWyKrL`FFbkLqP>UJB65wY*JCG@+pr!453E#)_y_D}| ze5bh8sUl#te=GWc`bOon3cgq3E-g;7z+r@wmv;)5bJ^->1ccVDmrNDgPX5b-UEATlm0x`HtfEHj4@Di{O z$U!V_GH@kuEAR>+5Ra<>C_XnIFoAa9QQ&XD0pJXD^jKg9paZ$NUNId`P8YWEOfD{S zBqxVaii$jwp*?IEndT8nIaZG15;1%@ad-wq#K@7nym`eu#L%IfD@|ZSoPBmehJ+=K zEsZUqDMAz?FVCr-Dh|Qz6(g~Oa7PbIV!!}qa77eSCXhp(GJza!F?g_($`t@epK6h& z)panZ@z+KF{#0&~OD+ZtiobVz-Q4b}9cwl!i2VFGt%rLoMvXdFHk;1wLN6zm zM^Sn|mIq_pJxlGXiqWIVqNH3Y{L#*5jZL^XYqDo4h73t4*X=xVKc(EKxhvARfIPO- zd3oFgDV(1~M^2n`4jJf~&+e;p&rOrj?UHc=rl54@v`N#J?r@Cga4gc6?#O6Uju;WM zGrgO{YX_N_&Jz-2#w7B1I6DsOY&y=H?9ZUT(|&6arKQK?&LB4It!6b#vd7|8oYi-! zof5C&EVXpS<>aJt6vV)Rsa})+AbR#>CjqUN))R1~B^~$CFP%KoV@*u>wv+HR3llL_ zA_uatC%9AXuTogLQYXYhVAyM|46(>wLT>)Lgepu`qH4hvS|F&J1vQb#G`%UJm<6@? zh^HDghM~94(u44RahegCnb~3k&PX`6h<)_p+rDaH0O4f5PImiF=Cz1k(zgKh=t*y6w}D-50g4sn0ThL)2HXU7c!v;zD75dG z?Y4XY%QV?3eIm;UgIIDE%cMZ*lUT;mVaXJW!F!1?hoCj(Ml2}l*OXn~*qVaklIj?1 z19#nHZ9;7@&!HD_JiB$4aaud>YxsEs?#~0;Sl)$u2OvEkDdmdv~oKRdK!=9T#A zp(Qh~#Lo^bnRz9Cc4*1WE3;W|SMpvbm{%5YI`c~UPC@O%ypq0Fkj%W2zFCmWypq0Lkjy-izF&~cJktJN zBJdr{dN1({Vk@0_Bz-fX{LCZi+X>0cBY7_t%p-X(7R)1gFBZ%rc`p{sBWW+DJvbi| zisr~ZAo*NRJBfYKI9w8&mce%eJsF2#P*WBB4(y?STZ&lzwybbcN_Xa8089m59gp-*m+HfC4r(y9wli-kf literal 0 HcmV?d00001 diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui.txt b/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui.txt deleted file mode 100644 index 0f34f37a..00000000 --- a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui.txt +++ /dev/null @@ -1,31 +0,0 @@ -java.io.FileNotFoundException: /data/system/theme_config/theme_compatibility.xml: open failed: ENOENT (No such file or directory) - at libcore.io.IoBridge.open(IoBridge.java:574) - at java.io.FileInputStream.(FileInputStream.java:160) - at java.io.FileInputStream.(FileInputStream.java:115) - at java.io.FileReader.(FileReader.java:60) - at miui.content.res.ThemeCompatibilityLoader.getVersion(ThemeCompatibilityLoader.java:108) - at miui.content.res.ThemeCompatibilityLoader.getConfigDocumentTree(ThemeCompatibilityLoader.java:126) - at miui.content.res.ThemeCompatibilityLoader.loadConfig(ThemeCompatibilityLoader.java:59) - at miui.content.res.ThemeCompatibility.(ThemeCompatibility.java:31) - at miui.content.res.ThemeCompatibility.isThemeEnabled(ThemeCompatibility.java:111) - at android.content.res.MiuiResourcesImpl.(MiuiResourcesImpl.java:41) - at android.content.res.MiuiResources.(MiuiResources.java:58) - at android.content.res.IMiuiResourceImpl.createResources(IMiuiResourceImpl.java:13) - at android.content.res.ThemeManagerStub.createMiuiResources(ThemeManagerStub.java:56) - at android.content.res.Resources.getSystem(Resources.java:235) - at android.util.MiuiMultiWindowAdapter.(MiuiMultiWindowAdapter.java:81) - at android.util.MiuiMultiWindowAdapter.getSize(MiuiMultiWindowAdapter.java:305) - at com.xiaomi.freeform.MiuiFreeformImpl.getSize(MiuiFreeformImpl.java:53) - at android.util.MiuiFreeformUtils.getSize(MiuiFreeformUtils.java:49) - at android.view.Display.getSize(Display.java:796) - at com.android.commands.uiautomator.DumpCommand.run(DumpCommand.java:110) - at com.android.commands.uiautomator.Launcher.main(Launcher.java:83) - at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) - at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:363) -Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory) - at libcore.io.Linux.open(Native Method) - at libcore.io.ForwardingOs.open(ForwardingOs.java:563) - at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274) - at libcore.io.IoBridge.open(IoBridge.java:560) - ... 22 more -UI hierchary dumped to: /dev/tty \ No newline at end of file diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui_clean.txt b/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui_clean.txt deleted file mode 100644 index 2c583156..00000000 --- a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen_miui_clean.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/AdvancedSharpAdbClient.Tests/Assets/test.txt b/AdvancedSharpAdbClient.Tests/Assets/test.txt deleted file mode 100644 index 99b60400..00000000 --- a/AdvancedSharpAdbClient.Tests/Assets/test.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test file. \ No newline at end of file diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs index 95521865..4678eb89 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs @@ -17,8 +17,8 @@ public partial class DeviceExtensionsTests [Fact] public async void DumpScreenStringAsyncTest() { - string dump = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); - string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + string dump = await File.ReadAllTextAsync(@"Assets/DumpScreen.txt"); + string cleanDump = await File.ReadAllTextAsync(@"Assets/DumpScreen.Clean.xml"); DummyAdbClient client = new(); client.Commands["shell:uiautomator dump /dev/tty"] = dump; @@ -37,8 +37,8 @@ public async void DumpScreenStringAsyncTest() [Fact] public async void DumpScreenStringMIUIAsyncTest() { - string miuidump = await File.ReadAllTextAsync(@"Assets/dumpscreen_miui.txt"); - string cleanMIUIDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_miui_clean.txt"); + string miuidump = await File.ReadAllTextAsync(@"Assets/DumpScreen.MIUI.txt"); + string cleanMIUIDump = await File.ReadAllTextAsync(@"Assets/DumpScreen.Clean.xml"); DummyAdbClient client = new(); client.Commands["shell:uiautomator dump /dev/tty"] = miuidump; @@ -75,7 +75,7 @@ public async void DumpScreenStringEmptyAsyncTest() public async void DumpScreenStringErrorAsyncTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen_error.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/DumpScreen.Error.txt"); _ = await Assert.ThrowsAsync(() => new DeviceClient(client, Device).DumpScreenStringAsync()); @@ -90,14 +90,14 @@ public async void DumpScreenStringErrorAsyncTest() public async void DumpScreenAsyncTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/DumpScreen.txt"); XmlDocument xml = await new DeviceClient(client, Device).DumpScreenAsync(); Assert.Single(client.ReceivedCommands); Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); - string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + string cleanDump = await File.ReadAllTextAsync(@"Assets/DumpScreen.Clean.xml"); XmlDocument doc = new(); doc.LoadXml(cleanDump); @@ -112,14 +112,14 @@ public async void DumpScreenAsyncTest() public async void DumpScreenWinRTAsyncTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = await File.ReadAllTextAsync(@"Assets/DumpScreen.txt"); Windows.Data.Xml.Dom.XmlDocument xml = await new DeviceClient(client, Device).DumpScreenWinRTAsync(); Assert.Single(client.ReceivedCommands); Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); - string cleanDump = await File.ReadAllTextAsync(@"Assets/dumpscreen_clean.txt"); + string cleanDump = await File.ReadAllTextAsync(@"Assets/DumpScreen.Clean.xml"); Windows.Data.Xml.Dom.XmlDocument doc = new(); doc.LoadXml(cleanDump); @@ -342,7 +342,7 @@ public async void GetAppStatusForegroundAsyncTest(string packageName, AppStatus public async void FindElementAsyncTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); Element element = await new DeviceClient(client, Device).FindElementAsync(); @@ -367,7 +367,7 @@ public async void FindElementAsyncTest() public async void FindElementsAsyncTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); Element[] elements = (await new DeviceClient(client, Device).FindElementsAsync()).ToArray(); @@ -389,7 +389,7 @@ public async void FindElementsAsyncTest() public async void FindAsyncElementsTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); List elements = []; await foreach (Element value in new DeviceClient(client, Device).FindAsyncElements()) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs index 08544aec..6e175e65 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs @@ -31,8 +31,8 @@ public void ConstructorNullTest() [Fact] public void DumpScreenStringTest() { - string dump = File.ReadAllText(@"Assets/dumpscreen.txt"); - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + string dump = File.ReadAllText(@"Assets/DumpScreen.txt"); + string cleanDump = File.ReadAllText(@"Assets/DumpScreen.Clean.xml"); DummyAdbClient client = new(); client.Commands["shell:uiautomator dump /dev/tty"] = dump; @@ -51,8 +51,8 @@ public void DumpScreenStringTest() [Fact] public void DumpScreenStringMIUITest() { - string miuidump = File.ReadAllText(@"Assets/dumpscreen_miui.txt"); - string cleanMIUIDump = File.ReadAllText(@"Assets/dumpscreen_miui_clean.txt"); + string miuidump = File.ReadAllText(@"Assets/DumpScreen.MIUI.txt"); + string cleanMIUIDump = File.ReadAllText(@"Assets/DumpScreen.Clean.xml"); DummyAdbClient client = new(); client.Commands["shell:uiautomator dump /dev/tty"] = miuidump; @@ -89,7 +89,7 @@ public void DumpScreenStringEmptyTest() public void DumpScreenStringErrorTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen_error.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.Error.txt"); _ = Assert.Throws(() => new DeviceClient(client, Device).DumpScreenString()); @@ -104,14 +104,14 @@ public void DumpScreenStringErrorTest() public void DumpScreenTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); XmlDocument xml = new DeviceClient(client, Device).DumpScreen(); Assert.Single(client.ReceivedCommands); Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + string cleanDump = File.ReadAllText(@"Assets/DumpScreen.Clean.xml"); XmlDocument doc = new(); doc.LoadXml(cleanDump); @@ -126,14 +126,14 @@ public void DumpScreenTest() public void DumpScreenWinRTTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); Windows.Data.Xml.Dom.XmlDocument xml = new DeviceClient(client, Device).DumpScreenWinRT(); Assert.Single(client.ReceivedCommands); Assert.Equal("shell:uiautomator dump /dev/tty", client.ReceivedCommands[0]); - string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt"); + string cleanDump = File.ReadAllText(@"Assets/DumpScreen.Clean.xml"); Windows.Data.Xml.Dom.XmlDocument doc = new(); doc.LoadXml(cleanDump); @@ -356,7 +356,7 @@ public void GetAppStatusForegroundTest(string packageName, AppStatus expected) public void FindElementTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); Element element = new DeviceClient(client, Device).FindElement(); @@ -381,7 +381,7 @@ public void FindElementTest() public void FindElementsTest() { DummyAdbClient client = new(); - client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/dumpscreen.txt"); + client.Commands["shell:uiautomator dump /dev/tty"] = File.ReadAllText(@"Assets/DumpScreen.txt"); Element[] elements = new DeviceClient(client, Device).FindElements().ToArray(); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/LinuxPathTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/LinuxPathTests.cs index 77723e5e..af667cb2 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/LinuxPathTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/LinuxPathTests.cs @@ -58,8 +58,8 @@ public void CombineTest() [Fact] public void CombineCurrentDirTest() { - string result = LinuxPath.Combine(".", "test.txt"); - Assert.Equal("./test.txt", result); + string result = LinuxPath.Combine(".", "Test.txt"); + Assert.Equal("./Test.txt", result); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 2d62a816..78dcf734 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -11,8 +11,8 @@ public async void InstallRemotePackageAsyncTest() DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["shell:pm install -r -t \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install \"/data/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r -t \"/data/base.apk\""] = "Success"; DeviceData device = new() { @@ -21,17 +21,17 @@ public async void InstallRemotePackageAsyncTest() PackageManager manager = new(adbClient, device); - await manager.InstallRemotePackageAsync("/data/test.apk", new InstallProgress(PackageInstallProgressState.Installing)); + await manager.InstallRemotePackageAsync("/data/base.apk", new InstallProgress(PackageInstallProgressState.Installing)); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install \"/data/base.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - await manager.InstallRemotePackageAsync("/data/test.apk", new InstallProgress(PackageInstallProgressState.Installing), default, "-r", "-t"); + await manager.InstallRemotePackageAsync("/data/base.apk", new InstallProgress(PackageInstallProgressState.Installing), default, "-r", "-t"); Assert.Single(adbClient.ReceivedCommands); - Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install -r -t \"/data/base.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -42,8 +42,8 @@ public async void InstallPackageAsyncTest() DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["shell:pm install \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:pm install \"/data/local/tmp/base.apk\""] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/base.apk\""] = string.Empty; DeviceData device = new() { @@ -52,21 +52,20 @@ public async void InstallPackageAsyncTest() PackageManager manager = new(adbClient, device, (c, d) => syncService); - await manager.InstallPackageAsync("Assets/test.txt", + await manager.InstallPackageAsync("Assets/TestApp/base.apk", new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.Installing, PackageInstallProgressState.PostInstall, PackageInstallProgressState.Finished)); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:rm \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[2]); Assert.Single(syncService.UploadedFiles); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); - - Factories.Reset(); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/base.apk")); } [Fact] @@ -80,8 +79,8 @@ public async void InstallMultipleRemotePackageAsyncTest() adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() @@ -91,7 +90,7 @@ public async void InstallMultipleRemotePackageAsyncTest() PackageManager manager = new(adbClient, device); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -100,13 +99,13 @@ await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split- Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3..5]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[3..5]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3..5]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[3..5]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], + await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -115,13 +114,13 @@ await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split- Assert.Equal(5, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2..4]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2..4]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", + await manager.InstallMultipleRemotePackageAsync(["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -129,13 +128,13 @@ await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/s Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1..3]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[1..3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", + await manager.InstallMultipleRemotePackageAsync(["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -143,8 +142,8 @@ await manager.InstallMultipleRemotePackageAsync(["/data/split-dpi.apk", "/data/s Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1..3]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[1..3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); } @@ -158,13 +157,13 @@ public async void InstallMultiplePackageAsyncTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; - adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; - adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; - adbClient.Commands["shell:rm \"/data/local/tmp/logcat.bin\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/base.apk\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\""] = string.Empty; DeviceData device = new() { @@ -173,8 +172,9 @@ public async void InstallMultiplePackageAsyncTest() PackageManager manager = new(adbClient, device, (c, d) => syncService); - await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], + await manager.InstallMultiplePackageAsync("Assets/TestApp/base.apk", ["Assets/TestApp/split_config.arm64_v8a.apk", "Assets/TestApp/split_config.xxhdpi.apk"], new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -184,24 +184,25 @@ await manager.InstallMultiplePackageAsync("Assets/test.txt", ["Assets/gapps.txt" Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3..5]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[3..5]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Contains("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6..8]); - Assert.Contains("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[6..8]); - Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:rm \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/base.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.arm64_v8a.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.xxhdpi.apk")); syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", + await manager.InstallMultiplePackageAsync(["Assets/TestApp/split_config.arm64_v8a.apk", "Assets/TestApp/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -211,15 +212,15 @@ await manager.InstallMultiplePackageAsync(["Assets/gapps.txt", "Assets/logcat.bi Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); - Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1..3]); - Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[1..3]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); - Assert.Contains("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[4..6]); - Assert.Contains("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4..6]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[5]); Assert.Equal(2, syncService.UploadedFiles.Count); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.arm64_v8a.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.xxhdpi.apk")); } [Fact] @@ -249,7 +250,7 @@ public async void GetPackageVersionInfoAsyncTest() }; DummyAdbClient client = new(); - client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); + client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/DumpSys.GApps.txt"); PackageManager manager = new(client, device, skipInit: true); VersionInfo versionInfo = await manager.GetVersionInfoAsync("com.google.android.gms"); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs index c0a86332..4181559a 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.cs @@ -39,8 +39,8 @@ public void InstallRemotePackageTest() DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["shell:pm install \"/data/test.apk\""] = "Success"; - adbClient.Commands["shell:pm install -r -t \"/data/test.apk\""] = "Success"; + adbClient.Commands["shell:pm install \"/data/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install -r -t \"/data/base.apk\""] = "Success"; DeviceData device = new() { @@ -49,17 +49,17 @@ public void InstallRemotePackageTest() PackageManager manager = new(adbClient, device); - manager.InstallRemotePackage("/data/test.apk", new InstallProgress(PackageInstallProgressState.Installing)); + manager.InstallRemotePackage("/data/base.apk", new InstallProgress(PackageInstallProgressState.Installing)); Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install \"/data/test.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install \"/data/base.apk\"", adbClient.ReceivedCommands[1]); adbClient.ReceivedCommands.Clear(); - manager.InstallRemotePackage("/data/test.apk", new InstallProgress(PackageInstallProgressState.Installing), "-r", "-t"); + manager.InstallRemotePackage("/data/base.apk", new InstallProgress(PackageInstallProgressState.Installing), "-r", "-t"); Assert.Single(adbClient.ReceivedCommands); - Assert.Equal("shell:pm install -r -t \"/data/test.apk\"", adbClient.ReceivedCommands[0]); + Assert.Equal("shell:pm install -r -t \"/data/base.apk\"", adbClient.ReceivedCommands[0]); } [Fact] @@ -70,8 +70,8 @@ public void InstallPackageTest() DummyAdbClient adbClient = new(); adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; - adbClient.Commands["shell:pm install \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; + adbClient.Commands["shell:pm install \"/data/local/tmp/base.apk\""] = "Success"; + adbClient.Commands["shell:rm \"/data/local/tmp/base.apk\""] = string.Empty; DeviceData device = new() { @@ -80,19 +80,20 @@ public void InstallPackageTest() PackageManager manager = new(adbClient, device, (c, d) => syncService); - manager.InstallPackage("Assets/test.txt", + manager.InstallPackage("Assets/TestApp/base.apk", new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.Installing, PackageInstallProgressState.PostInstall, PackageInstallProgressState.Finished)); Assert.Equal(3, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm install \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:rm \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[2]); Assert.Single(syncService.UploadedFiles); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/base.apk")); } [Fact] @@ -106,8 +107,8 @@ public void InstallMultipleRemotePackageTest() adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms -r -t"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/base.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; DeviceData device = new() @@ -117,7 +118,7 @@ public void InstallMultipleRemotePackageTest() PackageManager manager = new(adbClient, device); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -126,13 +127,13 @@ public void InstallMultipleRemotePackageTest() Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split-dpi.apk", "/data/split-abi.apk"], + manager.InstallMultipleRemotePackage("/data/base.apk", ["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -141,13 +142,13 @@ public void InstallMultipleRemotePackageTest() Assert.Equal(5, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", + manager.InstallMultipleRemotePackage(["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -155,13 +156,13 @@ public void InstallMultipleRemotePackageTest() Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); adbClient.ReceivedCommands.Clear(); - manager.InstallMultipleRemotePackage(["/data/split-dpi.apk", "/data/split-abi.apk"], "com.google.android.gms", + manager.InstallMultipleRemotePackage(["/data/split_config.arm64_v8a.apk", "/data/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -169,8 +170,8 @@ public void InstallMultipleRemotePackageTest() Assert.Equal(4, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms -r -t", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split-dpi.apk\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split-abi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); } @@ -184,13 +185,13 @@ public void InstallMultiplePackageTest() adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm install-create"] = "Success: created install session [936013062]"; adbClient.Commands["shell:pm install-create -p com.google.android.gms"] = "Success: created install session [936013062]"; - adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\""] = "Success"; - adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 base.apk \"/data/local/tmp/base.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\""] = "Success"; + adbClient.Commands["shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\""] = "Success"; adbClient.Commands["shell:pm install-commit 936013062"] = "Success"; - adbClient.Commands["shell:rm \"/data/local/tmp/test.txt\""] = string.Empty; - adbClient.Commands["shell:rm \"/data/local/tmp/gapps.txt\""] = string.Empty; - adbClient.Commands["shell:rm \"/data/local/tmp/logcat.bin\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/base.apk\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\""] = string.Empty; + adbClient.Commands["shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\""] = string.Empty; DeviceData device = new() { @@ -199,8 +200,9 @@ public void InstallMultiplePackageTest() PackageManager manager = new(adbClient, device, (c, d) => syncService); - manager.InstallMultiplePackage("Assets/test.txt", ["Assets/gapps.txt", "Assets/logcat.bin"], + manager.InstallMultiplePackage("Assets/TestApp/base.apk", ["Assets/TestApp/split_config.arm64_v8a.apk", "Assets/TestApp/split_config.xxhdpi.apk"], new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -210,24 +212,25 @@ public void InstallMultiplePackageTest() Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[7]); - Assert.Equal("shell:rm \"/data/local/tmp/test.txt\"", adbClient.ReceivedCommands[8]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[6]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[7]); + Assert.Equal("shell:rm \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/test.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/base.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.arm64_v8a.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.xxhdpi.apk")); syncService.UploadedFiles.Clear(); adbClient.ReceivedCommands.Clear(); - manager.InstallMultiplePackage(["Assets/gapps.txt", "Assets/logcat.bin"], "com.google.android.gms", + manager.InstallMultiplePackage(["Assets/TestApp/split_config.arm64_v8a.apk", "Assets/TestApp/split_config.xxhdpi.apk"], "com.google.android.gms", new InstallProgress( + PackageInstallProgressState.Preparing, PackageInstallProgressState.Uploading, PackageInstallProgressState.CreateSession, PackageInstallProgressState.WriteSession, @@ -237,15 +240,15 @@ public void InstallMultiplePackageTest() Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[2]); + Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1]); + Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:rm \"/data/local/tmp/gapps.txt\"", adbClient.ReceivedCommands[4]); - Assert.Equal("shell:rm \"/data/local/tmp/logcat.bin\"", adbClient.ReceivedCommands[5]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[4]); + Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[5]); Assert.Equal(2, syncService.UploadedFiles.Count); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/gapps.txt")); - Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/logcat.bin")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.arm64_v8a.apk")); + Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.xxhdpi.apk")); } [Fact] @@ -275,7 +278,7 @@ public void GetPackageVersionInfoTest() }; DummyAdbClient client = new(); - client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/gapps.txt"); + client.Commands["shell:dumpsys package com.google.android.gms"] = File.ReadAllText("Assets/DumpSys.GApps.txt"); PackageManager manager = new(client, device, skipInit: true); VersionInfo versionInfo = manager.GetVersionInfo("com.google.android.gms"); diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs index 55213e4e..5ca72ca5 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/Receivers/VersionInfoReceiverTests.cs @@ -37,7 +37,7 @@ public void GetVersionTest() Assert.Null(receiver.GetVersionName(" versionName")); Assert.Equal(string.Empty, receiver.GetVersionName(" versionName=")); - string dumpsys = string.Join(Environment.NewLine, File.ReadAllLines(@"Assets/dumpsys_package.txt")); + string dumpsys = string.Join(Environment.NewLine, File.ReadAllLines(@"Assets/DumpSys.Package.txt")); receiver = new VersionInfoReceiver(); StringReader reader = new(dumpsys); diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs index c5c07644..148bc6c3 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbClient.cs @@ -171,9 +171,9 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken) => throw new NotImplementedException(); - void IAdbClient.Install(DeviceData device, Stream apk, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.Install(DeviceData device, Stream apk, IProgress progress, params string[] arguments) => throw new NotImplementedException(); - Task IAdbClient.InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallAsync(DeviceData device, Stream apk, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); void IAdbClient.InstallCommit(DeviceData device, string session) => throw new NotImplementedException(); @@ -183,17 +183,17 @@ public Task ExecuteServerCommandAsync(string target, string command, IAdbSocket Task IAdbClient.InstallCreateAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - void IAdbClient.InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress progress, params string[] arguments) => throw new NotImplementedException(); - void IAdbClient.InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => throw new NotImplementedException(); + void IAdbClient.InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress progress, params string[] arguments) => throw new NotImplementedException(); - Task IAdbClient.InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - Task IAdbClient.InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); + Task IAdbClient.InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress progress, CancellationToken cancellationToken, params string[] arguments) => throw new NotImplementedException(); - void IAdbClient.InstallWrite(DeviceData device, Stream apk, string apkName, string session) => throw new NotImplementedException(); + void IAdbClient.InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress progress) => throw new NotImplementedException(); - Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken) => throw new NotImplementedException(); + Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress progress, CancellationToken cancellationToken) => throw new NotImplementedException(); void IAdbClient.KillAdb() => throw new NotImplementedException(); diff --git a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs index d75c7f19..72ab9c3c 100644 --- a/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs +++ b/AdvancedSharpAdbClient.Tests/Logs/LogTests.cs @@ -10,7 +10,7 @@ public class LogTests [Fact] public void ReadLogTest() { - using FileStream stream = File.OpenRead(@"Assets/logcat.bin"); + using FileStream stream = File.OpenRead(@"Assets/Logcat.bin"); using ShellStream shellStream = new(stream, false); LogReader reader = new(shellStream); @@ -39,7 +39,7 @@ public void ReadLogTest() [Fact] public async void ReadLogAsyncTest() { - await using FileStream stream = File.OpenRead(@"Assets/logcat.bin"); + await using FileStream stream = File.OpenRead(@"Assets/Logcat.bin"); await using ShellStream shellStream = new(stream, false); LogReader reader = new(shellStream); @@ -70,7 +70,7 @@ public void ReadEventLogTest() { // The data in this stream was read using a ShellStream, so the CRLF fixing // has already taken place. - using FileStream stream = File.OpenRead(@"Assets/logcatevents.bin"); + using FileStream stream = File.OpenRead(@"Assets/LogcatEvents.bin"); LogReader reader = new(stream); LogEntry entry = reader.ReadEntry(); @@ -104,7 +104,7 @@ public async void ReadEventLogAsyncTest() { // The data in this stream was read using a ShellStream, so the CRLF fixing // has already taken place. - await using FileStream stream = File.OpenRead(@"Assets/logcatevents.bin"); + await using FileStream stream = File.OpenRead(@"Assets/LogcatEvents.bin"); LogReader reader = new(stream); LogEntry entry = await reader.ReadEntryAsync(); diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs index e1440868..21507cc7 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferHeaderTests.cs @@ -15,7 +15,7 @@ public class FramebufferHeaderTests [Fact] public void ReadFramebufferTest() { - byte[] data = File.ReadAllBytes("Assets/framebufferheader-v1.bin"); + byte[] data = File.ReadAllBytes("Assets/FramebufferHeader.V1.bin"); FramebufferHeader header = [.. data]; @@ -45,7 +45,7 @@ public void ReadFramebufferTest() [Fact] public void ReadFramebufferV2Test() { - byte[] data = File.ReadAllBytes("Assets/framebufferheader-v2.bin"); + byte[] data = File.ReadAllBytes("Assets/FramebufferHeader.V2.bin"); FramebufferHeader header = [.. data]; @@ -77,9 +77,9 @@ public void ReadFramebufferV2Test() [SupportedOSPlatform("windows")] public void ToImageTest() { - byte[] data = File.ReadAllBytes("Assets/framebufferheader.bin"); + byte[] data = File.ReadAllBytes("Assets/FramebufferHeader.bin"); FramebufferHeader header = FramebufferHeader.Read(data); - byte[] framebuffer = File.ReadAllBytes("Assets/framebuffer.bin"); + byte[] framebuffer = File.ReadAllBytes("Assets/Framebuffer.bin"); using Bitmap image = header.ToImage(framebuffer); Assert.NotNull(image); Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); @@ -98,7 +98,7 @@ public void ToImageTest() [SupportedOSPlatform("windows")] public void ToImageEmptyTest() { - byte[] data = File.ReadAllBytes("Assets/framebufferheader-empty.bin"); + byte[] data = File.ReadAllBytes("Assets/FramebufferHeader.Empty.bin"); FramebufferHeader header = FramebufferHeader.Read(data); byte[] framebuffer = []; diff --git a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs index 5b38106f..bcf05f0f 100644 --- a/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs +++ b/AdvancedSharpAdbClient.Tests/Models/FramebufferTests.cs @@ -44,8 +44,8 @@ public void RefreshTest() socket.Requests.Add("host:transport:169.254.109.177:5555"); socket.Requests.Add("framebuffer:"); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebufferheader.bin")); - socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/framebuffer.bin")); + socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/FramebufferHeader.bin")); + socket.SyncDataReceived.Enqueue(File.ReadAllBytes("Assets/Framebuffer.bin")); using Framebuffer framebuffer = new(device, (endPoint) => socket); @@ -105,8 +105,8 @@ public async void RefreshAsyncTest() socket.Requests.Add("host:transport:169.254.109.177:5555"); socket.Requests.Add("framebuffer:"); - socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/framebufferheader.bin")); - socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/framebuffer.bin")); + socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/FramebufferHeader.bin")); + socket.SyncDataReceived.Enqueue(await File.ReadAllBytesAsync("Assets/Framebuffer.bin")); using Framebuffer framebuffer = new(device, (endPoint) => socket); diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index 9afb513b..d12e3b86 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Xunit; -namespace AdvancedSharpAdbClient +namespace AdvancedSharpAdbClient.Tests { public class SocketBasedTests { diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 7aefb0b8..65c639e2 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -153,7 +153,7 @@ public async void GetAsyncListingTest() public async void PullAsyncTest() { await using MemoryStream stream = new(); - byte[] content = await File.ReadAllBytesAsync("Assets/fstab.bin"); + byte[] content = await File.ReadAllBytesAsync("Assets/Fstab.bin"); byte[] contentLength = BitConverter.GetBytes(content.Length); await RunTestAsync( @@ -187,8 +187,8 @@ await RunTestAsync( [Fact] public async void PushAsyncTest() { - FileStream stream = File.OpenRead("Assets/fstab.bin"); - byte[] content = await File.ReadAllBytesAsync("Assets/fstab.bin"); + FileStream stream = File.OpenRead("Assets/Fstab.bin"); + byte[] content = await File.ReadAllBytesAsync("Assets/Fstab.bin"); byte[] contentMessage = [ .. SyncCommandConverter.GetBytes(SyncCommand.DATA), diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 7ffeabf3..4933d3df 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -104,7 +104,7 @@ public void GetListingTest() public void PullTest() { using MemoryStream stream = new(); - byte[] content = File.ReadAllBytes("Assets/fstab.bin"); + byte[] content = File.ReadAllBytes("Assets/Fstab.bin"); byte[] contentLength = BitConverter.GetBytes(content.Length); RunTest( @@ -138,8 +138,8 @@ public void PullTest() [Fact] public void PushTest() { - FileStream stream = File.OpenRead("Assets/fstab.bin"); - byte[] content = File.ReadAllBytes("Assets/fstab.bin"); + FileStream stream = File.OpenRead("Assets/Fstab.bin"); + byte[] content = File.ReadAllBytes("Assets/Fstab.bin"); byte[] contentMessage = [ .. SyncCommandConverter.GetBytes(SyncCommand.DATA), diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 1b37462d..bf6534f4 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -18,7 +18,7 @@ namespace AdvancedSharpAdbClient public partial class AdbClient { /// - public async Task GetAdbVersionAsync(CancellationToken cancellationToken = default) + public virtual async Task GetAdbVersionAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -30,7 +30,7 @@ public async Task GetAdbVersionAsync(CancellationToken cancellationToken = } /// - public async Task KillAdbAsync(CancellationToken cancellationToken = default) + public virtual async Task KillAdbAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SendAdbRequestAsync("host:kill", cancellationToken).ConfigureAwait(false); @@ -40,7 +40,7 @@ public async Task KillAdbAsync(CancellationToken cancellationToken = default) } /// - public async Task> GetDevicesAsync(CancellationToken cancellationToken = default) + public virtual async Task> GetDevicesAsync(CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -53,7 +53,7 @@ public async Task> GetDevicesAsync(CancellationToken can } /// - public async Task CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken = default) + public virtual async Task CreateForwardAsync(DeviceData device, string local, string remote, bool allowRebind, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -69,7 +69,7 @@ public async Task CreateForwardAsync(DeviceData device, string local, strin } /// - public async Task CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken = default) + public virtual async Task CreateReverseForwardAsync(DeviceData device, string remote, string local, bool allowRebind, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -87,7 +87,7 @@ public async Task CreateReverseForwardAsync(DeviceData device, string remot } /// - public async Task RemoveReverseForwardAsync(DeviceData device, string remote, CancellationToken cancellationToken = default) + public virtual async Task RemoveReverseForwardAsync(DeviceData device, string remote, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -99,7 +99,7 @@ public async Task RemoveReverseForwardAsync(DeviceData device, string remote, Ca } /// - public async Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -111,7 +111,7 @@ public async Task RemoveAllReverseForwardsAsync(DeviceData device, CancellationT } /// - public async Task RemoveForwardAsync(DeviceData device, int localPort, CancellationToken cancellationToken = default) + public virtual async Task RemoveForwardAsync(DeviceData device, int localPort, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -121,7 +121,7 @@ public async Task RemoveForwardAsync(DeviceData device, int localPort, Cancellat } /// - public async Task RemoveAllForwardsAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task RemoveAllForwardsAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -131,7 +131,7 @@ public async Task RemoveAllForwardsAsync(DeviceData device, CancellationToken ca } /// - public async Task> ListForwardAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task> ListForwardAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -145,7 +145,7 @@ public async Task> ListForwardAsync(DeviceData device, } /// - public async Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task> ListReverseForwardAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -161,7 +161,7 @@ public async Task> ListReverseForwardAsync(DeviceData d } /// - public async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteServerCommandAsync(string target, string command, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -169,7 +169,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, Encod } /// - public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); @@ -185,7 +185,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS } /// - public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteRemoteCommandAsync(string command, DeviceData device, Encoding encoding, CancellationToken cancellationToken = default) { EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); @@ -197,7 +197,7 @@ public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, E } /// - public async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteServerCommandAsync(string target, string command, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -205,7 +205,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IShel } /// - public async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteServerCommandAsync(string target, string command, IAdbSocket socket, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(encoding); @@ -251,7 +251,7 @@ public async Task ExecuteServerCommandAsync(string target, string command, IAdbS } /// - public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) + public virtual async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding, CancellationToken cancellationToken = default) { EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); @@ -263,7 +263,7 @@ public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, I } /// - public async Task GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task GetFrameBufferAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -275,7 +275,7 @@ public async Task GetFrameBufferAsync(DeviceData device, Cancellati } /// - public async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) + public virtual async Task RunLogServiceAsync(DeviceData device, Action messageSink, CancellationToken cancellationToken, params LogId[] logNames) { EnsureDevice(device); ExceptionExtensions.ThrowIfNull(messageSink); @@ -326,7 +326,7 @@ public async Task RunLogServiceAsync(DeviceData device, Action message } /// - public async Task RebootAsync(string into, DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task RebootAsync(string into, DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); @@ -338,7 +338,7 @@ public async Task RebootAsync(string into, DeviceData device, CancellationToken } /// - public async Task PairAsync(DnsEndPoint endpoint, string code, CancellationToken cancellationToken = default) + public virtual async Task PairAsync(DnsEndPoint endpoint, string code, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(endpoint); @@ -350,7 +350,7 @@ public async Task PairAsync(DnsEndPoint endpoint, string code, Cancellat } /// - public async Task ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken = default) + public virtual async Task ConnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(endpoint); @@ -362,7 +362,7 @@ public async Task ConnectAsync(DnsEndPoint endpoint, CancellationToken c } /// - public async Task DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken = default) + public virtual async Task DisconnectAsync(DnsEndPoint endpoint, CancellationToken cancellationToken = default) { ExceptionExtensions.ThrowIfNull(endpoint); @@ -374,10 +374,10 @@ public async Task DisconnectAsync(DnsEndPoint endpoint, CancellationToke } /// - public Task RootAsync(DeviceData device, CancellationToken cancellationToken = default) => RootAsync("root:", device, cancellationToken); + public virtual Task RootAsync(DeviceData device, CancellationToken cancellationToken = default) => RootAsync("root:", device, cancellationToken); /// - public Task UnrootAsync(DeviceData device, CancellationToken cancellationToken = default) => RootAsync("unroot:", device, cancellationToken); + public virtual Task UnrootAsync(DeviceData device, CancellationToken cancellationToken = default) => RootAsync("unroot:", device, cancellationToken); /// /// Restarts the ADB daemon running on the device with or without root privileges. @@ -422,8 +422,10 @@ protected async Task RootAsync(string request, DeviceData device, CancellationTo } /// - public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -455,18 +457,24 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken byte[] buffer = new byte[32 * 1024]; int read = 0; + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + #if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); - } #else while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0) { await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); - } #endif + totalBytesRead += read; + progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess)); + } + progress?.Report(new InstallProgressEventArgs(1, 1, 100d)); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = #if HAS_BUFFERS @@ -479,42 +487,58 @@ public async Task InstallAsync(DeviceData device, Stream apk, CancellationToken { throw new AdbException(value); } + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// - public async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(splitAPKs); ExceptionExtensions.ThrowIfNull(packageName); + if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) + { + throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream"); + } + + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false); - int i = 0; - await Extensions.WhenAll(splitAPKs.Select(async splitAPK => + int progressCount = 0; + int splitAPKsCount = splitAPKs.Count(); + Dictionary status = new(splitAPKsCount); + void OnSyncProgressChanged(string? sender, double args) { - if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) + lock (status) { - Debug.WriteLine("The apk stream must be a readable and seekable stream"); - return; + if (sender is null) + { + progressCount++; + } + else if (sender is string path) + { + status[path] = args; + } + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); } + } - try - { - await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } - })).ConfigureAwait(false); + int i = 0; + await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// - public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); @@ -524,34 +548,48 @@ public async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnume throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); } + if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) + { + throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream"); + } + + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false); - await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, cancellationToken).ConfigureAwait(false); + int splitAPKsCount = splitAPKs.Count(); + void OnMainSyncProgressChanged(string? sender, double args) => + progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); - int i = 0; - await Extensions.WhenAll(splitAPKs.Select(async splitAPK => + await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false); + + int progressCount = 1; + Dictionary status = new(splitAPKsCount); + void OnSplitSyncProgressChanged(string? sender, double args) { - if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) + lock (status) { - Debug.WriteLine("The apk stream must be a readable and seekable stream"); - return; + if (sender is null) + { + progressCount++; + } + else if (sender is string path) + { + status[path] = args; + } + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); } + } - try - { - await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } - })).ConfigureAwait(false); + int i = 0; + await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// - public async Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task InstallCreateAsync(DeviceData device, string? packageName, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); @@ -591,8 +629,10 @@ public async Task InstallCreateAsync(DeviceData device, string? packageN } /// - public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken = default) + public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null, CancellationToken cancellationToken = default) { + progress?.Report(0d); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); ExceptionExtensions.ThrowIfNull(apkName); @@ -619,17 +659,94 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam byte[] buffer = new byte[32 * 1024]; int read = 0; + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + #if HAS_BUFFERS while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) { await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); - } #else while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0) { await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); +#endif + totalBytesRead += read; + progress?.Report(totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + } + + read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); +#endif + + if (!value.Contains("Success")) + { + throw new AdbException(value); } + progress?.Report(100d); + } + + /// + /// Asynchronously write an apk into the given install session. + /// + /// The device on which to install the application. + /// A which represents the application to install. + /// The name of the application. + /// The session ID of the install session. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as a value between 0 and 100, representing the percentage of the apk which has been transferred. + /// A which can be used to cancel the asynchronous operation. + /// A which represents the asynchronous operation. + protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, Action? progress, CancellationToken cancellationToken = default) + { + progress?.Invoke(apkName, 0d); + + EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(apk); + ExceptionExtensions.ThrowIfNull(apkName); + ExceptionExtensions.ThrowIfNull(session); + + if (!apk.CanRead || !apk.CanSeek) + { + throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); + } + + StringBuilder requestBuilder = + new StringBuilder().Append($"exec:cmd package 'install-write'") + // add size parameter [required for streaming installs] + // do last to override any user specified value + .AppendFormat(" -S {0}", apk.Length) + .AppendFormat(" {0} {1}.apk", session, apkName); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); + + await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false); + _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false); + + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + +#if HAS_BUFFERS + while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) + { + await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); +#else + while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0) + { + await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif + totalBytesRead += read; + progress?.Invoke(apkName, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + } + progress?.Invoke(apkName, 100d); read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = @@ -643,10 +760,11 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam { throw new AdbException(value); } + progress?.Invoke(null, 100d); } /// - public async Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken = default) + public virtual async Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken = default) { using IAdbSocket socket = adbSocketFactory(EndPoint); await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false); @@ -663,7 +781,7 @@ public async Task InstallCommitAsync(DeviceData device, string session, Cancella } /// - public async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) + public virtual async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) { EnsureDevice(device); @@ -694,7 +812,7 @@ public async Task UninstallAsync(DeviceData device, string packageName, Cancella } /// - public async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default) + public virtual async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default) { EnsureDevice(device); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index dcc9f17b..37010e25 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; @@ -168,7 +167,7 @@ public static byte[] CreateAdbForwardRequest(string address, int port) } /// - public int GetAdbVersion() + public virtual int GetAdbVersion() { using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -180,7 +179,7 @@ public int GetAdbVersion() } /// - public void KillAdb() + public virtual void KillAdb() { using IAdbSocket socket = adbSocketFactory(EndPoint); socket.SendAdbRequest("host:kill"); @@ -190,7 +189,7 @@ public void KillAdb() } /// - public IEnumerable GetDevices() + public virtual IEnumerable GetDevices() { using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -203,7 +202,7 @@ public IEnumerable GetDevices() } /// - public int CreateForward(DeviceData device, string local, string remote, bool allowRebind) + public virtual int CreateForward(DeviceData device, string local, string remote, bool allowRebind) { EnsureDevice(device); @@ -219,7 +218,7 @@ public int CreateForward(DeviceData device, string local, string remote, bool al } /// - public int CreateReverseForward(DeviceData device, string remote, string local, bool allowRebind) + public virtual int CreateReverseForward(DeviceData device, string remote, string local, bool allowRebind) { EnsureDevice(device); @@ -237,7 +236,7 @@ public int CreateReverseForward(DeviceData device, string remote, string local, } /// - public void RemoveReverseForward(DeviceData device, string remote) + public virtual void RemoveReverseForward(DeviceData device, string remote) { EnsureDevice(device); @@ -249,7 +248,7 @@ public void RemoveReverseForward(DeviceData device, string remote) } /// - public void RemoveAllReverseForwards(DeviceData device) + public virtual void RemoveAllReverseForwards(DeviceData device) { EnsureDevice(device); @@ -261,7 +260,7 @@ public void RemoveAllReverseForwards(DeviceData device) } /// - public void RemoveForward(DeviceData device, int localPort) + public virtual void RemoveForward(DeviceData device, int localPort) { EnsureDevice(device); @@ -271,7 +270,7 @@ public void RemoveForward(DeviceData device, int localPort) } /// - public void RemoveAllForwards(DeviceData device) + public virtual void RemoveAllForwards(DeviceData device) { EnsureDevice(device); @@ -311,7 +310,7 @@ public IEnumerable ListReverseForward(DeviceData device) } /// - public void ExecuteServerCommand(string target, string command, Encoding encoding) + public virtual void ExecuteServerCommand(string target, string command, Encoding encoding) { ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -335,7 +334,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke } /// - public void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) + public virtual void ExecuteRemoteCommand(string command, DeviceData device, Encoding encoding) { EnsureDevice(device); ExceptionExtensions.ThrowIfNull(encoding); @@ -347,7 +346,7 @@ public void ExecuteRemoteCommand(string command, DeviceData device, Encoding enc } /// - public void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) + public virtual void ExecuteServerCommand(string target, string command, IShellOutputReceiver receiver, Encoding encoding) { ExceptionExtensions.ThrowIfNull(encoding); using IAdbSocket socket = adbSocketFactory(EndPoint); @@ -394,7 +393,7 @@ public void ExecuteServerCommand(string target, string command, IAdbSocket socke } /// - public void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) + public virtual void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding) { EnsureDevice(device); @@ -424,7 +423,7 @@ public Framebuffer GetFrameBuffer(DeviceData device) } /// - public void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) + public virtual void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames) { EnsureDevice(device); ExceptionExtensions.ThrowIfNull(messageSink); @@ -472,7 +471,7 @@ public void RunLogService(DeviceData device, Action messageSink, param } /// - public void Reboot(string into, DeviceData device) + public virtual void Reboot(string into, DeviceData device) { EnsureDevice(device); @@ -569,8 +568,10 @@ protected void Root(string request, DeviceData device) } /// - public void Install(DeviceData device, Stream apk, params string[] arguments) + public void Install(DeviceData device, Stream apk, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -602,18 +603,24 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) byte[] buffer = new byte[32 * 1024]; int read = 0; + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + #if HAS_BUFFERS while ((read = apk.Read(buffer)) > 0) { socket.Send(buffer.AsSpan(0, read)); - } #else while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) { socket.Send(buffer, read); - } #endif + totalBytesRead += read; + progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess)); + } + progress?.Report(new InstallProgressEventArgs(1, 1, 100d)); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); read = socket.Read(buffer); string value = #if HAS_BUFFERS @@ -626,42 +633,61 @@ public void Install(DeviceData device, Stream apk, params string[] arguments) { throw new AdbException(value); } + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// - public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) + public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(splitAPKs); ExceptionExtensions.ThrowIfNull(packageName); + if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) + { + throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream"); + } + + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); string session = InstallCreate(device, packageName, arguments); - int i = 0; - foreach (Stream splitAPK in splitAPKs) + int progressCount = 0; + int splitAPKsCount = splitAPKs.Count(); + Dictionary status = new(splitAPKsCount); + void OnSyncProgressChanged(string? sender, double args) { - if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) + lock (status) { - Debug.WriteLine("The apk stream must be a readable and seekable stream"); - continue; + if (sender is null) + { + progressCount++; + } + else if (sender is string path) + { + status[path] = args; + } + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); } + } - try - { - InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + int i = 0; + foreach (Stream splitAPK in splitAPKs) + { + InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged); } + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallCommit(device, session); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// - public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) + public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); @@ -671,30 +697,47 @@ public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable apk == null || !apk.CanRead || !apk.CanSeek)) + { + throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream"); + } + + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); string session = InstallCreate(device, null, arguments); - InstallWrite(device, baseAPK, nameof(baseAPK), session); + int splitAPKsCount = splitAPKs.Count(); + void OnMainSyncProgressChanged(string? sender, double args) => + progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); - int i = 0; - foreach (Stream splitAPK in splitAPKs) + InstallWrite(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged); + + int progressCount = 1; + Dictionary status = new(splitAPKsCount); + void OnSplitSyncProgressChanged(string? sender, double args) { - if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek) + lock (status) { - Debug.WriteLine("The apk stream must be a readable and seekable stream"); - continue; + if (sender is null) + { + progressCount++; + } + else if (sender is string path) + { + status[path] = args; + } + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); } + } - try - { - InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } + int i = 0; + foreach (Stream splitAPK in splitAPKs) + { + InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged); } + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); InstallCommit(device, session); + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Finished)); } /// @@ -737,8 +780,10 @@ public string InstallCreate(DeviceData device, string? packageName = null, param } /// - public void InstallWrite(DeviceData device, Stream apk, string apkName, string session) + public void InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null) { + progress?.Report(0d); + EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); ExceptionExtensions.ThrowIfNull(apkName); @@ -765,18 +810,92 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s byte[] buffer = new byte[32 * 1024]; int read = 0; + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + #if HAS_BUFFERS while ((read = apk.Read(buffer)) > 0) { socket.Send(buffer.AsSpan(0, read)); - } #else while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) { socket.Send(buffer, read); +#endif + totalBytesRead += read; + progress?.Report(totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); } + + read = socket.Read(buffer); + string value = +#if HAS_BUFFERS + Encoding.UTF8.GetString(buffer.AsSpan(0, read)); +#else + Encoding.UTF8.GetString(buffer, 0, read); #endif + if (!value.Contains("Success")) + { + throw new AdbException(value); + } + progress?.Report(100d); + } + + /// + /// Write an apk into the given install session. + /// + /// The device on which to install the application. + /// A which represents the application to install. + /// The name of the application. + /// The session ID of the install session. + /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the apk which has been transferred. + protected virtual void InstallWrite(DeviceData device, Stream apk, string apkName, string session, Action? progress) + { + progress?.Invoke(apkName, 0d); + + EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(apk); + ExceptionExtensions.ThrowIfNull(apkName); + ExceptionExtensions.ThrowIfNull(session); + + if (!apk.CanRead || !apk.CanSeek) + { + throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream"); + } + + StringBuilder requestBuilder = + new StringBuilder().Append($"exec:cmd package 'install-write'") + // add size parameter [required for streaming installs] + // do last to override any user specified value + .AppendFormat(" -S {0}", apk.Length) + .AppendFormat(" {0} {1}.apk", session, apkName); + + using IAdbSocket socket = adbSocketFactory(EndPoint); + socket.SetDevice(device); + + socket.SendAdbRequest(requestBuilder.ToString()); + _ = socket.ReadAdbResponse(); + + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + long totalBytesToProcess = apk.Length; + long totalBytesRead = 0; + +#if HAS_BUFFERS + while ((read = apk.Read(buffer)) > 0) + { + socket.Send(buffer.AsSpan(0, read)); +#else + while ((read = apk.Read(buffer, 0, buffer.Length)) > 0) + { + socket.Send(buffer, read); +#endif + totalBytesRead += read; + progress?.Invoke(apkName, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + } + progress?.Invoke(apkName, 100d); + read = socket.Read(buffer); string value = #if HAS_BUFFERS @@ -789,10 +908,11 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s { throw new AdbException(value); } + progress?.Invoke(null, 100d); } /// - public void InstallCommit(DeviceData device, string session) + public virtual void InstallCommit(DeviceData device, string session) { EnsureDevice(device); @@ -811,7 +931,7 @@ public void InstallCommit(DeviceData device, string session) } /// - public void Uninstall(DeviceData device, string packageName, params string[] arguments) + public virtual void Uninstall(DeviceData device, string packageName, params string[] arguments) { EnsureDevice(device); @@ -842,7 +962,7 @@ public void Uninstall(DeviceData device, string packageName, params string[] arg } /// - public IEnumerable GetFeatureSet(DeviceData device) + public virtual IEnumerable GetFeatureSet(DeviceData device) { EnsureDevice(device); diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs index 710c9839..0e5bfb17 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/Models/Enums/PackageInstallProgressState.cs @@ -9,6 +9,11 @@ namespace AdvancedSharpAdbClient.DeviceCommands.Models /// public enum PackageInstallProgressState { + /// + /// Preparing the installation. + /// + Preparing, + /// /// Uploading packages to the device. /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 348588c0..9bb1ded7 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -48,6 +48,8 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d /// A which represents the asynchronous operation. public virtual async Task InstallPackageAsync(string packageFilePath, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => @@ -111,6 +113,8 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, IProg /// A which represents the asynchronous operation. public virtual async Task InstallMultiplePackageAsync(string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); int splitPackageFileCount = splitPackageFilePaths.Count(); @@ -184,6 +188,8 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// A which represents the asynchronous operation. public virtual async Task InstallMultiplePackageAsync(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); int splitPackageFileCount = splitPackageFilePaths.Count(); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 4ed37369..86c751aa 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -142,6 +142,8 @@ public virtual void RefreshPackages() /// The arguments to pass to adb install. public virtual void InstallPackage(string packageFilePath, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => @@ -201,6 +203,8 @@ public virtual void InstallRemotePackage(string remoteFilePath, IProgressThe arguments to pass to pm install-create. public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); int splitPackageFileCount = splitPackageFilePaths.Count(); @@ -260,6 +264,8 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg /// The arguments to pass to pm install-create. public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, params string[] arguments) { + progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); + ValidateDevice(); int splitPackageFileCount = splitPackageFilePaths.Count(); @@ -444,7 +450,7 @@ protected void ValidateDevice() /// If fatal error occurred when pushing file. protected virtual string SyncPackageToDevice(string localFilePath, Action? progress) { - progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 100)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0L, 100L)); ValidateDevice(); @@ -480,7 +486,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using System.Threading; @@ -270,41 +267,7 @@ public static Task ConnectAsync(this IAdbClient client, string host, int client.ConnectAsync(Extensions.CreateDnsEndPoint(host, port), cancellationToken); /// - /// Asynchronously installs an Android application on an device. - /// - /// An instance of a class that implements the interface. - /// The device on which to install the application. - /// A which represents the application to install. - /// The arguments to pass to adb install. - /// A which represents the asynchronous operation. - public static Task InstallAsync(this IAdbClient client, DeviceData device, Stream apk, params string[] arguments) => client.InstallAsync(device, apk, default, arguments); - - /// - /// Asynchronously push multiple APKs to the device and install them. - /// - /// An instance of a class that implements the interface. - /// The device on which to install the application. - /// s which represents the split APKs to install. - /// The package name of the base APK to install. - /// The arguments to pass to adb install-create. - /// A which represents the asynchronous operation. - public static Task InstallMultipleAsync(this IAdbClient client, DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments) => - client.InstallMultipleAsync(device, splitAPKs, packageName, default, arguments); - - /// - /// Asynchronously push multiple APKs to the device and install them. - /// - /// An instance of a class that implements the interface. - /// The device on which to install the application. - /// A which represents the base APK to install. - /// s which represents the split APKs to install. - /// The arguments to pass to adb install-create. - /// A which represents the asynchronous operation. - public static Task InstallMultipleAsync(this IAdbClient client, DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments) => - client.InstallMultipleAsync(device, baseAPK, splitAPKs, default, arguments); - - /// - /// Like "install", but starts an install session ssynchronously. + /// Like "install", but starts an install session synchronously. /// /// An instance of a class that implements the interface. /// The device on which to install the application. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index 5163d153..ca3b4f33 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -306,10 +306,12 @@ public partial interface IAdbClient /// /// The device on which to install the application. /// A which represents the application to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install. /// A which represents the asynchronous operation. - Task InstallAsync(DeviceData device, Stream apk, CancellationToken cancellationToken, params string[] arguments); + Task InstallAsync(DeviceData device, Stream apk, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); /// /// Asynchronously push multiple APKs to the device and install them. @@ -317,10 +319,12 @@ public partial interface IAdbClient /// The device on which to install the application. /// s which represents the split APKs to install. /// The package name of the base APK to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install-create. /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, CancellationToken cancellationToken, params string[] arguments); + Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); /// /// Asynchronously push multiple APKs to the device and install them. @@ -328,10 +332,12 @@ public partial interface IAdbClient /// The device on which to install the application. /// A which represents the base APK to install. /// s which represents the split APKs to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install-create. /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, CancellationToken cancellationToken, params string[] arguments); + Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); /// /// Like "install", but starts an install session asynchronously. @@ -350,9 +356,11 @@ public partial interface IAdbClient /// A which represents the application to install. /// The name of the application. /// The session ID of the install session. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as a value between 0 and 100, representing the percentage of the apk which has been transferred. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, CancellationToken cancellationToken); + Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress? progress, CancellationToken cancellationToken); /// /// Asynchronously commit the given active install session, installing the app. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 592f359d..227f5a65 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -17,6 +17,12 @@ namespace AdvancedSharpAdbClient /// public partial interface IAdbClient { + // remount: not implemented + // dev: not implemented + // track-jdwp: not implemented + // localreserved: not implemented + // localabstract: not implemented + /// /// Gets the at which the Android Debug Bridge server is listening. /// @@ -236,15 +242,6 @@ public partial interface IAdbClient /// The encoding to use when parsing the command output. void ExecuteRemoteCommand(string command, DeviceData device, IShellOutputReceiver receiver, Encoding encoding); - // shell: not implemented - // remount: not implemented - // dev: not implemented - // tcp: not implemented - // tcp:: not implemented - // local: not implemented - // localreserved: not implemented - // localabstract: not implemented - /// /// Gets a which contains the framebuffer data for this device. The framebuffer data can be refreshed, /// giving you high performance access to the device's framebuffer. @@ -269,12 +266,7 @@ public partial interface IAdbClient /// A callback which will receive the event log messages as they are received. /// Optionally, the names of the logs to receive. void RunLogService(DeviceData device, Action messageSink, params LogId[] logNames); - - // jdwp:: not implemented - // track-jdwp: not implemented - // sync: not implemented - // reverse:: not implemented - + /// /// Reboots the specified device in to the specified mode. /// @@ -321,8 +313,10 @@ public partial interface IAdbClient /// /// The device on which to install the application. /// A which represents the application to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install. - void Install(DeviceData device, Stream apk, params string[] arguments); + void Install(DeviceData device, Stream apk, IProgress? progress, params string[] arguments); /// /// Push multiple APKs to the device and install them. @@ -330,8 +324,10 @@ public partial interface IAdbClient /// The device on which to install the application. /// s which represents the split APKs to install. /// The package name of the base APK to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install-create. - void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, params string[] arguments); + void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, params string[] arguments); /// /// Push multiple APKs to the device and install them. @@ -339,8 +335,10 @@ public partial interface IAdbClient /// The device on which to install the application. /// A which represents the base APK to install. /// s which represents the split APKs to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install-create. - void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, params string[] arguments); + void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, params string[] arguments); /// /// Like "install", but starts an install session. @@ -358,7 +356,9 @@ public partial interface IAdbClient /// A which represents the application to install. /// The name of the application. /// The session ID of the install session. - void InstallWrite(DeviceData device, Stream apk, string apkName, string session); + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as a value between 0 and 100, representing the percentage of the apk which has been transferred. + void InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress? progress); /// /// Commit the given active install session, installing the app. From 38a91518a17b33b9279532ec910a45ba3d7a7b84 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 00:00:22 +0800 Subject: [PATCH 07/12] Add test for InstallMultiple --- .../AdbClientTests.Async.cs | 138 ++++++++++++- .../AdbClientTests.cs | 188 ++++++++++++++++-- .../SocketBasedTests.cs | 98 ++++----- .../SyncServiceTests.Async.cs | 4 +- .../SyncServiceTests.cs | 4 +- 5 files changed, 360 insertions(+), 72 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs index 09760d04..c95bcf51 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs @@ -677,7 +677,7 @@ await RunTestAsync( NoSyncRequests, NoSyncResponses, [response], - applicationDataChunks.ToArray(), + applicationDataChunks, () => TestClient.InstallAsync(Device, stream, new InstallProgress( PackageInstallProgressState.Preparing, @@ -687,6 +687,140 @@ await RunTestAsync( } } + /// + /// Tests the method. + /// + [Fact] + public async void InstallMultipleAsyncTest() + { + // The app data is sent in chunks of 32 kb + List applicationDataChunks = []; + + await using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + await using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"); + + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-create' -p com.google.android.gms", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk", + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-commit' 936013062" + ]; + + byte[][] responses = + [ + Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n") + ]; + + await using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n")); + await using MemoryStream commitStream = new("Success\n"u8.ToArray()); + + await RunTestAsync( + OkResponses(6), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + responses, + applicationDataChunks, + [sessionStream, commitStream], + () => TestClient.InstallMultipleAsync(Device, [abiStream], "com.google.android.gms", + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); + } + + /// + /// Tests the method. + /// + [Fact] + public async void InstallMultipleWithBaseAsyncTest() + { + // The app data is sent in chunks of 32 kb + List applicationDataChunks = []; + + await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + await using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + await using FileStream baseStream = File.OpenRead("Assets/TestApp/base.apk"); + await using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"); + + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-create'", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {baseStream.Length} 936013062 baseAPK.apk", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk", + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-commit' 936013062" + ]; + + byte[][] responses = + [ + Encoding.ASCII.GetBytes($"Success: streamed {baseStream.Length} bytes\n"), + Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n") + ]; + + using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n")); + using MemoryStream commitStream = new("Success\n"u8.ToArray()); + + await RunTestAsync( + OkResponses(8), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + responses, + applicationDataChunks, + [sessionStream, commitStream], + () => TestClient.InstallMultipleAsync(Device, baseStream, [abiStream], + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); + } + /// /// Tests the method. /// @@ -758,7 +892,7 @@ await RunTestAsync( NoSyncRequests, NoSyncResponses, [response], - applicationDataChunks.ToArray(), + applicationDataChunks, () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062", progress)); } } diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs index 29aaad6f..754f3841 100644 --- a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs +++ b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs @@ -785,7 +785,7 @@ public void InstallTest() NoSyncRequests, NoSyncResponses, [response], - applicationDataChunks.ToArray(), + applicationDataChunks, () => TestClient.Install(Device, stream, new InstallProgress( PackageInstallProgressState.Preparing, @@ -795,6 +795,172 @@ public void InstallTest() } } + /// + /// Tests the method. + /// + [Fact] + public void InstallMultipleTest() + { + // The app data is sent in chunks of 32 kb + List applicationDataChunks = []; + + using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"); + using FileStream dpiStream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk"); + + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-create' -p com.google.android.gms", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {dpiStream.Length} 936013062 splitAPK1.apk", + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-commit' 936013062" + ]; + + byte[][] responses = + [ + Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n"), + Encoding.ASCII.GetBytes($"Success: streamed {dpiStream.Length} bytes\n") + ]; + + using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n")); + using MemoryStream commitStream = new("Success\n"u8.ToArray()); + + RunTest( + OkResponses(8), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + responses, + applicationDataChunks, + [sessionStream, commitStream], + () => TestClient.InstallMultiple(Device, [abiStream, dpiStream], "com.google.android.gms", + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); + } + + /// + /// Tests the method. + /// + [Fact] + public void InstallMultipleWithBaseTest() + { + // The app data is sent in chunks of 32 kb + List applicationDataChunks = []; + + using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk")) + { + byte[] buffer = new byte[32 * 1024]; + int read = 0; + + while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0) + { + byte[] array = buffer.AsSpan(0, read).ToArray(); + applicationDataChunks.Add(array); + } + } + + using FileStream baseStream = File.OpenRead("Assets/TestApp/base.apk"); + using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"); + using FileStream dpiStream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk"); + + string[] requests = + [ + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-create'", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {baseStream.Length} 936013062 baseAPK.apk", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk", + "host:transport:169.254.109.177:5555", + $"exec:cmd package 'install-write' -S {dpiStream.Length} 936013062 splitAPK1.apk", + "host:transport:169.254.109.177:5555", + "exec:cmd package 'install-commit' 936013062" + ]; + + byte[][] responses = + [ + Encoding.ASCII.GetBytes($"Success: streamed {baseStream.Length} bytes\n"), + Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n"), + Encoding.ASCII.GetBytes($"Success: streamed {dpiStream.Length} bytes\n") + ]; + + using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n")); + using MemoryStream commitStream = new("Success\n"u8.ToArray()); + + RunTest( + OkResponses(10), + NoResponseMessages, + requests, + NoSyncRequests, + NoSyncResponses, + responses, + applicationDataChunks, + [sessionStream, commitStream], + () => TestClient.InstallMultiple(Device, baseStream, [abiStream, dpiStream], + new InstallProgress( + PackageInstallProgressState.Preparing, + PackageInstallProgressState.CreateSession, + PackageInstallProgressState.Uploading, + PackageInstallProgressState.Installing, + PackageInstallProgressState.Finished))); + } + /// /// Tests the method. /// @@ -851,14 +1017,6 @@ public void InstallWriteTest() byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Length} bytes\n"); - double temp = 0; - Progress progress = new(); - progress.ProgressChanged += (sender, args) => - { - Assert.True(temp <= args, $"{nameof(args)}: {args} is less than {temp}."); - temp = args; - }; - RunTest( OkResponses(2), NoResponseMessages, @@ -866,8 +1024,8 @@ public void InstallWriteTest() NoSyncRequests, NoSyncResponses, [response], - applicationDataChunks.ToArray(), - () => TestClient.InstallWrite(Device, stream, "base", "936013062", progress)); + applicationDataChunks, + () => TestClient.InstallWrite(Device, stream, "base", "936013062", new InstallProgress())); } } @@ -987,7 +1145,7 @@ private void RunCreateForwardTest(Action test, string forwardString) () => test(Device)); } - private struct InstallProgress(params PackageInstallProgressState[] states) : IProgress + private struct InstallProgress(params PackageInstallProgressState[] states) : IProgress, IProgress { private PackageInstallProgressState? state; private int packageFinished; @@ -1033,6 +1191,12 @@ or PackageInstallProgressState.Installing packageFinished = value.PackageFinished; uploadProgress = value.UploadProgress; } + + public void Report(double value) + { + Assert.True(uploadProgress <= value, $"{nameof(value)}: {value} is less than {uploadProgress}."); + uploadProgress = value; + } } } } diff --git a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs index d12e3b86..fa0cb53c 100644 --- a/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs +++ b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs @@ -267,11 +267,11 @@ protected void RunTest( Assert.Empty(Socket.ShellStreams); // Make sure a request was sent - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { @@ -280,7 +280,7 @@ protected void RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -291,23 +291,23 @@ protected void RunTest( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToArray(), Socket.Responses); - Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + Assert.Equal(responses, Socket.Responses); + Assert.Equal(responseMessages, Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + Assert.Equal(syncResponses, Socket.SyncResponses); } else { @@ -316,7 +316,7 @@ protected void RunTest( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + Assert.Equal(syncDataReceived, Socket.SyncDataReceived); } else { @@ -325,7 +325,7 @@ protected void RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -334,7 +334,7 @@ protected void RunTest( if (shellStreams != null) { - Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + Assert.Equal(shellStreams, Socket.ShellStreams); } else { @@ -560,11 +560,11 @@ protected TResult RunTest( Assert.Empty(Socket.ShellStreams); // Make sure a request was sent - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { @@ -573,7 +573,7 @@ protected TResult RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -584,23 +584,23 @@ protected TResult RunTest( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToArray(), Socket.Responses); - Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + Assert.Equal(responses, Socket.Responses); + Assert.Equal(responseMessages, Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + Assert.Equal(syncResponses, Socket.SyncResponses); } else { @@ -609,7 +609,7 @@ protected TResult RunTest( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + Assert.Equal(syncDataReceived, Socket.SyncDataReceived); } else { @@ -618,7 +618,7 @@ protected TResult RunTest( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -627,7 +627,7 @@ protected TResult RunTest( if (shellStreams != null) { - Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + Assert.Equal(shellStreams, Socket.ShellStreams); } else { @@ -853,11 +853,11 @@ protected async Task RunTestAsync( Assert.Empty(Socket.ShellStreams); // Make sure a request was sent - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { @@ -866,7 +866,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -877,23 +877,23 @@ protected async Task RunTestAsync( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToArray(), Socket.Responses); - Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + Assert.Equal(responses, Socket.Responses); + Assert.Equal(responseMessages, Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + Assert.Equal(syncResponses, Socket.SyncResponses); } else { @@ -902,7 +902,7 @@ protected async Task RunTestAsync( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + Assert.Equal(syncDataReceived, Socket.SyncDataReceived); } else { @@ -911,7 +911,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -920,7 +920,7 @@ protected async Task RunTestAsync( if (shellStreams != null) { - Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + Assert.Equal(shellStreams, Socket.ShellStreams); } else { @@ -1150,11 +1150,11 @@ protected async Task RunTestAsync( Assert.Empty(Socket.ShellStreams); // Make sure a request was sent - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { @@ -1163,7 +1163,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -1174,23 +1174,23 @@ protected async Task RunTestAsync( { // Make sure the traffic sent on the wire matches the traffic // we have defined in our unit test. - Assert.Equal(requests.ToArray(), Socket.Requests); + Assert.Equal(requests, Socket.Requests); if (syncRequests != null) { - Assert.Equal(syncRequests.ToArray(), Socket.SyncRequests); + Assert.Equal(syncRequests, Socket.SyncRequests); } else { Assert.Empty(Socket.SyncRequests); } - Assert.Equal(responses.ToArray(), Socket.Responses); - Assert.Equal(responseMessages.ToArray(), Socket.ResponseMessages); + Assert.Equal(responses, Socket.Responses); + Assert.Equal(responseMessages, Socket.ResponseMessages); if (syncResponses != null) { - Assert.Equal(syncResponses.ToArray(), Socket.SyncResponses); + Assert.Equal(syncResponses, Socket.SyncResponses); } else { @@ -1199,7 +1199,7 @@ protected async Task RunTestAsync( if (syncDataReceived != null) { - AssertEqual(syncDataReceived.ToArray(), Socket.SyncDataReceived.ToArray()); + Assert.Equal(syncDataReceived, Socket.SyncDataReceived); } else { @@ -1208,7 +1208,7 @@ protected async Task RunTestAsync( if (syncDataSent != null) { - AssertEqual(syncDataSent.ToArray(), Socket.SyncDataSent.ToArray()); + Assert.Equal(syncDataSent, Socket.SyncDataSent); } else { @@ -1217,7 +1217,7 @@ protected async Task RunTestAsync( if (shellStreams != null) { - Assert.Equal(shellStreams.ToArray(), [.. Socket.ShellStreams]); + Assert.Equal(shellStreams, [.. Socket.ShellStreams]); } else { @@ -1231,15 +1231,5 @@ protected async Task RunTestAsync( #endregion protected static IEnumerable OkResponses(int count) => Enumerable.Repeat(AdbResponse.OK, count); - - private static void AssertEqual(IList expected, IList actual) - { - Assert.Equal(expected.Count, actual.Count); - - for (int i = 0; i < expected.Count; i++) - { - Assert.Equal(expected[i], actual[i]); - } - } } } diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs index 65c639e2..2547944a 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs @@ -147,7 +147,7 @@ public async void GetAsyncListingTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void PullAsyncTest() @@ -182,7 +182,7 @@ await RunTestAsync( } /// - /// Tests the method. + /// Tests the method. /// [Fact] public async void PushAsyncTest() diff --git a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs index 4933d3df..033048c2 100644 --- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs +++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs @@ -98,7 +98,7 @@ public void GetListingTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void PullTest() @@ -133,7 +133,7 @@ public void PullTest() } /// - /// Tests the method. + /// Tests the method. /// [Fact] public void PushTest() From d2f5245daa8718ee42460bc201c53c4587dc5d88 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 00:14:40 +0800 Subject: [PATCH 08/12] Fix assets files name --- .../Assets/{dumpscreen.txt => DumpScreen.txt} | 0 .../Assets/{framebuffer.bin => Framebuffer.bin} | 0 ...{framebufferheader.bin => FramebufferHeader.bin} | Bin .../Assets/{fstab.bin => Fstab.bin} | 0 .../Assets/{logcat.bin => Logcat.bin} | Bin .../Assets/{logcatevents.bin => LogcatEvents.bin} | Bin .../Assets/{printenv.txt => PrintEnv.txt} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename AdvancedSharpAdbClient.Tests/Assets/{dumpscreen.txt => DumpScreen.txt} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{framebuffer.bin => Framebuffer.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{framebufferheader.bin => FramebufferHeader.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{fstab.bin => Fstab.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{logcat.bin => Logcat.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{logcatevents.bin => LogcatEvents.bin} (100%) rename AdvancedSharpAdbClient.Tests/Assets/{printenv.txt => PrintEnv.txt} (100%) diff --git a/AdvancedSharpAdbClient.Tests/Assets/dumpscreen.txt b/AdvancedSharpAdbClient.Tests/Assets/DumpScreen.txt similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/dumpscreen.txt rename to AdvancedSharpAdbClient.Tests/Assets/DumpScreen.txt diff --git a/AdvancedSharpAdbClient.Tests/Assets/framebuffer.bin b/AdvancedSharpAdbClient.Tests/Assets/Framebuffer.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/framebuffer.bin rename to AdvancedSharpAdbClient.Tests/Assets/Framebuffer.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/framebufferheader.bin b/AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/framebufferheader.bin rename to AdvancedSharpAdbClient.Tests/Assets/FramebufferHeader.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/fstab.bin b/AdvancedSharpAdbClient.Tests/Assets/Fstab.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/fstab.bin rename to AdvancedSharpAdbClient.Tests/Assets/Fstab.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/logcat.bin b/AdvancedSharpAdbClient.Tests/Assets/Logcat.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/logcat.bin rename to AdvancedSharpAdbClient.Tests/Assets/Logcat.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/logcatevents.bin b/AdvancedSharpAdbClient.Tests/Assets/LogcatEvents.bin similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/logcatevents.bin rename to AdvancedSharpAdbClient.Tests/Assets/LogcatEvents.bin diff --git a/AdvancedSharpAdbClient.Tests/Assets/printenv.txt b/AdvancedSharpAdbClient.Tests/Assets/PrintEnv.txt similarity index 100% rename from AdvancedSharpAdbClient.Tests/Assets/printenv.txt rename to AdvancedSharpAdbClient.Tests/Assets/PrintEnv.txt From 9116486e9b1ba65a5cfeb692f7186cc5f07ec9e7 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 17:16:07 +0800 Subject: [PATCH 09/12] Move InstallProgressEventArgs to root --- AdvancedSharpAdbClient/AdbClient.Async.cs | 18 ++++++++-------- AdvancedSharpAdbClient/AdbClient.cs | 18 ++++++++-------- .../DeviceCommands/PackageManager.cs | 4 ++-- .../Enums/PackageInstallProgressState.cs | 2 +- .../Models/Enums/UnixFileType.cs | 14 ++++++------- .../InstallProgress.EventArgs.cs} | 21 ++++++++++++------- .../Models/SyncService.EventArgs.cs | 2 +- 7 files changed, 43 insertions(+), 36 deletions(-) rename AdvancedSharpAdbClient/{DeviceCommands => }/Models/Enums/PackageInstallProgressState.cs (95%) rename AdvancedSharpAdbClient/{DeviceCommands/Models/InstallProgressEventArgs.cs => Models/InstallProgress.EventArgs.cs} (66%) diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index bf6534f4..1fbafe94 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -470,9 +470,9 @@ public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress< await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += read; - progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess)); + progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess)); } - progress?.Report(new InstallProgressEventArgs(1, 1, 100d)); + progress?.Report(new InstallProgressEventArgs(1, 1, 100)); progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); @@ -631,7 +631,7 @@ public virtual async Task InstallCreateAsync(DeviceData device, string? /// public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null, CancellationToken cancellationToken = default) { - progress?.Report(0d); + progress?.Report(0); EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -672,7 +672,7 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += read; - progress?.Report(totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + progress?.Report(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess); } read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); @@ -687,7 +687,7 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin { throw new AdbException(value); } - progress?.Report(100d); + progress?.Report(100); } /// @@ -703,7 +703,7 @@ public virtual async Task InstallWriteAsync(DeviceData device, Stream apk, strin /// A which represents the asynchronous operation. protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, Action? progress, CancellationToken cancellationToken = default) { - progress?.Invoke(apkName, 0d); + progress?.Invoke(apkName, 0); EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -744,9 +744,9 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false); #endif totalBytesRead += read; - progress?.Invoke(apkName, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + progress?.Invoke(apkName, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess); } - progress?.Invoke(apkName, 100d); + progress?.Invoke(apkName, 100); read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); string value = @@ -760,7 +760,7 @@ protected virtual async Task InstallWriteAsync(DeviceData device, Stream apk, st { throw new AdbException(value); } - progress?.Invoke(null, 100d); + progress?.Invoke(null, 100); } /// diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index 37010e25..c95ef2c2 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -616,9 +616,9 @@ public void Install(DeviceData device, Stream apk, IProgress public void InstallWrite(DeviceData device, Stream apk, string apkName, string session, IProgress? progress = null) { - progress?.Report(0d); + progress?.Report(0); EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -823,7 +823,7 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s socket.Send(buffer, read); #endif totalBytesRead += read; - progress?.Report(totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + progress?.Report(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess); } read = socket.Read(buffer); @@ -838,7 +838,7 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s { throw new AdbException(value); } - progress?.Report(100d); + progress?.Report(100); } /// @@ -851,7 +851,7 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s /// An optional parameter which, when specified, returns progress notifications. The progress is reported as a value between 0 and 100, representing the percentage of the apk which has been transferred. protected virtual void InstallWrite(DeviceData device, Stream apk, string apkName, string session, Action? progress) { - progress?.Invoke(apkName, 0d); + progress?.Invoke(apkName, 0); EnsureDevice(device); ExceptionExtensions.ThrowIfNull(apk); @@ -892,9 +892,9 @@ protected virtual void InstallWrite(DeviceData device, Stream apk, string apkNam socket.Send(buffer, read); #endif totalBytesRead += read; - progress?.Invoke(apkName, totalBytesToProcess == 0L ? 0.0 : totalBytesRead * 100.0 / totalBytesToProcess); + progress?.Invoke(apkName, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess); } - progress?.Invoke(apkName, 100d); + progress?.Invoke(apkName, 100); read = socket.Read(buffer); string value = @@ -908,7 +908,7 @@ protected virtual void InstallWrite(DeviceData device, Stream apk, string apkNam { throw new AdbException(value); } - progress?.Invoke(null, 100d); + progress?.Invoke(null, 100); } /// diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 86c751aa..634a9bf1 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -450,7 +450,7 @@ protected void ValidateDevice() /// If fatal error occurred when pushing file. protected virtual string SyncPackageToDevice(string localFilePath, Action? progress) { - progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0L, 100L)); + progress?.Invoke(localFilePath, new SyncProgressChangedEventArgs(0, 100)); ValidateDevice(); @@ -486,7 +486,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action -namespace AdvancedSharpAdbClient.DeviceCommands.Models +namespace AdvancedSharpAdbClient.Models { /// /// Represents the state of the installation. diff --git a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs index fcfabdc7..b2022884 100644 --- a/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs +++ b/AdvancedSharpAdbClient/Models/Enums/UnixFileType.cs @@ -12,11 +12,6 @@ namespace AdvancedSharpAdbClient.Models [Flags] public enum UnixFileType { - /// - /// The mask that can be used to retrieve the file type from a . - /// - TypeMask = 0x8000, - /// /// The file is a Unix socket. /// @@ -30,7 +25,7 @@ public enum UnixFileType /// /// The file is a regular file. /// - Regular = TypeMask, + Regular = 0x8000, /// /// The file is a block device. @@ -50,6 +45,11 @@ public enum UnixFileType /// /// The file is a first-in first-out queue. /// - FIFO = 0x1000 + FIFO = 0x1000, + + /// + /// The mask that can be used to retrieve the file type from a . + /// + TypeMask = Regular } } diff --git a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs b/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs similarity index 66% rename from AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs rename to AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs index 105eedfb..00760cff 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/Models/InstallProgressEventArgs.cs +++ b/AdvancedSharpAdbClient/Models/InstallProgress.EventArgs.cs @@ -1,23 +1,24 @@ -// +// // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // using System; -namespace AdvancedSharpAdbClient.DeviceCommands.Models +namespace AdvancedSharpAdbClient.Models { /// - /// Represents the state of the installation for . + /// Represents the state of apk installation. /// + /// The state of the installation. public class InstallProgressEventArgs(PackageInstallProgressState state) : EventArgs { /// - /// State of the installation. + /// Get the state of the installation. /// public PackageInstallProgressState State { get; } = state; /// - /// Number of packages which is finished operation. + /// Get the number of packages which is finished operation. /// Used only in , /// and /// state. @@ -25,7 +26,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event public int PackageFinished { get; init; } /// - /// Number of packages required for this operation. + /// Get the number of packages required for this operation. /// Used only in , /// and /// state. @@ -33,7 +34,7 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event public int PackageRequired { get; init; } /// - /// Upload percentage (from to ) completed. + /// Get the upload percentage (from to ) completed. /// Used only in state. /// public double UploadProgress { get; init; } @@ -42,6 +43,9 @@ public class InstallProgressEventArgs(PackageInstallProgressState state) : Event /// Initializes a new instance of the class. /// Which is used for state. /// + /// The number of packages which is finished operation. + /// The number of packages required for this operation. + /// Get the upload percentage (from to ) completed. public InstallProgressEventArgs(int packageUploaded, int packageRequired, double uploadProgress) : this(PackageInstallProgressState.Uploading) { PackageFinished = packageUploaded; @@ -55,6 +59,9 @@ public InstallProgressEventArgs(int packageUploaded, int packageRequired, double /// and /// state. /// + /// The number of packages which is finished operation. + /// The number of packages required for this operation. + /// The state of the installation. public InstallProgressEventArgs(int packageFinished, int packageRequired, PackageInstallProgressState state) : this(state) { PackageFinished = packageFinished; diff --git a/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs b/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs index 8942242b..fe6df0b8 100644 --- a/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs +++ b/AdvancedSharpAdbClient/Models/SyncService.EventArgs.cs @@ -26,6 +26,6 @@ public class SyncProgressChangedEventArgs(long current, long total) : EventArgs /// /// Gets the number of progress percentage (from to ) for the sync operation. /// - public double ProgressPercentage => TotalBytesToReceive == 0L ? 0.0 : ReceivedBytesSize * 100.0 / TotalBytesToReceive; + public double ProgressPercentage => TotalBytesToReceive == 0 ? 0 : ReceivedBytesSize * 100d / TotalBytesToReceive; } } From b515c0d6449ca322338243a58f8bf92b65986c8a Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 18:07:08 +0800 Subject: [PATCH 10/12] Add InstallPackage in DeviceExtensions --- .../DeviceExtensionsTests.Async.cs | 12 ++-- .../DeviceCommands/DeviceExtensionsTests.cs | 12 ++-- AdvancedSharpAdbClient/AdbClient.Async.cs | 48 ++++++------- AdvancedSharpAdbClient/AdbClient.cs | 48 ++++++------- .../DeviceCommands/DeviceExtensions.Async.cs | 72 ++++++++++++++++++- .../DeviceCommands/DeviceExtensions.cs | 56 +++++++++++++-- .../DeviceCommands/PackageManager.Async.cs | 20 ++++-- .../DeviceCommands/PackageManager.cs | 18 +++-- .../Interfaces/IAdbClient.Async.cs | 8 +-- .../Interfaces/IAdbClient.cs | 8 +-- 10 files changed, 211 insertions(+), 91 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs index cb6496bf..b9a75121 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.Async.cs @@ -98,7 +98,6 @@ public async void UninstallPackageAsyncTests() { DummyAdbClient adbClient = new(); - adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm uninstall com.example"] = "Success"; DeviceData device = new() @@ -107,9 +106,8 @@ public async void UninstallPackageAsyncTests() }; await adbClient.UninstallPackageAsync(device, "com.example"); - Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[1]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[0]); } [Theory] @@ -343,7 +341,6 @@ public async void GetPackageVersionAsyncTest(string command, int versionCode, st { DummyAdbClient adbClient = new(); - adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands[$"shell:dumpsys package {packageName}"] = command; DeviceData device = new() @@ -355,9 +352,8 @@ public async void GetPackageVersionAsyncTest(string command, int versionCode, st Assert.Equal(versionCode, version.VersionCode); Assert.Equal(versionName, version.VersionName); - Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[1]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[0]); } [Fact] diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs index a417066f..e3e5f268 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceExtensionsTests.cs @@ -100,14 +100,12 @@ public void UninstallPackageTests() { DummyAdbClient adbClient = new(); - adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands["shell:pm uninstall com.example"] = "Success"; adbClient.UninstallPackage(Device, "com.example"); - Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[1]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal("shell:pm uninstall com.example", adbClient.ReceivedCommands[0]); } [Theory] @@ -341,7 +339,6 @@ public void GetPackageVersionTest(string command, int versionCode, string versio { DummyAdbClient adbClient = new(); - adbClient.Commands["shell:pm list packages -f"] = "package:/system/app/Gallery2/Gallery2.apk=com.android.gallery3d"; adbClient.Commands[$"shell:dumpsys package {packageName}"] = command; VersionInfo version = adbClient.GetPackageVersion(Device, packageName); @@ -349,9 +346,8 @@ public void GetPackageVersionTest(string command, int versionCode, string versio Assert.Equal(versionCode, version.VersionCode); Assert.Equal(versionName, version.VersionName); - Assert.Equal(2, adbClient.ReceivedCommands.Count); - Assert.Equal("shell:pm list packages -f", adbClient.ReceivedCommands[0]); - Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[1]); + Assert.Single(adbClient.ReceivedCommands); + Assert.Equal($"shell:dumpsys package {packageName}", adbClient.ReceivedCommands[0]); } [Fact] diff --git a/AdvancedSharpAdbClient/AdbClient.Async.cs b/AdvancedSharpAdbClient/AdbClient.Async.cs index 1fbafe94..b4ec0753 100644 --- a/AdvancedSharpAdbClient/AdbClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbClient.Async.cs @@ -491,13 +491,18 @@ public virtual async Task InstallAsync(DeviceData device, Stream apk, IProgress< } /// - public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) + public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); - ExceptionExtensions.ThrowIfNull(packageName); + + if (!baseAPK.CanRead || !baseAPK.CanSeek) + { + throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); + } if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) { @@ -505,12 +510,17 @@ public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable + progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); + + await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false); + + int progressCount = 1; Dictionary status = new(splitAPKsCount); - void OnSyncProgressChanged(string? sender, double args) + void OnSplitSyncProgressChanged(string? sender, double args) { lock (status) { @@ -522,12 +532,12 @@ void OnSyncProgressChanged(string? sender, double args) { status[path] = args; } - progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); } } int i = 0; - await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken))).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); @@ -535,18 +545,13 @@ void OnSyncProgressChanged(string? sender, double args) } /// - public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) + public virtual async Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) { progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); - - if (!baseAPK.CanRead || !baseAPK.CanSeek) - { - throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); - } + ExceptionExtensions.ThrowIfNull(packageName); if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) { @@ -554,17 +559,12 @@ public virtual async Task InstallMultipleAsync(DeviceData device, Stream baseAPK } progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); - string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false); + string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false); + int progressCount = 0; int splitAPKsCount = splitAPKs.Count(); - void OnMainSyncProgressChanged(string? sender, double args) => - progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); - - await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false); - - int progressCount = 1; Dictionary status = new(splitAPKsCount); - void OnSplitSyncProgressChanged(string? sender, double args) + void OnSyncProgressChanged(string? sender, double args) { lock (status) { @@ -576,12 +576,12 @@ void OnSplitSyncProgressChanged(string? sender, double args) { status[path] = args; } - progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); } } int i = 0; - await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged, cancellationToken))).ConfigureAwait(false); + await Extensions.WhenAll(splitAPKs.Select(splitAPK => InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged, cancellationToken))).ConfigureAwait(false); progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); await InstallCommitAsync(device, session, cancellationToken).ConfigureAwait(false); diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs index c95ef2c2..fb789e12 100644 --- a/AdvancedSharpAdbClient/AdbClient.cs +++ b/AdvancedSharpAdbClient/AdbClient.cs @@ -637,13 +637,18 @@ public void Install(DeviceData device, Stream apk, IProgress - public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, params string[] arguments) + public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, params string[] arguments) { progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); EnsureDevice(device); + ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); - ExceptionExtensions.ThrowIfNull(packageName); + + if (!baseAPK.CanRead || !baseAPK.CanSeek) + { + throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); + } if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) { @@ -651,12 +656,17 @@ public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, st } progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession)); - string session = InstallCreate(device, packageName, arguments); + string session = InstallCreate(device, null, arguments); - int progressCount = 0; int splitAPKsCount = splitAPKs.Count(); + void OnMainSyncProgressChanged(string? sender, double args) => + progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); + + InstallWrite(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged); + + int progressCount = 1; Dictionary status = new(splitAPKsCount); - void OnSyncProgressChanged(string? sender, double args) + void OnSplitSyncProgressChanged(string? sender, double args) { lock (status) { @@ -668,14 +678,14 @@ void OnSyncProgressChanged(string? sender, double args) { status[path] = args; } - progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); } } int i = 0; foreach (Stream splitAPK in splitAPKs) { - InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged); + InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged); } progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); @@ -684,18 +694,13 @@ void OnSyncProgressChanged(string? sender, double args) } /// - public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress = null, params string[] arguments) + public void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress = null, params string[] arguments) { progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Preparing)); EnsureDevice(device); - ExceptionExtensions.ThrowIfNull(baseAPK); ExceptionExtensions.ThrowIfNull(splitAPKs); - - if (!baseAPK.CanRead || !baseAPK.CanSeek) - { - throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream"); - } + ExceptionExtensions.ThrowIfNull(packageName); if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek)) { @@ -703,17 +708,12 @@ public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable - progress?.Report(new InstallProgressEventArgs(sender is null ? 1 : 0, splitAPKsCount + 1, args / 2)); - - InstallWrite(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged); - - int progressCount = 1; Dictionary status = new(splitAPKsCount); - void OnSplitSyncProgressChanged(string? sender, double args) + void OnSyncProgressChanged(string? sender, double args) { lock (status) { @@ -725,14 +725,14 @@ void OnSplitSyncProgressChanged(string? sender, double args) { status[path] = args; } - progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount + 1, (status.Values.Select(x => x / splitAPKsCount).Sum() + 100) / 2)); + progress?.Report(new InstallProgressEventArgs(progressCount, splitAPKsCount, status.Values.Select(x => x / splitAPKsCount).Sum())); } } int i = 0; foreach (Stream splitAPK in splitAPKs) { - InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSplitSyncProgressChanged); + InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, OnSyncProgressChanged); } progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing)); diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs index aa570fc8..3516016d 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs @@ -295,6 +295,59 @@ public static async Task> GetEnvironmentVariablesAsyn return receiver.EnvironmentVariables; } + /// + /// Asynchronously installs an Android application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute file system path to file on local host to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to adb install. + /// A which represents the asynchronous operation. + public static Task InstallPackageAsync(this IAdbClient client, DeviceData device, string packageFilePath, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + return manager.InstallPackageAsync(packageFilePath, progress, cancellationToken, arguments); + } + + /// + /// Asynchronously installs Android multiple application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute base app file system path to file on local host to install. + /// The absolute split app file system paths to file on local host to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public static Task InstallMultiplePackageAsync(this IAdbClient client, DeviceData device, string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + return manager.InstallMultiplePackageAsync(basePackageFilePath, splitPackageFilePaths, progress, cancellationToken, arguments); + } + + /// + /// Asynchronously installs Android multiple application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute split app file system paths to file on local host to install. + /// The absolute package name of the base app. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm install-create. + /// A which represents the asynchronous operation. + public static Task InstallMultiplePackageAsync(this IAdbClient client, DeviceData device, IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, CancellationToken cancellationToken = default, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + return manager.InstallMultiplePackageAsync(splitPackageFilePaths, packageName, progress, cancellationToken, arguments); + } + /// /// Asynchronously uninstalls a package from the device. /// @@ -305,10 +358,25 @@ public static async Task> GetEnvironmentVariablesAsyn /// A which represents the asynchronous operation. public static Task UninstallPackageAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken = default) { - PackageManager manager = new(client, device); + PackageManager manager = new(client, device, skipInit: true); return manager.UninstallPackageAsync(packageName, cancellationToken); } + /// + /// Asynchronously uninstalls a package from the device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The name of the package to uninstall. + /// A which can be used to cancel the asynchronous operation. + /// The arguments to pass to pm uninstall. + /// A which represents the asynchronous operation. + public static Task UninstallPackageAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + return manager.UninstallPackageAsync(packageName, cancellationToken, arguments); + } + /// /// Asynchronously requests the version information from the device. /// @@ -319,7 +387,7 @@ public static Task UninstallPackageAsync(this IAdbClient client, DeviceData devi /// A which return the of target application. public static Task GetPackageVersionAsync(this IAdbClient client, DeviceData device, string packageName, CancellationToken cancellationToken = default) { - PackageManager manager = new(client, device); + PackageManager manager = new(client, device, skipInit: true); return manager.GetVersionInfoAsync(packageName, cancellationToken); } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs index 986f7938..a0dac92a 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs @@ -271,16 +271,64 @@ public static Dictionary GetEnvironmentVariables(this IAdbClient return receiver.EnvironmentVariables; } + /// + /// Installs an Android application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute file system path to file on local host to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// The arguments to pass to adb install. + public static void InstallPackage(this IAdbClient client, DeviceData device, string packageFilePath, IProgress? progress = null, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + manager.InstallPackage(packageFilePath, progress, arguments); + } + + /// + /// Installs Android multiple application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute base app file system path to file on local host to install. + /// The absolute split app file system paths to file on local host to install. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// The arguments to pass to pm install-create. + public static void InstallMultiplePackage(this IAdbClient client, DeviceData device, string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + manager.InstallMultiplePackage(basePackageFilePath, splitPackageFilePaths, progress, arguments); + } + + /// + /// Installs Android multiple application on device. + /// + /// The connection to the adb server. + /// The device on which to uninstall the package. + /// The absolute split app file system paths to file on local host to install. + /// The absolute package name of the base app. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. + /// The arguments to pass to pm install-create. + public static void InstallMultiplePackage(this IAdbClient client, DeviceData device, IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, params string[] arguments) + { + PackageManager manager = new(client, device, skipInit: true); + manager.InstallMultiplePackage(splitPackageFilePaths, packageName, progress, arguments); + } + /// /// Uninstalls a package from the device. /// /// The connection to the adb server. /// The device on which to uninstall the package. /// The name of the package to uninstall. - public static void UninstallPackage(this IAdbClient client, DeviceData device, string packageName) + /// The arguments to pass to pm uninstall. + public static void UninstallPackage(this IAdbClient client, DeviceData device, string packageName, params string[] arguments) { - PackageManager manager = new(client, device); - manager.UninstallPackage(packageName); + PackageManager manager = new(client, device, skipInit: true); + manager.UninstallPackage(packageName, arguments); } /// @@ -291,7 +339,7 @@ public static void UninstallPackage(this IAdbClient client, DeviceData device, s /// The name of the package from which to get the application version. public static VersionInfo GetPackageVersion(this IAdbClient client, DeviceData device, string packageName) { - PackageManager manager = new(client, device); + PackageManager manager = new(client, device, skipInit: true); return manager.GetVersionInfo(packageName); } diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 9bb1ded7..9de7240b 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -42,7 +42,8 @@ public virtual Task RefreshPackagesAsync(CancellationToken cancellationToken = d /// Asynchronously installs an Android application on device. /// /// The absolute file system path to file on local host to install. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install. /// A which represents the asynchronous operation. @@ -70,7 +71,8 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => /// Asynchronously installs the application package that was pushed to a temporary location on the device. /// /// absolute file path to package file on device. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install. /// A which represents the asynchronous operation. @@ -107,7 +109,8 @@ public virtual async Task InstallRemotePackageAsync(string remoteFilePath, IProg /// /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. @@ -182,7 +185,8 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. @@ -249,7 +253,8 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async x => /// /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. @@ -297,7 +302,8 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) /// /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to pm install-create. /// A which represents the asynchronous operation. @@ -337,7 +343,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath) } /// - /// Asynchronously ininstalls a package from the device. + /// Asynchronously uninstalls a package from the device. /// /// The name of the package to uninstall. /// The arguments to pass to pm uninstall. diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 634a9bf1..56d70260 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -138,7 +138,8 @@ public virtual void RefreshPackages() /// Installs an Android application on device. /// /// The absolute file system path to file on local host to install. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install. public virtual void InstallPackage(string packageFilePath, IProgress? progress = null, params string[] arguments) { @@ -164,7 +165,8 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) => /// Installs the application package that was pushed to a temporary location on the device. /// /// absolute file path to package file on device. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install. public virtual void InstallRemotePackage(string remoteFilePath, IProgress? progress = null, params string[] arguments) { @@ -199,7 +201,8 @@ public virtual void InstallRemotePackage(string remoteFilePath, IProgress /// The absolute base app file system path to file on local host to install. /// The absolute split app file system paths to file on local host to install. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to pm install-create. public virtual void InstallMultiplePackage(string basePackageFilePath, IEnumerable splitPackageFilePaths, IProgress? progress = null, params string[] arguments) { @@ -260,7 +263,8 @@ void OnSplitSyncProgressChanged(string? sender, SyncProgressChangedEventArgs arg /// /// The absolute split app file system paths to file on local host to install. /// The absolute package name of the base app. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to pm install-create. public virtual void InstallMultiplePackage(IEnumerable splitPackageFilePaths, string packageName, IProgress? progress = null, params string[] arguments) { @@ -313,7 +317,8 @@ void OnSyncProgressChanged(string? sender, SyncProgressChangedEventArgs args) /// /// The absolute base app file path to package file on device. /// The absolute split app file paths to package file on device. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to pm install-create. public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnumerable splitRemoteFilePaths, IProgress? progress = null, params string[] arguments) { @@ -354,7 +359,8 @@ public virtual void InstallMultipleRemotePackage(string baseRemoteFilePath, IEnu /// /// The absolute split app file paths to package file on device. /// The absolute package name of the base app. - /// An optional parameter which, when specified, returns progress notifications. The progress is reported as , representing the state of installation. + /// An optional parameter which, when specified, returns progress notifications. + /// The progress is reported as , representing the state of installation. /// The arguments to pass to pm install-create. public virtual void InstallMultipleRemotePackage(IEnumerable splitRemoteFilePaths, string packageName, IProgress? progress = null, params string[] arguments) { diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs index ca3b4f33..e8016534 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.Async.cs @@ -317,27 +317,27 @@ public partial interface IAdbClient /// Asynchronously push multiple APKs to the device and install them. /// /// The device on which to install the application. + /// A which represents the base APK to install. /// s which represents the split APKs to install. - /// The package name of the base APK to install. /// An optional parameter which, when specified, returns progress notifications. /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install-create. /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); + Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); /// /// Asynchronously push multiple APKs to the device and install them. /// /// The device on which to install the application. - /// A which represents the base APK to install. /// s which represents the split APKs to install. + /// The package name of the base APK to install. /// An optional parameter which, when specified, returns progress notifications. /// The progress is reported as , representing the state of installation. /// A which can be used to cancel the asynchronous operation. /// The arguments to pass to adb install-create. /// A which represents the asynchronous operation. - Task InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); + Task InstallMultipleAsync(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, CancellationToken cancellationToken, params string[] arguments); /// /// Like "install", but starts an install session asynchronously. diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs index 227f5a65..1681474c 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbClient.cs @@ -322,23 +322,23 @@ public partial interface IAdbClient /// Push multiple APKs to the device and install them. /// /// The device on which to install the application. + /// A which represents the base APK to install. /// s which represents the split APKs to install. - /// The package name of the base APK to install. /// An optional parameter which, when specified, returns progress notifications. /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install-create. - void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, params string[] arguments); + void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, params string[] arguments); /// /// Push multiple APKs to the device and install them. /// /// The device on which to install the application. - /// A which represents the base APK to install. /// s which represents the split APKs to install. + /// The package name of the base APK to install. /// An optional parameter which, when specified, returns progress notifications. /// The progress is reported as , representing the state of installation. /// The arguments to pass to adb install-create. - void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, IProgress? progress, params string[] arguments); + void InstallMultiple(DeviceData device, IEnumerable splitAPKs, string packageName, IProgress? progress, params string[] arguments); /// /// Like "install", but starts an install session. From c24c003c445ce92d19461ec6f97692172bc040c8 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 20:17:01 +0800 Subject: [PATCH 11/12] Create special file methods for UWP --- .../Dummys/DummyAdbCommandLineClient.cs | 2 ++ .../AdbCommandLineClient.Async.cs | 23 ++++++------ .../AdbCommandLineClient.cs | 17 ++++----- AdvancedSharpAdbClient/AdbServer.Async.cs | 11 ++---- AdvancedSharpAdbClient/AdbServer.cs | 14 ++------ .../DeviceCommands/DeviceClient.Async.cs | 36 +++++++++---------- .../DeviceCommands/DeviceClient.cs | 36 +++++++++---------- .../DeviceCommands/PackageManager.Async.cs | 15 +++++++- .../DeviceCommands/PackageManager.cs | 16 ++++++++- .../Extensions/Extensions.cs | 16 +++++++++ .../Extensions/Factories.cs | 7 ---- .../Interfaces/IAdbCommandLineClient.Async.cs | 8 +++++ .../Interfaces/IAdbCommandLineClient.cs | 1 + .../Polyfills/DnsEndPoint.cs | 2 ++ .../Extensions/DateTimeExtensions.cs | 2 ++ .../Polyfills/Extensions/EnumExtensions.cs | 2 ++ .../Extensions/EnumerableExtensions.cs | 2 ++ .../Extensions/ExceptionExtensions.cs | 8 ++++- .../Polyfills/Extensions/SocketExtensions.cs | 2 ++ .../Polyfills/Extensions/StreamExtensions.cs | 2 ++ .../Extensions/StringBuilderExtensions.cs | 2 ++ .../Polyfills/Extensions/StringExtensions.cs | 2 ++ AdvancedSharpAdbClient/Polyfills/HashCode.cs | 1 + .../Polyfills/Interfaces/IProgress.cs | 3 ++ .../Interfaces/IReadOnlyCollection.cs | 2 ++ .../Polyfills/Interfaces/IReadOnlyList.cs | 3 ++ AdvancedSharpAdbClient/Polyfills/Point.cs | 2 ++ AdvancedSharpAdbClient/Polyfills/Rectangle.cs | 2 ++ .../Properties/GlobalUsings.cs | 1 + 29 files changed, 155 insertions(+), 85 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs index 7987d578..40ec2451 100644 --- a/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient.Tests/Dummys/DummyAdbCommandLineClient.cs @@ -22,6 +22,8 @@ public DummyAdbCommandLineClient() : base(ServerName) // No validation done in the dummy adb client. public override bool CheckFileExists(string adbPath) => true; + public override Task CheckFileExistsAsync(string adbPath, CancellationToken cancellationToken = default) => Task.FromResult(true); + protected override int RunProcess(string filename, string command, ICollection errorOutput, ICollection standardOutput) { if (filename == AdbPath) diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs index f3c1dd28..11edbf1d 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs @@ -8,17 +8,14 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Threading; namespace AdvancedSharpAdbClient { public partial class AdbCommandLineClient { - /// - /// Asynchronously queries adb for its version number and checks it against . - /// - /// A which can be used to cancel the asynchronous operation. - /// A which return a object that contains the version number of the Android Command Line client. + /// public virtual async Task GetVersionAsync(CancellationToken cancellationToken = default) { // Run the adb.exe version command and capture the output. @@ -36,12 +33,8 @@ public virtual async Task GetVersionAsync(CancellationToken cancellatio return version; } - - /// - /// Asynchronously starts the adb server by running the adb start-server command. - /// - /// A which can be used to cancel the asynchronous operation. - /// A which represents the asynchronous operation. + + /// public virtual async Task StartServerAsync(CancellationToken cancellationToken = default) { int status = await RunAdbProcessInnerAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); @@ -81,6 +74,14 @@ public virtual async Task StartServerAsync(CancellationToken cancellationToken = await RunAdbProcessAsync("start-server", null, null, cancellationToken).ConfigureAwait(false); } + /// + public virtual Task CheckFileExistsAsync(string adbPath, CancellationToken cancellationToken = default) => +#if WINDOWS_UWP + StorageFile.GetFileFromPathAsync(adbPath).AsTask(cancellationToken).ContinueWith(x => x.Result != null && x.Result.IsOfType(StorageItemTypes.File)); +#else + Extensions.FromResult(File.Exists(adbPath)); +#endif + /// /// Asynchronously runs the adb.exe process, invoking a specific , /// and reads the standard output and standard error output. diff --git a/AdvancedSharpAdbClient/AdbCommandLineClient.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.cs index 9c9c5b66..293d4012 100644 --- a/AdvancedSharpAdbClient/AdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/AdbCommandLineClient.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.RegularExpressions; +using System.Threading; namespace AdvancedSharpAdbClient { @@ -61,10 +62,7 @@ public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger public string AdbPath { get; protected set; } - /// - /// Queries adb for its version number and checks it against . - /// - /// A object that contains the version number of the Android Command Line client. + /// public virtual Version GetVersion() { // Run the adb.exe version command and capture the output. @@ -83,9 +81,7 @@ public virtual Version GetVersion() return version; } - /// - /// Starts the adb server by running the adb start-server command. - /// + /// public virtual void StartServer() { int status = RunAdbProcessInner("start-server", null, null); @@ -126,7 +122,12 @@ public virtual void StartServer() } /// - public virtual bool CheckFileExists(string adbPath) => Factories.CheckFileExists(adbPath); + public virtual bool CheckFileExists(string adbPath) => +#if WINDOWS_UWP + StorageFile.GetFileFromPathAsync(adbPath).GetResults() is StorageFile file && file.IsOfType(StorageItemTypes.File); +#else + File.Exists(adbPath); +#endif /// /// Throws an error if the path does not point to a valid instance of adb.exe. diff --git a/AdvancedSharpAdbClient/AdbServer.Async.cs b/AdvancedSharpAdbClient/AdbServer.Async.cs index bfbf35bd..0ca5e40d 100644 --- a/AdvancedSharpAdbClient/AdbServer.Async.cs +++ b/AdvancedSharpAdbClient/AdbServer.Async.cs @@ -22,9 +22,8 @@ public virtual async Task StartServerAsync(string adbPath, bo Version? commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - CheckFileExists = commandLineClient.CheckFileExists; - if (commandLineClient.CheckFileExists(adbPath)) + if (await commandLineClient.CheckFileExistsAsync(adbPath, cancellationToken).ConfigureAwait(false)) { CachedAdbPath = adbPath; commandLineVersion = await commandLineClient.GetVersionAsync(cancellationToken).ConfigureAwait(false); @@ -36,8 +35,8 @@ public virtual async Task StartServerAsync(string adbPath, bo return !serverStatus.IsRunning ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") : serverStatus.Version >= RequiredAdbVersion - ? StartServerResult.AlreadyRunning - : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); + ? StartServerResult.AlreadyRunning + : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); } if (serverStatus.IsRunning) @@ -45,8 +44,6 @@ public virtual async Task StartServerAsync(string adbPath, bo if (serverStatus.Version < RequiredAdbVersion || (restartServerIfNewer && serverStatus.Version < commandLineVersion)) { - ExceptionExtensions.ThrowIfNull(adbPath); - await StopServerAsync(cancellationToken); await commandLineClient.StartServerAsync(cancellationToken); return StartServerResult.RestartedOutdatedDaemon; @@ -58,8 +55,6 @@ public virtual async Task StartServerAsync(string adbPath, bo } else { - ExceptionExtensions.ThrowIfNull(adbPath); - await commandLineClient.StartServerAsync(cancellationToken); return StartServerResult.Started; } diff --git a/AdvancedSharpAdbClient/AdbServer.cs b/AdvancedSharpAdbClient/AdbServer.cs index 1ac66757..714d7120 100644 --- a/AdvancedSharpAdbClient/AdbServer.cs +++ b/AdvancedSharpAdbClient/AdbServer.cs @@ -154,11 +154,6 @@ public AdbServer(Func adbSocketFactory, Func public static IAdbServer Instance { get; set; } = new AdbServer(); - /// - /// Throws an error if the path does not point to a valid instance of adb.exe. - /// - protected static Func CheckFileExists { get; set; } = Factories.CheckFileExists; - /// /// if is starting adb server; otherwise, . /// @@ -179,7 +174,6 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI Version? commandLineVersion = null; IAdbCommandLineClient commandLineClient = adbCommandLineClientFactory(adbPath); - CheckFileExists = commandLineClient.CheckFileExists; if (commandLineClient.CheckFileExists(adbPath)) { @@ -193,8 +187,8 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI return !serverStatus.IsRunning ? throw new AdbException("The adb server is not running, but no valid path to the adb.exe executable was provided. The adb server cannot be started.") : serverStatus.Version >= RequiredAdbVersion - ? StartServerResult.AlreadyRunning - : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); + ? StartServerResult.AlreadyRunning + : throw new AdbException($"The adb daemon is running an outdated version ${commandLineVersion}, but not valid path to the adb.exe executable was provided. A more recent version of the adb server cannot be started."); } if (serverStatus.IsRunning) @@ -202,8 +196,6 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI if (serverStatus.Version < RequiredAdbVersion || (restartServerIfNewer && serverStatus.Version < commandLineVersion)) { - ExceptionExtensions.ThrowIfNull(adbPath); - StopServer(); commandLineClient.StartServer(); return StartServerResult.RestartedOutdatedDaemon; @@ -215,8 +207,6 @@ public virtual StartServerResult StartServer(string adbPath, bool restartServerI } else { - ExceptionExtensions.ThrowIfNull(adbPath); - commandLineClient.StartServer(); return StartServerResult.Started; } diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs index c5f4ae54..282632c4 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs @@ -21,7 +21,7 @@ public partial class DeviceClient /// A which can be used to cancel the asynchronous operation. /// A which return a containing current hierarchy. /// Failed if start with ERROR or java.lang.Exception. - public async Task DumpScreenStringAsync(CancellationToken cancellationToken = default) + public virtual async Task DumpScreenStringAsync(CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, "uiautomator dump /dev/tty", receiver, cancellationToken).ConfigureAwait(false); @@ -46,7 +46,7 @@ public async Task DumpScreenStringAsync(CancellationToken cancellationTo /// /// A which can be used to cancel the asynchronous operation. /// A which return a containing current hierarchy. - public async Task DumpScreenAsync(CancellationToken cancellationToken = default) + public virtual async Task DumpScreenAsync(CancellationToken cancellationToken = default) { string xmlString = await DumpScreenStringAsync(cancellationToken).ConfigureAwait(false); XmlDocument doc = new(); @@ -64,7 +64,7 @@ public async Task DumpScreenStringAsync(CancellationToken cancellationTo /// /// A which can be used to cancel the asynchronous operation. /// A which return a containing current hierarchy. - public async Task DumpScreenWinRTAsync(CancellationToken cancellationToken = default) + public virtual async Task DumpScreenWinRTAsync(CancellationToken cancellationToken = default) { string xmlString = await DumpScreenStringAsync(cancellationToken).ConfigureAwait(false); Windows.Data.Xml.Dom.XmlDocument doc = new(); @@ -83,7 +83,7 @@ public async Task DumpScreenStringAsync(CancellationToken cancellationTo /// The to click. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task ClickAsync(Point cords, CancellationToken cancellationToken = default) + public virtual async Task ClickAsync(Point cords, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input tap {cords.X} {cords.Y}", receiver, cancellationToken).ConfigureAwait(false); @@ -107,7 +107,7 @@ public async Task ClickAsync(Point cords, CancellationToken cancellationToken = /// The Y co-ordinate to click. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task ClickAsync(int x, int y, CancellationToken cancellationToken = default) + public virtual async Task ClickAsync(int x, int y, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input tap {x} {y}", receiver, cancellationToken).ConfigureAwait(false); @@ -132,7 +132,7 @@ public async Task ClickAsync(int x, int y, CancellationToken cancellationToken = /// The time spent in swiping. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task SwipeAsync(Element first, Element second, long speed, CancellationToken cancellationToken = default) + public virtual async Task SwipeAsync(Element first, Element second, long speed, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", receiver, cancellationToken).ConfigureAwait(false); @@ -157,7 +157,7 @@ public async Task SwipeAsync(Element first, Element second, long speed, Cancella /// The time spent in swiping. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task SwipeAsync(Point first, Point second, long speed, CancellationToken cancellationToken = default) + public virtual async Task SwipeAsync(Point first, Point second, long speed, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {first.X} {first.Y} {second.X} {second.Y} {speed}", receiver, cancellationToken).ConfigureAwait(false); @@ -184,7 +184,7 @@ public async Task SwipeAsync(Point first, Point second, long speed, Cancellation /// The time spent in swiping. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task SwipeAsync(int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken = default) + public virtual async Task SwipeAsync(int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input swipe {x1} {y1} {x2} {y2} {speed}", receiver, cancellationToken).ConfigureAwait(false); @@ -207,7 +207,7 @@ public async Task SwipeAsync(int x1, int y1, int x2, int y2, long speed, Cancell /// The package name of the app to check. /// A which can be used to cancel the asynchronous operation. /// A which return the result. if the app is running in foreground; otherwise, . - public async Task IsAppRunningAsync(string packageName, CancellationToken cancellationToken = default) + public virtual async Task IsAppRunningAsync(string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"pidof {packageName}", receiver, cancellationToken).ConfigureAwait(false); @@ -223,7 +223,7 @@ public async Task IsAppRunningAsync(string packageName, CancellationToken /// The package name of the app to check. /// A which can be used to cancel the asynchronous operation. /// A which return the result. if the app is running in background; otherwise, . - public async Task IsAppInForegroundAsync(string packageName, CancellationToken cancellationToken = default) + public virtual async Task IsAppInForegroundAsync(string packageName, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"dumpsys activity activities | grep mResumedActivity", receiver, cancellationToken).ConfigureAwait(false); @@ -238,7 +238,7 @@ public async Task IsAppInForegroundAsync(string packageName, CancellationT /// The package name of the app to check. /// A which can be used to cancel the asynchronous operation. /// A which return the of the app. Foreground, stopped or running in background. - public async Task GetAppStatusAsync(string packageName, CancellationToken cancellationToken = default) + public virtual async Task GetAppStatusAsync(string packageName, CancellationToken cancellationToken = default) { // Check if the app is in foreground bool currentApp = await IsAppInForegroundAsync(packageName, cancellationToken).ConfigureAwait(false); @@ -259,7 +259,7 @@ public async Task GetAppStatusAsync(string packageName, CancellationT /// A which can be used to cancel the asynchronous operation. /// Only check once if . Or it will continue check until is . /// A which return the of . - public async Task FindElementAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + public virtual async Task FindElementAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) { try { @@ -308,7 +308,7 @@ public async Task GetAppStatusAsync(string packageName, CancellationT /// A which can be used to cancel the asynchronous operation. /// Only check once if . Or it will continue check until is . /// A which return the of has got. - public async Task> FindElementsAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) + public virtual async Task> FindElementsAsync(string xpath = "hierarchy/node", CancellationToken cancellationToken = default) { try { @@ -365,7 +365,7 @@ static IEnumerable FindElements(IAdbClient client, DeviceData device, X /// A which can be used to cancel the asynchronous operation. /// Only check once if . Or it will continue check until is . /// A which return the of has got. - public async IAsyncEnumerable FindAsyncElements(string xpath = "hierarchy/node", [EnumeratorCancellation] CancellationToken cancellationToken = default) + public virtual async IAsyncEnumerable FindAsyncElements(string xpath = "hierarchy/node", [EnumeratorCancellation] CancellationToken cancellationToken = default) { while (!cancellationToken.IsCancellationRequested) { @@ -418,7 +418,7 @@ public async IAsyncEnumerable FindAsyncElements(string xpath = "hierarc /// The key event to send. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task SendKeyEventAsync(string key, CancellationToken cancellationToken = default) + public virtual async Task SendKeyEventAsync(string key, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input keyevent {key}", receiver, cancellationToken).ConfigureAwait(false); @@ -441,7 +441,7 @@ public async Task SendKeyEventAsync(string key, CancellationToken cancellationTo /// The text to send. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public async Task SendTextAsync(string text, CancellationToken cancellationToken = default) + public virtual async Task SendTextAsync(string text, CancellationToken cancellationToken = default) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; await AdbClient.ExecuteShellCommandAsync(Device, $"input text {text}", receiver, cancellationToken).ConfigureAwait(false); @@ -464,7 +464,7 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken /// The package name of the application to start. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public Task StartAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"monkey -p {packageName} 1", cancellationToken); + public virtual Task StartAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"monkey -p {packageName} 1", cancellationToken); /// /// Stop an Android application on device. @@ -472,7 +472,7 @@ public async Task SendTextAsync(string text, CancellationToken cancellationToken /// The package name of the application to stop. /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. - public Task StopAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"am force-stop {packageName}", cancellationToken); + public virtual Task StopAppAsync(string packageName, CancellationToken cancellationToken = default) => AdbClient.ExecuteShellCommandAsync(Device, $"am force-stop {packageName}", cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs index 0c5185ee..d5963db5 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs @@ -49,7 +49,7 @@ public DeviceClient(IAdbClient client, DeviceData device) /// /// A containing current hierarchy. /// Failed if start with ERROR or java.lang.Exception. - public string DumpScreenString() + public virtual string DumpScreenString() { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, "uiautomator dump /dev/tty", receiver); @@ -73,7 +73,7 @@ public string DumpScreenString() /// Gets the current device screen snapshot. /// /// A containing current hierarchy. - public XmlDocument? DumpScreen() + public virtual XmlDocument? DumpScreen() { XmlDocument doc = new(); string xmlString = DumpScreenString(); @@ -90,7 +90,7 @@ public string DumpScreenString() /// Gets the current device screen snapshot. /// /// A containing current hierarchy. - public Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT() + public virtual Windows.Data.Xml.Dom.XmlDocument? DumpScreenWinRT() { Windows.Data.Xml.Dom.XmlDocument doc = new(); string xmlString = DumpScreenString(); @@ -107,7 +107,7 @@ public string DumpScreenString() /// Clicks on the specified coordinates. /// /// The to click. - public void Click(Point cords) + public virtual void Click(Point cords) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input tap {cords.X} {cords.Y}", receiver); @@ -129,7 +129,7 @@ public void Click(Point cords) /// /// The X co-ordinate to click. /// The Y co-ordinate to click. - public void Click(int x, int y) + public virtual void Click(int x, int y) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input tap {x} {y}", receiver); @@ -152,7 +152,7 @@ public void Click(int x, int y) /// The start element. /// The end element. /// The time spent in swiping. - public void Swipe(Element first, Element second, long speed) + public virtual void Swipe(Element first, Element second, long speed) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input swipe {first.Center.X} {first.Center.Y} {second.Center.X} {second.Center.Y} {speed}", receiver); @@ -175,7 +175,7 @@ public void Swipe(Element first, Element second, long speed) /// The start . /// The end . /// The time spent in swiping. - public void Swipe(Point first, Point second, long speed) + public virtual void Swipe(Point first, Point second, long speed) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input swipe {first.X} {first.Y} {second.X} {second.Y} {speed}", receiver); @@ -200,7 +200,7 @@ public void Swipe(Point first, Point second, long speed) /// The end X co-ordinate. /// The end Y co-ordinate. /// The time spent in swiping. - public void Swipe(int x1, int y1, int x2, int y2, long speed) + public virtual void Swipe(int x1, int y1, int x2, int y2, long speed) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input swipe {x1} {y1} {x2} {y2} {speed}", receiver); @@ -222,7 +222,7 @@ public void Swipe(int x1, int y1, int x2, int y2, long speed) /// /// The package name of the app to check. /// if the app is running in foreground; otherwise, . - public bool IsAppRunning(string packageName) + public virtual bool IsAppRunning(string packageName) { ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"pidof {packageName}", receiver); @@ -237,7 +237,7 @@ public bool IsAppRunning(string packageName) /// /// The package name of the app to check. /// if the app is running in background; otherwise, . - public bool IsAppInForeground(string packageName) + public virtual bool IsAppInForeground(string packageName) { ConsoleOutputReceiver receiver = new() { TrimLines = true, ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"dumpsys activity activities | grep mResumedActivity", receiver); @@ -251,7 +251,7 @@ public bool IsAppInForeground(string packageName) /// /// The package name of the app to check. /// The of the app. Foreground, stopped or running in background. - public AppStatus GetAppStatus(string packageName) + public virtual AppStatus GetAppStatus(string packageName) { // Check if the app is in foreground bool currentApp = IsAppInForeground(packageName); @@ -272,7 +272,7 @@ public AppStatus GetAppStatus(string packageName) /// The timeout for waiting the element. /// Only check once if or . /// The of . - public Element? FindElement(string xpath = "hierarchy/node", TimeSpan timeout = default) + public virtual Element? FindElement(string xpath = "hierarchy/node", TimeSpan timeout = default) { Stopwatch stopwatch = new(); stopwatch.Start(); @@ -311,7 +311,7 @@ public AppStatus GetAppStatus(string packageName) /// The timeout for waiting the elements. /// Only check once if or . /// The of has got. - public IEnumerable FindElements(string xpath = "hierarchy/node", TimeSpan timeout = default) + public virtual IEnumerable FindElements(string xpath = "hierarchy/node", TimeSpan timeout = default) { Stopwatch stopwatch = new(); stopwatch.Start(); @@ -354,7 +354,7 @@ public IEnumerable FindElements(string xpath = "hierarchy/node", TimeSp /// Send key event to specific. You can see key events here https://developer.android.com/reference/android/view/KeyEvent. /// /// The key event to send. - public void SendKeyEvent(string key) + public virtual void SendKeyEvent(string key) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input keyevent {key}", receiver); @@ -375,7 +375,7 @@ public void SendKeyEvent(string key) /// Send text to device. Doesn't support Russian. /// /// The text to send. - public void SendText(string text) + public virtual void SendText(string text) { ConsoleOutputReceiver receiver = new() { ParsesErrors = false }; AdbClient.ExecuteShellCommand(Device, $"input text {text}", receiver); @@ -396,20 +396,20 @@ public void SendText(string text) /// Start an Android application on device. /// /// The package name of the application to start. - public void StartApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"monkey -p {packageName} 1"); + public virtual void StartApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"monkey -p {packageName} 1"); /// /// Stop an Android application on device. /// /// The package name of the application to stop. - public void StopApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"am force-stop {packageName}"); + public virtual void StopApp(string packageName) => AdbClient.ExecuteShellCommand(Device, $"am force-stop {packageName}"); /// /// Deconstruct the class. /// /// The to use to communicate with the Android Debug Bridge. /// The device on which to process command. - public void Deconstruct(out IAdbClient client, out DeviceData device) + public virtual void Deconstruct(out IAdbClient client, out DeviceData device) { client = AdbClient; device = Device; diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs index 9de7240b..3a9abb57 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs @@ -398,6 +398,19 @@ public virtual async Task GetVersionInfoAsync(string packageName, C return receiver.VersionInfo; } + /// + /// Asynchronously opens an existing file for reading. + /// + /// The file to be opened for reading. + /// A which can be used to cancel the asynchronous operation. + /// A read-only on the specified path. + protected virtual Task GetFileStreamAsync(string path, CancellationToken cancellationToken = default) => +#if WINDOWS_UWP + StorageFile.GetFileFromPathAsync(path).AsTask(cancellationToken).ContinueWith(x => x.Result.OpenReadAsync().AsTask(cancellationToken)).Unwrap().ContinueWith(x => x.Result.AsStream()); +#else + Extensions.FromResult(File.OpenRead(path)); +#endif + /// /// Asynchronously pushes a file to device /// @@ -428,7 +441,7 @@ protected virtual async Task SyncPackageToDeviceAsync(string localFilePa #if NETCOREAPP3_0_OR_GREATER await #endif - using FileStream stream = File.OpenRead(localFilePath); + using Stream stream = await GetFileStreamAsync(localFilePath, cancellationToken).ConfigureAwait(false); logger.LogDebug("Uploading file onto device '{0}'", Device.Serial); diff --git a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs index 56d70260..70db1c20 100644 --- a/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs +++ b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Text; @@ -447,6 +448,18 @@ protected void ValidateDevice() } } + /// + /// Opens an existing file for reading. + /// + /// The file to be opened for reading. + /// A read-only on the specified path. + protected virtual Stream GetFileStream(string path) => +#if WINDOWS_UWP + StorageFile.GetFileFromPathAsync(path).GetResults().OpenReadAsync().GetResults().AsStream(); +#else + File.OpenRead(path); +#endif + /// /// Pushes a file to device /// @@ -473,7 +486,7 @@ protected virtual string SyncPackageToDevice(string localFilePath, Action /// The absolute path to file on local host. /// An optional parameter which, when specified, returns progress notifications. + [EditorBrowsable(EditorBrowsableState.Never)] protected readonly struct SyncProgress(string localFilePath, Action progress) : IProgress { /// diff --git a/AdvancedSharpAdbClient/Extensions/Extensions.cs b/AdvancedSharpAdbClient/Extensions/Extensions.cs index 487f5ac0..d6dd2604 100644 --- a/AdvancedSharpAdbClient/Extensions/Extensions.cs +++ b/AdvancedSharpAdbClient/Extensions/Extensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Net; using System.Net.Sockets; @@ -20,6 +21,7 @@ namespace AdvancedSharpAdbClient /// /// Extension methods for the namespace. /// + [EditorBrowsable(EditorBrowsableState.Never)] internal static class Extensions { public static char[] NewLineSeparator { get; } = ['\r', '\n']; @@ -139,6 +141,20 @@ public static YieldAwaitable Yield() => #endif .Yield(); + /// + /// Creates a System.Threading.Tasks.Task`1 that's completed successfully with the specified result. + /// + /// The type of the result returned by the task. + /// The result to store into the completed task. + /// The successfully completed task. + public static Task FromResult(TResult result) => +#if NETFRAMEWORK && !NET45_OR_GREATER + TaskEx +#else + Task +#endif + .FromResult(result); + #if !NET7_0_OR_GREATER /// /// Reads a line of characters asynchronously and returns the data as a string. diff --git a/AdvancedSharpAdbClient/Extensions/Factories.cs b/AdvancedSharpAdbClient/Extensions/Factories.cs index 538bea74..e1f38a59 100644 --- a/AdvancedSharpAdbClient/Extensions/Factories.cs +++ b/AdvancedSharpAdbClient/Extensions/Factories.cs @@ -16,11 +16,6 @@ public static class Factories { static Factories() => Reset(); - /// - /// Determines whether the specified file exists. - /// - public static Func CheckFileExists { get; set; } - /// /// Gets or sets a delegate which creates a new instance of the class. /// @@ -51,14 +46,12 @@ public static class Factories /// Resets all factories to their default values. /// [MemberNotNull( - nameof(CheckFileExists), nameof(AdbSocketFactory), nameof(AdbClientFactory), nameof(AdbCommandLineClientFactory), nameof(SyncServiceFactory))] public static void Reset() { - CheckFileExists = File.Exists; AdbSocketFactory = endPoint => new AdbSocket(endPoint); AdbClientFactory = endPoint => new AdbClient(endPoint, AdbSocketFactory); AdbCommandLineClientFactory = path => new AdbCommandLineClient(path); diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs index bb9e5577..c6dd2b88 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.Async.cs @@ -23,6 +23,14 @@ public partial interface IAdbCommandLineClient /// A which can be used to cancel the asynchronous operation. /// A which represents the asynchronous operation. Task StartServerAsync(CancellationToken cancellationToken); + + /// + /// Determines whether the adb.exe file exists. + /// + /// The path to validate. + /// A which can be used to cancel the asynchronous operation. + /// A which return if the adb.exe file is exists, otherwise . + Task CheckFileExistsAsync(string adbPath, CancellationToken cancellationToken); } } #endif \ No newline at end of file diff --git a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs index f5eaffc0..a04102bb 100644 --- a/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs +++ b/AdvancedSharpAdbClient/Interfaces/IAdbCommandLineClient.cs @@ -25,6 +25,7 @@ public partial interface IAdbCommandLineClient /// Determines whether the adb.exe file exists. /// /// The path to validate. + /// if the adb.exe file is exists, otherwise . bool CheckFileExists(string adbPath); } } diff --git a/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs b/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs index 295846e9..0c9e9b13 100644 --- a/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs +++ b/AdvancedSharpAdbClient/Polyfills/DnsEndPoint.cs @@ -1,5 +1,6 @@ #if NETFRAMEWORK && !NET40_OR_GREATER using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Sockets; @@ -9,6 +10,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Represents a network endpoint as a host name or a string representation of an IP address and a port number. /// + [EditorBrowsable(EditorBrowsableState.Never)] public class DnsEndPoint : EndPoint { private readonly AddressFamily _family; diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs index 33e0d8a5..b3d26794 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/DateTimeExtensions.cs @@ -3,12 +3,14 @@ // using System; +using System.ComponentModel; namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides helper methods for working with Unix-based date formats. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class DateTimeExtensions { #if NETFRAMEWORK && !NET46_OR_GREATER diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs index e9718122..15a423b5 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumExtensions.cs @@ -3,12 +3,14 @@ // using System; +using System.ComponentModel; namespace AdvancedSharpAdbClient.Polyfills { /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class EnumExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs index 13eb90fc..302de322 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/EnumerableExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; namespace AdvancedSharpAdbClient.Polyfills @@ -7,6 +8,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class EnumerableExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs index 41a2f0fa..b4f3a1a4 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/ExceptionExtensions.cs @@ -1,4 +1,9 @@ -using System; +// +// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. +// + +using System; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -8,6 +13,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class ExceptionExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs index 3f6f8cb6..ba5f1b76 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/SocketExtensions.cs @@ -4,6 +4,7 @@ // using System; +using System.ComponentModel; using System.Net.Sockets; using System.Threading; @@ -12,6 +13,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class SocketExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs index 83c7173c..f0169bca 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StreamExtensions.cs @@ -4,6 +4,7 @@ // using System; +using System.ComponentModel; using System.IO; using System.Threading; @@ -12,6 +13,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class StreamExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs index d3480120..4dbcb829 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringBuilderExtensions.cs @@ -3,6 +3,7 @@ // Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved. // +using System.ComponentModel; using System.Text; namespace AdvancedSharpAdbClient.Polyfills @@ -10,6 +11,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class StringBuilderExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs index 1b1db8ba..94967f16 100644 --- a/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs +++ b/AdvancedSharpAdbClient/Polyfills/Extensions/StringExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Text; namespace AdvancedSharpAdbClient.Polyfills @@ -11,6 +12,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Provides extension methods for the class. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static class StringExtensions { /// diff --git a/AdvancedSharpAdbClient/Polyfills/HashCode.cs b/AdvancedSharpAdbClient/Polyfills/HashCode.cs index a88fa196..50eeeb3c 100644 --- a/AdvancedSharpAdbClient/Polyfills/HashCode.cs +++ b/AdvancedSharpAdbClient/Polyfills/HashCode.cs @@ -55,6 +55,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Combines the hash code for multiple values into a single hash code. /// + [EditorBrowsable(EditorBrowsableState.Never)] public struct HashCode { private static readonly uint s_seed = GenerateGlobalSeed(); diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs index 5c88a752..e92586af 100644 --- a/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IProgress.cs @@ -2,10 +2,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; + namespace AdvancedSharpAdbClient.Polyfills { /// Defines a provider for progress updates. /// The type of progress update value. + [EditorBrowsable(EditorBrowsableState.Never)] public interface IProgress { /// Reports a progress update. diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs index cb3ca93c..922a36bd 100644 --- a/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyCollection.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; namespace AdvancedSharpAdbClient.Polyfills { @@ -10,6 +11,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// Represents a strongly-typed, read-only collection of elements. /// /// The type of the elements. + [EditorBrowsable(EditorBrowsableState.Never)] public interface IReadOnlyCollection : IEnumerable { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs index 07822794..3abef338 100644 --- a/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs +++ b/AdvancedSharpAdbClient/Polyfills/Interfaces/IReadOnlyList.cs @@ -2,12 +2,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; + namespace AdvancedSharpAdbClient.Polyfills { /// /// Represents a read-only collection of elements that can be accessed by index. /// /// The type of elements in the read-only list. + [EditorBrowsable(EditorBrowsableState.Never)] public interface IReadOnlyList : IReadOnlyCollection { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Point.cs b/AdvancedSharpAdbClient/Polyfills/Point.cs index 9f6ac9e0..7e710288 100644 --- a/AdvancedSharpAdbClient/Polyfills/Point.cs +++ b/AdvancedSharpAdbClient/Polyfills/Point.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient.Polyfills @@ -10,6 +11,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// /// Represents an ordered pair of x and y coordinates that define a point in a two-dimensional plane. /// + [EditorBrowsable(EditorBrowsableState.Never)] public struct Point : IEquatable { /// diff --git a/AdvancedSharpAdbClient/Polyfills/Rectangle.cs b/AdvancedSharpAdbClient/Polyfills/Rectangle.cs index 520e97a4..e721c715 100644 --- a/AdvancedSharpAdbClient/Polyfills/Rectangle.cs +++ b/AdvancedSharpAdbClient/Polyfills/Rectangle.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; namespace AdvancedSharpAdbClient.Polyfills @@ -14,6 +15,7 @@ namespace AdvancedSharpAdbClient.Polyfills /// The y-coordinate of the upper-left corner of the rectangle. /// The width of the rectangle. /// The height of the rectangle. + [EditorBrowsable(EditorBrowsableState.Never)] public struct Rectangle(int x, int y, int width, int height) : IEquatable { /// diff --git a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs index 4fd4524f..05610a52 100644 --- a/AdvancedSharpAdbClient/Properties/GlobalUsings.cs +++ b/AdvancedSharpAdbClient/Properties/GlobalUsings.cs @@ -20,6 +20,7 @@ global using Windows.Foundation; global using Windows.Foundation.Metadata; global using Windows.Graphics.Imaging; +global using Windows.Storage; global using Windows.Storage.Streams; global using Windows.System; global using Windows.UI.Core; From 38968ab44b7323032807d8042cf71e76e4e0a53b Mon Sep 17 00:00:00 2001 From: wherewhere Date: Sun, 10 Dec 2023 21:25:00 +0800 Subject: [PATCH 12/12] Fix InstallMultiplePackageAsyncTest --- .../PackageManagerTests.Async.cs | 20 +++++++++---------- .../Exceptions/JavaException.cs | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs index 78dcf734..bb57f0de 100644 --- a/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs +++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs @@ -114,8 +114,8 @@ await manager.InstallMultipleRemotePackageAsync("/data/base.apk", ["/data/split_ Assert.Equal(5, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -r -t", adbClient.ReceivedCommands[0]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/base.apk\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[3]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[2..4]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2..4]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[4]); adbClient.ReceivedCommands.Clear(); @@ -185,11 +185,11 @@ await manager.InstallMultiplePackageAsync("Assets/TestApp/base.apk", ["Assets/Te Assert.Equal(9, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create", adbClient.ReceivedCommands[1]); Assert.Equal("shell:pm install-write 936013062 base.apk \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[2]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[4]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[3..5]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[3..5]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[5]); - Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[6]); - Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[7]); + Assert.Contains("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[6..8]); + Assert.Contains("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[6..8]); Assert.Equal("shell:rm \"/data/local/tmp/base.apk\"", adbClient.ReceivedCommands[8]); Assert.Equal(3, syncService.UploadedFiles.Count); @@ -212,11 +212,11 @@ await manager.InstallMultiplePackageAsync(["Assets/TestApp/split_config.arm64_v8 Assert.Equal(6, adbClient.ReceivedCommands.Count); Assert.Equal("shell:pm install-create -p com.google.android.gms", adbClient.ReceivedCommands[0]); - Assert.Equal("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1]); - Assert.Equal("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[2]); + Assert.Contains("shell:pm install-write 936013062 split0.apk \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[1..3]); + Assert.Contains("shell:pm install-write 936013062 split1.apk \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[1..3]); Assert.Equal("shell:pm install-commit 936013062", adbClient.ReceivedCommands[3]); - Assert.Equal("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[4]); - Assert.Equal("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[5]); + Assert.Contains("shell:rm \"/data/local/tmp/split_config.arm64_v8a.apk\"", adbClient.ReceivedCommands[4..6]); + Assert.Contains("shell:rm \"/data/local/tmp/split_config.xxhdpi.apk\"", adbClient.ReceivedCommands[4..6]); Assert.Equal(2, syncService.UploadedFiles.Count); Assert.True(syncService.UploadedFiles.ContainsKey("/data/local/tmp/split_config.arm64_v8a.apk")); diff --git a/AdvancedSharpAdbClient/Exceptions/JavaException.cs b/AdvancedSharpAdbClient/Exceptions/JavaException.cs index 2da3e4d1..83d63312 100644 --- a/AdvancedSharpAdbClient/Exceptions/JavaException.cs +++ b/AdvancedSharpAdbClient/Exceptions/JavaException.cs @@ -85,7 +85,6 @@ public JavaException(SerializationInfo serializationInfo, StreamingContext conte /// The equivalent . public static JavaException Parse(string line) => Parse(line.Split(separator, StringSplitOptions.RemoveEmptyEntries)); - /// /// Creates a from it representation. ///