diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.Async.cs
index ef87d743..c95bcf51 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 =
@@ -341,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));
@@ -400,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;
@@ -637,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,
@@ -681,825 +677,290 @@ await RunTestAsync(
NoSyncRequests,
NoSyncResponses,
[response],
- applicationDataChunks.ToArray(),
- () => TestClient.InstallAsync(Device, stream));
+ applicationDataChunks,
+ () => TestClient.InstallAsync(Device, stream,
+ new InstallProgress(
+ PackageInstallProgressState.Preparing,
+ PackageInstallProgressState.Uploading,
+ PackageInstallProgressState.Installing,
+ PackageInstallProgressState.Finished)));
}
}
///
- /// Tests the method.
- ///
- [Fact]
- public async void InstallCreateAsyncTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "exec:cmd package 'install-create' -p com.google.android.gms"
- ];
-
- byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n");
- await using MemoryStream shellStream = new(streamData);
-
- string session = await RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.InstallCreateAsync(Device, "com.google.android.gms"));
-
- Assert.Equal("936013062", session);
- }
-
- ///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public async void InstallWriteAsyncTest()
+ public async void InstallMultipleAsyncTest()
{
- 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/split_config.arm64_v8a.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 RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
- requests,
- NoSyncRequests,
- NoSyncResponses,
- [response],
- applicationDataChunks.ToArray(),
- () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062"));
- }
- }
+ await using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk");
- ///
- /// Tests the method.
- ///
- [Fact]
- public async void InstallCommitAsyncTest()
- {
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[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
- await using MemoryStream shellStream = new(streamData);
-
- await RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.InstallCommitAsync(Device, "936013062"));
- }
-
- ///
- /// Tests the method.
- ///
- [Fact]
- public async void UninstallAsyncTest()
- {
- string[] requests =
+ byte[][] responses =
[
- "host:transport:169.254.109.177:5555",
- "exec:cmd package 'uninstall' com.android.gallery3d"
+ Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n")
];
- byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
- using MemoryStream shellStream = new(streamData);
+ 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(2),
+ OkResponses(6),
NoResponseMessages,
requests,
- [shellStream],
- () => TestClient.UninstallAsync(Device, "com.android.gallery3d"));
- }
-
- ///
- /// Tests the method.
- ///
- [Fact]
- public async void GetFeatureSetAsyncTest()
- {
- string[] requests = ["host-serial:169.254.109.177:5555:features"];
- string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"];
-
- string[] features = await RunTestAsync(
- OkResponse,
+ NoSyncRequests,
+ NoSyncResponses,
responses,
- requests,
- async () => await TestClient.GetFeatureSetAsync(Device).ToArrayAsync());
-
- Assert.Equal(12, features.Length);
- Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault());
- Assert.Equal("stat_v2", features.LastOrDefault());
+ 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.
+ /// Tests the method.
///
[Fact]
- public async void DumpScreenStringAsyncTest()
+ public async void InstallMultipleWithBaseAsyncTest()
{
- 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));
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
- Assert.Equal(cleanMIUIDump, miuiXml);
- }
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- ///
- /// Tests the method.
- ///
- [Fact]
- public async void DumpScreenStringAsyncEmptyTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
- ];
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- await using MemoryStream emptyStream = new();
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- string emptyXml = await RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [emptyStream],
- () => TestClient.DumpScreenStringAsync(Device));
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- Assert.True(string.IsNullOrEmpty(emptyXml));
- }
+ await using FileStream baseStream = File.OpenRead("Assets/TestApp/base.apk");
+ await using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk");
- ///
- /// 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 =
- [
+ "exec:cmd package 'install-create'",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {baseStream.Length} 936013062 baseAPK.apk",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk",
"host:transport:169.254.109.177:5555",
- "shell:input tap 100 100"
+ "exec:cmd package 'install-commit' 936013062"
];
- 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 =
+ byte[][] responses =
[
- "host:transport:169.254.109.177:5555",
- "shell:input tap 100 100"
+ Encoding.ASCII.GetBytes($"Success: streamed {baseStream.Length} bytes\n"),
+ Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n")
];
- 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();
+ 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(2),
+ OkResponses(8),
NoResponseMessages,
requests,
- [shellStream],
- () => TestClient.SwipeAsync(Device, 100, 200, 300, 400, 500));
+ 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.
+ /// 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)
+ public async void InstallCreateAsyncTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:dumpsys activity activities | grep mResumedActivity"
+ "exec:cmd package 'install-create' -p com.google.android.gms"
];
- byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029}
- mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n");
await using MemoryStream shellStream = new(streamData);
- AppStatus result = await RunTestAsync(
+ string session = 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);
+ () => TestClient.InstallCreateAsync(Device, "com.google.android.gms"));
- 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());
+ Assert.Equal("936013062", session);
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public async void FindElementsAsyncTest()
+ public async void InstallWriteAsyncTest()
{
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
- ];
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
- string dump = File.ReadAllText(@"Assets/dumpscreen.txt");
- byte[] streamData = Encoding.UTF8.GetBytes(dump);
- await using MemoryStream shellStream = new(streamData);
+ await using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- 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);
- }
+ while ((read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- ///
- /// Tests the method.
- ///
- [Fact]
- public async void FindAsyncElementsTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
- ];
+ 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"
+ ];
- string dump = File.ReadAllText(@"Assets/dumpscreen.txt");
- byte[] streamData = Encoding.UTF8.GetBytes(dump);
- await using MemoryStream shellStream = new(streamData);
+ byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Length} bytes\n");
- List elements = await RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- async () =>
+ double temp = 0;
+ Progress progress = new();
+ progress.ProgressChanged += (sender, args) =>
{
- 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);
- }
+ Assert.True(temp <= args, $"{nameof(args)}: {args} is less than {temp}.");
+ temp = args;
+ };
- ///
- /// 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"));
+ await RunTestAsync(
+ OkResponses(2),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ [response],
+ applicationDataChunks,
+ () => TestClient.InstallWriteAsync(Device, stream, "base", "936013062", progress));
+ }
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public async void SendTextAsyncTest()
+ public async void InstallCommitAsyncTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:input text Hello, World",
+ "exec:cmd package 'install-commit' 936013062"
];
- await using MemoryStream shellStream = new();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
+ await using MemoryStream shellStream = new(streamData);
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"));
+ () => TestClient.InstallCommitAsync(Device, "936013062"));
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public async void ClickBackButtonAsyncTest()
+ public async void UninstallAsyncTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:input keyevent KEYCODE_BACK"
+ "exec:cmd package 'uninstall' com.android.gallery3d"
];
- await using MemoryStream shellStream = new();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
+ using MemoryStream shellStream = new(streamData);
await RunTestAsync(
OkResponses(2),
NoResponseMessages,
requests,
[shellStream],
- () => TestClient.ClickBackButtonAsync(Device));
+ () => TestClient.UninstallAsync(Device, "com.android.gallery3d"));
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public async void ClickHomeButtonAsyncTest()
+ public async void GetFeatureSetAsyncTest()
{
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:input keyevent KEYCODE_HOME"
- ];
-
- await using MemoryStream shellStream = new();
+ string[] requests = ["host-serial:169.254.109.177:5555:features"];
+ string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"];
- await RunTestAsync(
- OkResponses(2),
- NoResponseMessages,
+ string[] features = await RunTestAsync(
+ OkResponse,
+ responses,
requests,
- [shellStream],
- () => TestClient.ClickHomeButtonAsync(Device));
+ async () => await TestClient.GetFeatureSetAsync(Device).ToArrayAsync());
+
+ Assert.Equal(12, features.Length);
+ Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault());
+ Assert.Equal("stat_v2", features.LastOrDefault());
}
private Task RunConnectAsyncTest(Func test, string connectString)
diff --git a/AdvancedSharpAdbClient.Tests/AdbClientTests.cs b/AdvancedSharpAdbClient.Tests/AdbClientTests.cs
index bff1ee48..754f3841 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 =
@@ -452,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));
@@ -508,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;
@@ -745,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,
@@ -789,791 +785,314 @@ public void InstallTest()
NoSyncRequests,
NoSyncResponses,
[response],
- applicationDataChunks.ToArray(),
- () => TestClient.Install(Device, stream));
+ applicationDataChunks,
+ () => TestClient.Install(Device, stream,
+ new InstallProgress(
+ PackageInstallProgressState.Preparing,
+ PackageInstallProgressState.Uploading,
+ PackageInstallProgressState.Installing,
+ PackageInstallProgressState.Finished)));
}
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void InstallCreateTest()
+ public void InstallMultipleTest()
{
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "exec:cmd package 'install-create' -p com.google.android.gms"
- ];
-
- byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n");
- using MemoryStream shellStream = new(streamData);
-
- string session = RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.InstallCreate(Device, "com.google.android.gms"));
-
- Assert.Equal("936013062", session);
- }
-
- ///
- /// 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/split_config.arm64_v8a.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/split_config.xxhdpi.apk"))
{
- RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- NoSyncRequests,
- NoSyncResponses,
- [response],
- applicationDataChunks.ToArray(),
- () => TestClient.InstallWrite(Device, stream, "base", "936013062"));
- }
- }
-
- ///
- /// Tests the method.
- ///
- [Fact]
- public void InstallCommitTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "exec:cmd package 'install-commit' 936013062"
- ];
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
- using MemoryStream shellStream = new(streamData);
+ while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.InstallCommit(Device, "936013062"));
- }
+ using FileStream abiStream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk");
+ using FileStream dpiStream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk");
- ///
- /// Tests the method.
- ///
- [Fact]
- public void UninstallTest()
- {
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "exec:cmd package 'uninstall' com.android.gallery3d"
- ];
-
- byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
- using MemoryStream shellStream = new(streamData);
-
- RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.Uninstall(Device, "com.android.gallery3d"));
- }
-
- ///
- /// Tests the method.
- ///
- [Fact]
- public void GetFeatureSetTest()
- {
- string[] requests = ["host-serial:169.254.109.177:5555:features"];
- string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"];
-
- string[] features = RunTest(
- OkResponse,
- responses,
- requests,
- () => TestClient.GetFeatureSet(Device).ToArray());
-
- Assert.Equal(12, features.Length);
- Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault());
- Assert.Equal("stat_v2", features.LastOrDefault());
- }
-
- ///
- /// Tests the method.
- ///
- [Fact]
- public void DumpScreenStringTest()
- {
- string[] requests =
- [
+ "exec:cmd package 'install-create' -p com.google.android.gms",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {dpiStream.Length} 936013062 splitAPK1.apk",
"host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
+ "exec:cmd package 'install-commit' 936013062"
];
- 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 =
+ byte[][] responses =
[
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
+ Encoding.ASCII.GetBytes($"Success: streamed {abiStream.Length} bytes\n"),
+ Encoding.ASCII.GetBytes($"Success: streamed {dpiStream.Length} bytes\n")
];
- string errorXml = File.ReadAllText(@"Assets/dumpscreen_error.txt");
- byte[] errorStreamData = Encoding.UTF8.GetBytes(errorXml);
- using MemoryStream errorStream = new(errorStreamData);
+ using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"));
+ using MemoryStream commitStream = new("Success\n"u8.ToArray());
- Assert.Throws(() =>
RunTest(
- OkResponses(2),
+ OkResponses(8),
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);
+ 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)));
}
-#if WINDOWS10_0_17763_0_OR_GREATER
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void DumpScreenWinRTTest()
+ public void InstallMultipleWithBaseTest()
{
- 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);
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
- Windows.Data.Xml.Dom.XmlDocument xml = RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.DumpScreenWinRT(Device));
+ using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- string cleanDump = File.ReadAllText(@"Assets/dumpscreen_clean.txt");
- Windows.Data.Xml.Dom.XmlDocument doc = new();
- doc.LoadXml(cleanDump);
+ while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- Assert.Equal(doc.InnerText, xml.InnerText);
- }
+ using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.arm64_v8a.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
-#endif
+ while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- ///
- /// Tests the method.
- ///
- [Fact]
- public void ClickTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:input tap 100 100"
- ];
+ using (FileStream stream = File.OpenRead("Assets/TestApp/split_config.xxhdpi.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- 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);
+ while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- 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);
- }
+ 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");
- ///
- /// 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 =
- [
+ "exec:cmd package 'install-create'",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {baseStream.Length} 936013062 baseAPK.apk",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {abiStream.Length} 936013062 splitAPK0.apk",
"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 =
- [
+ $"exec:cmd package 'install-write' -S {dpiStream.Length} 936013062 splitAPK1.apk",
"host:transport:169.254.109.177:5555",
- "shell:dumpsys activity activities | grep mResumedActivity"
+ "exec:cmd package 'install-commit' 936013062"
];
- 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 =
+ byte[][] responses =
[
- "host:transport:169.254.109.177:5555",
- "shell:dumpsys activity activities | grep mResumedActivity",
- "host:transport:169.254.109.177:5555",
- $"shell:pidof {packageName}"
+ 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")
];
- 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);
+ using MemoryStream sessionStream = new(Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n"));
+ using MemoryStream commitStream = new("Success\n"u8.ToArray());
- AppStatus result = RunTest(
- OkResponses(4),
+ RunTest(
+ OkResponses(10),
NoResponseMessages,
requests,
- [activityStream, pidStream],
- () => TestClient.GetAppStatus(Device, packageName));
-
- Assert.Equal(expected, result);
+ 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.
+ /// Tests the method.
///
- [Theory]
- [InlineData("app.lawnchair", AppStatus.Foreground)]
- [InlineData("com.android.settings", AppStatus.Foreground)]
- public void GetAppStatusForegroundTest(string packageName, AppStatus expected)
+ [Fact]
+ public void InstallCreateTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:dumpsys activity activities | grep mResumedActivity"
+ "exec:cmd package 'install-create' -p com.google.android.gms"
];
- byte[] streamData = @" mResumedActivity: ActivityRecord{1f5309a u0 com.android.settings/.homepage.SettingsHomepageActivity t61029}
- mResumedActivity: ActivityRecord{896cc3 u0 app.lawnchair/.LawnchairLauncher t5}"u8.ToArray();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success: created install session [936013062]\r\n");
using MemoryStream shellStream = new(streamData);
- AppStatus result = RunTest(
+ string session = RunTest(
OkResponses(2),
NoResponseMessages,
requests,
[shellStream],
- () => TestClient.GetAppStatus(Device, packageName));
+ () => TestClient.InstallCreate(Device, "com.google.android.gms"));
- Assert.Equal(expected, result);
+ Assert.Equal("936013062", session);
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void FindElementTest()
+ public void InstallWriteTest()
{
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
- ];
+ // The app data is sent in chunks of 32 kb
+ List applicationDataChunks = [];
- string dump = File.ReadAllText(@"Assets/dumpscreen.txt");
- byte[] streamData = Encoding.UTF8.GetBytes(dump);
- using MemoryStream shellStream = new(streamData);
+ using (FileStream stream = File.OpenRead("Assets/TestApp/base.apk"))
+ {
+ byte[] buffer = new byte[32 * 1024];
+ int read = 0;
- 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());
- }
+ while ((read = stream.Read(buffer.AsSpan(0, buffer.Length))) > 0)
+ {
+ byte[] array = buffer.AsSpan(0, read).ToArray();
+ applicationDataChunks.Add(array);
+ }
+ }
- ///
- /// Tests the method.
- ///
- [Fact]
- public void FindElementsTest()
- {
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:uiautomator dump /dev/tty"
- ];
+ 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"
+ ];
- string dump = File.ReadAllText(@"Assets/dumpscreen.txt");
- byte[] streamData = Encoding.UTF8.GetBytes(dump);
- using MemoryStream shellStream = new(streamData);
+ byte[] response = Encoding.ASCII.GetBytes($"Success: streamed {stream.Length} bytes\n");
- 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);
+ RunTest(
+ OkResponses(2),
+ NoResponseMessages,
+ requests,
+ NoSyncRequests,
+ NoSyncResponses,
+ [response],
+ applicationDataChunks,
+ () => TestClient.InstallWrite(Device, stream, "base", "936013062", new InstallProgress()));
+ }
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void SendKeyEventTest()
+ public void InstallCommitTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:input keyevent KEYCODE_MOVE_END"
+ "exec:cmd package 'install-commit' 936013062"
];
- using MemoryStream shellStream = new();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
+ using MemoryStream shellStream = new(streamData);
RunTest(
OkResponses(2),
NoResponseMessages,
requests,
[shellStream],
- () => TestClient.SendKeyEvent(Device, "KEYCODE_MOVE_END"));
+ () => TestClient.InstallCommit(Device, "936013062"));
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void SendTextTest()
+ public void UninstallTest()
{
string[] requests =
[
"host:transport:169.254.109.177:5555",
- "shell:input text Hello, World",
+ "exec:cmd package 'uninstall' com.android.gallery3d"
];
- using MemoryStream shellStream = new();
+ byte[] streamData = Encoding.ASCII.GetBytes("Success\r\n");
+ using MemoryStream shellStream = new(streamData);
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"));
+ () => TestClient.Uninstall(Device, "com.android.gallery3d"));
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
- public void ClickBackButtonTest()
+ public void GetFeatureSetTest()
{
- string[] requests =
- [
- "host:transport:169.254.109.177:5555",
- "shell:input keyevent KEYCODE_BACK"
- ];
-
- using MemoryStream shellStream = new();
+ string[] requests = ["host-serial:169.254.109.177:5555:features"];
+ string[] responses = ["sendrecv_v2_brotli,remount_shell,sendrecv_v2,abb_exec,fixed_push_mkdir,fixed_push_symlink_timestamp,abb,shell_v2,cmd,ls_v2,apex,stat_v2\r\n"];
- RunTest(
- OkResponses(2),
- NoResponseMessages,
+ string[] features = RunTest(
+ OkResponse,
+ responses,
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();
+ () => TestClient.GetFeatureSet(Device).ToArray());
- RunTest(
- OkResponses(2),
- NoResponseMessages,
- requests,
- [shellStream],
- () => TestClient.ClickHomeButton(Device));
+ Assert.Equal(12, features.Length);
+ Assert.Equal("sendrecv_v2_brotli", features.FirstOrDefault());
+ Assert.Equal("stat_v2", features.LastOrDefault());
}
private void RunConnectTest(Action test, string connectString)
@@ -1625,5 +1144,59 @@ private void RunCreateForwardTest(Action test, string forwardString)
requests,
() => test(Device));
}
+
+ private struct InstallProgress(params PackageInstallProgressState[] states) : IProgress, 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;
+ }
+
+ public void Report(double value)
+ {
+ Assert.True(uploadProgress <= value, $"{nameof(value)}: {value} is less than {uploadProgress}.");
+ uploadProgress = value;
+ }
+ }
}
}
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/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/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/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-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/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
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 00000000..35ef9084
Binary files /dev/null and b/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.arm64_v8a.apk differ
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 00000000..9dfb4402
Binary files /dev/null and b/AdvancedSharpAdbClient.Tests/Assets/TestApp/split_config.xxhdpi.apk differ
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
new file mode 100644
index 00000000..4678eb89
--- /dev/null
+++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.Async.cs
@@ -0,0 +1,469 @@
+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.xml");
+
+ 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.Clean.xml");
+
+ 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.xml");
+ 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.xml");
+ 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 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.
+ ///
+ [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..6e175e65
--- /dev/null
+++ b/AdvancedSharpAdbClient.Tests/DeviceCommands/DeviceClientTexts.cs
@@ -0,0 +1,457 @@
+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.xml");
+
+ 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.Clean.xml");
+
+ 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.xml");
+ 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.xml");
+ 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 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.
+ ///
+ [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..b9a75121 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()
{
@@ -51,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()
@@ -60,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]
@@ -296,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()
@@ -308,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 d96a0bcf..e3e5f268 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"));
@@ -51,18 +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";
- 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]);
- 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]
@@ -296,21 +339,15 @@ 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;
- 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);
- 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]
@@ -342,8 +379,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/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/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/PackageManagerTests.Async.cs b/AdvancedSharpAdbClient.Tests/DeviceCommands/PackageManagerTests.Async.cs
index 2d62a816..bb57f0de 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.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();
- 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.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.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.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);
- 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.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.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.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/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/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..5ca72ca5 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.
@@ -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 bd6717d8..148bc6c3 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();
@@ -209,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();
@@ -221,25 +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();
-
- 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();
+ Task IAdbClient.InstallWriteAsync(DeviceData device, Stream apk, string apkName, string session, IProgress progress, CancellationToken cancellationToken) => throw new NotImplementedException();
void IAdbClient.KillAdb() => 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/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.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 be8e0a92..bcf05f0f 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()
@@ -40,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);
@@ -101,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/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.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.Tests/SocketBasedTests.cs b/AdvancedSharpAdbClient.Tests/SocketBasedTests.cs
index 9afb513b..fa0cb53c 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
{
@@ -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 7aefb0b8..2547944a 100644
--- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs
+++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.Async.cs
@@ -147,13 +147,13 @@ public async void GetAsyncListingTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
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(
@@ -182,13 +182,13 @@ await RunTestAsync(
}
///
- /// Tests the method.
+ /// Tests the method.
///
[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..033048c2 100644
--- a/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
+++ b/AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
@@ -98,13 +98,13 @@ public void GetListingTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[Fact]
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(
@@ -133,13 +133,13 @@ public void PullTest()
}
///
- /// Tests the method.
+ /// Tests the method.
///
[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 c15d9a52..b4ec0753 100644
--- a/AdvancedSharpAdbClient/AdbClient.Async.cs
+++ b/AdvancedSharpAdbClient/AdbClient.Async.cs
@@ -10,18 +10,15 @@
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
{
public partial class AdbClient
{
///
- public async Task GetAdbVersionAsync(CancellationToken cancellationToken = default)
+ public virtual async Task GetAdbVersionAsync(CancellationToken cancellationToken = default)
{
using IAdbSocket socket = adbSocketFactory(EndPoint);
@@ -33,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);
@@ -43,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);
@@ -56,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);
@@ -72,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);
@@ -90,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);
@@ -102,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);
@@ -114,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);
@@ -124,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);
@@ -134,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);
@@ -148,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);
@@ -164,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);
@@ -172,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);
@@ -188,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);
@@ -200,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);
@@ -208,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);
@@ -233,10 +230,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)
@@ -256,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);
@@ -268,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);
@@ -280,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);
@@ -331,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);
@@ -343,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);
@@ -355,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);
@@ -367,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);
@@ -379,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.
@@ -427,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);
@@ -460,15 +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)
{
-#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
+ totalBytesRead += read;
+ progress?.Report(new InstallProgressEventArgs(0, 1, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess));
}
+ progress?.Report(new InstallProgressEventArgs(1, 1, 100));
+ progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
string value =
#if HAS_BUFFERS
@@ -481,79 +487,109 @@ 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, 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);
- string session = await InstallCreateAsync(device, packageName, cancellationToken, arguments).ConfigureAwait(false);
+ if (!baseAPK.CanRead || !baseAPK.CanSeek)
+ {
+ throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream");
+ }
- int i = 0;
- await Extensions.WhenAll(splitAPKs.Select(async splitAPK =>
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek))
{
- if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek)
- {
- Debug.WriteLine("The apk stream must be a readable and seekable stream");
- return;
- }
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
+ }
- try
- {
- await InstallWriteAsync(device, splitAPK, $"{nameof(splitAPK)}{i++}", session, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
+ progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ string session = await InstallCreateAsync(device, null, cancellationToken, arguments).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));
+
+ await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, OnMainSyncProgressChanged, cancellationToken).ConfigureAwait(false);
+
+ int progressCount = 1;
+ Dictionary status = new(splitAPKsCount);
+ void OnSplitSyncProgressChanged(string? sender, double args)
+ {
+ lock (status)
{
- Debug.WriteLine(ex.Message);
+ 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));
}
- })).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 InstallMultipleAsync(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, 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(baseAPK);
ExceptionExtensions.ThrowIfNull(splitAPKs);
+ ExceptionExtensions.ThrowIfNull(packageName);
- if (!baseAPK.CanRead || !baseAPK.CanSeek)
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek))
{
- throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream");
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
}
- string session = await InstallCreateAsync(device, null, cancellationToken, arguments).ConfigureAwait(false);
-
- await InstallWriteAsync(device, baseAPK, nameof(baseAPK), session, cancellationToken).ConfigureAwait(false);
+ 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 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);
@@ -593,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(0);
+
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
ExceptionExtensions.ThrowIfNull(apkName);
@@ -621,13 +659,20 @@ 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)
{
-#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
+ totalBytesRead += read;
+ progress?.Report(totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
read = await socket.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
@@ -642,41 +687,40 @@ public async Task InstallWriteAsync(DeviceData device, Stream apk, string apkNam
{
throw new AdbException(value);
}
+ progress?.Report(100);
}
- ///
- public async Task InstallCommitAsync(DeviceData device, string session, CancellationToken cancellationToken = default)
+ ///
+ /// 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)
{
- using IAdbSocket socket = adbSocketFactory(EndPoint);
- await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false);
-
- await socket.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", cancellationToken).ConfigureAwait(false);
- _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
+ progress?.Invoke(apkName, 0);
- using StreamReader reader = new(socket.GetShellStream(), Encoding);
- string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
- if (result?.Contains("Success") != true)
- {
- throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false));
- }
- }
-
- ///
- public async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments)
- {
EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(apk);
+ ExceptionExtensions.ThrowIfNull(apkName);
+ ExceptionExtensions.ThrowIfNull(session);
- StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'");
-
- if (arguments != null)
+ if (!apk.CanRead || !apk.CanSeek)
{
- foreach (string argument in arguments)
- {
- _ = requestBuilder.AppendFormat(" {0}", argument);
- }
+ throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream");
}
- _ = requestBuilder.AppendFormat(" {0}", packageName);
+ 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);
@@ -684,441 +728,101 @@ public async Task UninstallAsync(DeviceData device, string packageName, Cancella
await socket.SendAdbRequestAsync(requestBuilder.ToString(), cancellationToken).ConfigureAwait(false);
_ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
- using StreamReader reader = new(socket.GetShellStream(), Encoding);
- string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
- if (result?.Contains("Success") != true)
- {
- throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false));
- }
- }
-
- ///
- public async Task> GetFeatureSetAsync(DeviceData device, CancellationToken cancellationToken = default)
- {
- EnsureDevice(device);
-
- using IAdbSocket socket = adbSocketFactory(EndPoint);
- await socket.SendAdbRequestAsync($"host-serial:{device.Serial}:features", cancellationToken).ConfigureAwait(false);
- _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
- string features = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false);
-
- 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))
+#if HAS_BUFFERS
+ while ((read = await apk.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
{
- 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))
+ await socket.SendAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
+#else
+ while ((read = await apk.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0)
{
- doc.LoadXml(xmlString);
- return doc;
- }
- return null;
- }
+ await socket.SendAsync(buffer, read, cancellationToken).ConfigureAwait(false);
#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);
+ totalBytesRead += read;
+ progress?.Invoke(apkName, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
- 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);
+ progress?.Invoke(apkName, 100);
- 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);
+ 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 (result.StartsWith("java.lang."))
- {
- throw JavaException.Parse(result);
- }
- else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR
+ if (!value.Contains("Success"))
{
- throw new ElementNotFoundException("Coordinates of element is invalid");
+ throw new AdbException(value);
}
+ progress?.Invoke(null, 100);
}
///
- public async Task SwipeAsync(DeviceData device, int x1, int y1, int x2, int y2, long speed, CancellationToken cancellationToken = default)
+ public virtual async Task InstallCommitAsync(DeviceData device, string session, 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.SendAdbRequestAsync($"exec:cmd package 'install-commit' {session}", 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
+ string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
+ if (result?.Contains("Success") != true)
{
- throw new ElementNotFoundException("Coordinates of element is invalid");
+ throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false));
}
}
///
- 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)
+ public virtual async Task UninstallAsync(DeviceData device, string packageName, CancellationToken cancellationToken, params string[] arguments)
{
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();
- }
+ StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'");
-#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)
+ if (arguments != null)
{
- 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)
+ foreach (string argument in arguments)
{
- 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;
- }
+ _ = requestBuilder.AppendFormat(" {0}", argument);
}
-
- 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);
+ _ = requestBuilder.AppendFormat(" {0}", packageName);
using IAdbSocket socket = adbSocketFactory(EndPoint);
await socket.SetDeviceAsync(device, cancellationToken).ConfigureAwait(false);
- await socket.SendAdbRequestAsync($"shell:input text {text}", cancellationToken).ConfigureAwait(false);
+ await socket.SendAdbRequestAsync(requestBuilder.ToString(), 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
+ string? result = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
+ if (result?.Contains("Success") != true)
{
- throw new InvalidTextException();
+ throw new AdbException(await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false));
}
}
///
- public async Task StartAppAsync(DeviceData device, string packageName, CancellationToken cancellationToken = default)
+ public virtual async Task> GetFeatureSetAsync(DeviceData device, 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.SendAdbRequestAsync($"host-serial:{device.Serial}:features", 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);
+ string features = await socket.ReadStringAsync(cancellationToken).ConfigureAwait(false);
- await socket.SendAdbRequestAsync($"shell:am force-stop {packageName}", cancellationToken).ConfigureAwait(false);
- _ = await socket.ReadAdbResponseAsync(cancellationToken).ConfigureAwait(false);
+ IEnumerable featureList = features.Trim().Split('\n', ',');
+ return featureList;
}
}
}
diff --git a/AdvancedSharpAdbClient/AdbClient.cs b/AdvancedSharpAdbClient/AdbClient.cs
index 59ebd5d0..fb789e12 100644
--- a/AdvancedSharpAdbClient/AdbClient.cs
+++ b/AdvancedSharpAdbClient/AdbClient.cs
@@ -5,16 +5,13 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
-using System.Text.RegularExpressions;
using System.Threading;
-using System.Xml;
namespace AdvancedSharpAdbClient
{
@@ -170,7 +167,7 @@ public static byte[] CreateAdbForwardRequest(string address, int port)
}
///
- public int GetAdbVersion()
+ public virtual int GetAdbVersion()
{
using IAdbSocket socket = adbSocketFactory(EndPoint);
@@ -182,7 +179,7 @@ public int GetAdbVersion()
}
///
- public void KillAdb()
+ public virtual void KillAdb()
{
using IAdbSocket socket = adbSocketFactory(EndPoint);
socket.SendAdbRequest("host:kill");
@@ -192,7 +189,7 @@ public void KillAdb()
}
///
- public IEnumerable GetDevices()
+ public virtual IEnumerable GetDevices()
{
using IAdbSocket socket = adbSocketFactory(EndPoint);
@@ -205,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);
@@ -221,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);
@@ -239,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);
@@ -251,7 +248,7 @@ public void RemoveReverseForward(DeviceData device, string remote)
}
///
- public void RemoveAllReverseForwards(DeviceData device)
+ public virtual void RemoveAllReverseForwards(DeviceData device)
{
EnsureDevice(device);
@@ -263,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);
@@ -273,7 +270,7 @@ public void RemoveForward(DeviceData device, int localPort)
}
///
- public void RemoveAllForwards(DeviceData device)
+ public virtual void RemoveAllForwards(DeviceData device)
{
EnsureDevice(device);
@@ -313,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);
@@ -337,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);
@@ -349,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);
@@ -382,7 +379,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)
@@ -396,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);
@@ -426,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);
@@ -474,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);
@@ -571,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);
@@ -604,15 +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)
{
-#if HAS_BUFFERS
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 == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess));
}
+ progress?.Report(new InstallProgressEventArgs(1, 1, 100));
+ progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.Installing));
read = socket.Read(buffer);
string value =
#if HAS_BUFFERS
@@ -625,75 +633,111 @@ 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, 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);
- string session = InstallCreate(device, packageName, arguments);
+ if (!baseAPK.CanRead || !baseAPK.CanSeek)
+ {
+ throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream");
+ }
- int i = 0;
- foreach (Stream splitAPK in splitAPKs)
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek))
{
- if (splitAPK == null || !splitAPK.CanRead || !splitAPK.CanSeek)
- {
- Debug.WriteLine("The apk stream must be a readable and seekable stream");
- continue;
- }
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
+ }
- try
- {
- InstallWrite(device, splitAPK, $"{nameof(splitAPK)}{i++}", session);
- }
- catch (Exception ex)
+ progress?.Report(new InstallProgressEventArgs(PackageInstallProgressState.CreateSession));
+ string session = InstallCreate(device, null, arguments);
+
+ 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 OnSplitSyncProgressChanged(string? sender, double args)
+ {
+ lock (status)
{
- Debug.WriteLine(ex.Message);
+ 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));
}
}
+ 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));
}
///
- public void InstallMultiple(DeviceData device, Stream baseAPK, IEnumerable splitAPKs, 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);
+ ExceptionExtensions.ThrowIfNull(packageName);
- if (!baseAPK.CanRead || !baseAPK.CanSeek)
+ if (splitAPKs.Any(apk => apk == null || !apk.CanRead || !apk.CanSeek))
{
- throw new ArgumentOutOfRangeException(nameof(baseAPK), "The apk stream must be a readable and seekable stream");
+ throw new ArgumentOutOfRangeException(nameof(splitAPKs), "The apk stream must be a readable and seekable stream");
}
- string session = InstallCreate(device, null, arguments);
-
- InstallWrite(device, baseAPK, nameof(baseAPK), session);
+ 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));
}
///
@@ -736,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(0);
+
EnsureDevice(device);
ExceptionExtensions.ThrowIfNull(apk);
ExceptionExtensions.ThrowIfNull(apkName);
@@ -764,13 +810,20 @@ 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)
{
-#if HAS_BUFFERS
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 == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
read = socket.Read(buffer);
@@ -785,43 +838,37 @@ public void InstallWrite(DeviceData device, Stream apk, string apkName, string s
{
throw new AdbException(value);
}
+ progress?.Report(100);
}
- ///
- public void InstallCommit(DeviceData device, string session)
+ ///
+ /// 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)
{
- EnsureDevice(device);
-
- using IAdbSocket socket = adbSocketFactory(EndPoint);
- socket.SetDevice(device);
-
- socket.SendAdbRequest($"exec:cmd package 'install-commit' {session}");
- _ = socket.ReadAdbResponse();
-
- using StreamReader reader = new(socket.GetShellStream(), Encoding);
- string? result = reader.ReadLine();
- if (result?.Contains("Success") != true)
- {
- throw new AdbException(reader.ReadToEnd());
- }
- }
+ progress?.Invoke(apkName, 0);
- ///
- public void Uninstall(DeviceData device, string packageName, params string[] arguments)
- {
EnsureDevice(device);
+ ExceptionExtensions.ThrowIfNull(apk);
+ ExceptionExtensions.ThrowIfNull(apkName);
+ ExceptionExtensions.ThrowIfNull(session);
- StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'");
-
- if (arguments != null)
+ if (!apk.CanRead || !apk.CanSeek)
{
- foreach (string argument in arguments)
- {
- _ = requestBuilder.AppendFormat(" {0}", argument);
- }
+ throw new ArgumentOutOfRangeException(nameof(apk), "The apk stream must be a readable and seekable stream");
}
- _ = requestBuilder.AppendFormat(" {0}", packageName);
+ 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);
@@ -829,374 +876,103 @@ public void Uninstall(DeviceData device, string packageName, params string[] arg
socket.SendAdbRequest(requestBuilder.ToString());
_ = socket.ReadAdbResponse();
- using StreamReader reader = new(socket.GetShellStream(), Encoding);
- string? result = reader.ReadLine();
- if (result?.Contains("Success") != true)
- {
- throw new AdbException(reader.ReadToEnd());
- }
- }
-
- ///
- public IEnumerable GetFeatureSet(DeviceData device)
- {
- EnsureDevice(device);
-
- using IAdbSocket socket = adbSocketFactory(EndPoint);
- socket.SendAdbRequest($"host-serial:{device.Serial}:features");
- _ = socket.ReadAdbResponse();
- string features = socket.ReadString();
-
- IEnumerable featureList = features.Trim().Split('\n', ',');
- 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))
+#if HAS_BUFFERS
+ while ((read = apk.Read(buffer)) > 0)
{
- 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))
+ socket.Send(buffer.AsSpan(0, read));
+#else
+ while ((read = apk.Read(buffer, 0, buffer.Length)) > 0)
{
- doc.LoadXml(xmlString);
- return doc;
- }
- return null;
- }
+ socket.Send(buffer, read);
#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");
+ totalBytesRead += read;
+ progress?.Invoke(apkName, totalBytesToProcess == 0 ? 0 : totalBytesRead * 100d / totalBytesToProcess);
}
- }
-
- ///
- 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();
+ progress?.Invoke(apkName, 100);
- using StreamReader reader = new(socket.GetShellStream(), Encoding);
- string result = reader.ReadToEnd().TrimStart();
+ 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 (result.StartsWith("java.lang."))
- {
- throw JavaException.Parse(result);
- }
- else if (result.Contains("ERROR", StringComparison.OrdinalIgnoreCase)) // error or ERROR
+ if (!value.Contains("Success"))
{
- throw new ElementNotFoundException("Coordinates of element is invalid");
+ throw new AdbException(value);
}
+ progress?.Invoke(null, 100);
}
///
- public void Swipe(DeviceData device, int x1, int y1, int x2, int y2, long speed)
+ public virtual void InstallCommit(DeviceData device, string session)
{
EnsureDevice(device);
using IAdbSocket socket = adbSocketFactory(EndPoint);
socket.SetDevice(device);
- socket.SendAdbRequest($"shell:input swipe {x1} {y1} {x2} {y2} {speed}");
+ socket.SendAdbRequest($"exec:cmd package 'install-commit' {session}");
_ = 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
+ string? result = reader.ReadLine();
+ if (result?.Contains("Success") != true)
{
- throw new ElementNotFoundException("Coordinates of element is invalid");
+ throw new AdbException(reader.ReadToEnd());
}
}
///
- 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)
+ public virtual void Uninstall(DeviceData device, string packageName, params string[] arguments)
{
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;
- }
+ StringBuilder requestBuilder = new StringBuilder().Append("exec:cmd package 'uninstall'");
- ///
- public IEnumerable FindElements(DeviceData device, string xpath = "hierarchy/node", TimeSpan timeout = default)
- {
- EnsureDevice(device);
- Stopwatch stopwatch = new();
- stopwatch.Start();
- do
+ if (arguments != null)
{
- XmlDocument? doc = null;
-
- try
- {
- doc = DumpScreen(device);
- }
- catch (XmlException)
- {
- // Ignore XmlException and try again
- }
-
- if (doc != null)
+ foreach (string argument in arguments)
{
- 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;
- }
+ _ = requestBuilder.AppendFormat(" {0}", argument);
}
-
- if (timeout == default) { break; }
}
- while (stopwatch.Elapsed < timeout);
- }
- ///
- public void SendKeyEvent(DeviceData device, string key)
- {
- EnsureDevice(device);
+ _ = requestBuilder.AppendFormat(" {0}", packageName);
using IAdbSocket socket = adbSocketFactory(EndPoint);
socket.SetDevice(device);
- socket.SendAdbRequest($"shell:input keyevent {key}");
+ socket.SendAdbRequest(requestBuilder.ToString());
_ = 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
+ string? result = reader.ReadLine();
+ if (result?.Contains("Success") != true)
{
- throw new InvalidKeyEventException("KeyEvent is invalid");
+ throw new AdbException(reader.ReadToEnd());
}
}
///
- 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)
+ public virtual IEnumerable GetFeatureSet(DeviceData device)
{
EnsureDevice(device);
using IAdbSocket socket = adbSocketFactory(EndPoint);
- socket.SetDevice(device);
-
- socket.SendAdbRequest($"shell:monkey -p {packageName} 1");
+ socket.SendAdbRequest($"host-serial:{device.Serial}:features");
_ = socket.ReadAdbResponse();
- }
-
- ///
- public void StopApp(DeviceData device, string packageName)
- {
- EnsureDevice(device);
-
- using IAdbSocket socket = adbSocketFactory(EndPoint);
- socket.SetDevice(device);
+ string features = socket.ReadString();
- socket.SendAdbRequest($"shell:am force-stop {packageName}");
- _ = socket.ReadAdbResponse();
+ IEnumerable featureList = features.Trim().Split('\n', ',');
+ return featureList;
}
///
@@ -1219,13 +995,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/AdbCommandLineClient.Async.cs b/AdvancedSharpAdbClient/AdbCommandLineClient.Async.cs
index 05906f5f..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
{
- ///
- /// 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;
}
-
- ///
- /// 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,8 +74,16 @@ 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
+
///
- /// 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 +103,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 +122,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/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/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/DeviceClient.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs
new file mode 100644
index 00000000..282632c4
--- /dev/null
+++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.Async.cs
@@ -0,0 +1,478 @@
+#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 virtual 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 virtual 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 virtual 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 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);
+
+ 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 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);
+
+ 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 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);
+
+ 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 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 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);
+
+ 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 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);
+
+ 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 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);
+
+ 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 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);
+
+ 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 virtual 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 virtual 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 virtual 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 virtual 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 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);
+
+ 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 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);
+
+ 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 virtual 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 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
new file mode 100644
index 00000000..d5963db5
--- /dev/null
+++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs
@@ -0,0 +1,425 @@
+//
+// 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 virtual 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 virtual 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 virtual 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 virtual 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 virtual 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 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);
+
+ 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 coordinates to second coordinates. Specify the speed in ms.
+ ///
+ /// The start .
+ /// The end .
+ /// The time spent in swiping.
+ 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);
+
+ 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 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);
+
+ 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 virtual 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 virtual 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 virtual 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 virtual 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 virtual 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 virtual 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 virtual 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 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 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 virtual 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..3516016d 100644
--- a/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs
+++ b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.Async.cs
@@ -9,13 +9,14 @@
using System.Linq;
using System.Text;
using System.Threading;
+using System.Xml;
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.
@@ -26,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.
@@ -38,7 +39,155 @@ public static Task ExecuteShellCommandAsync(this IAdbClient client, DeviceData d
client.ExecuteRemoteCommandAsync(command, device, receiver, AdbClient.Encoding, cancellationToken);
///
- /// Gets the file statistics of a given file.
+ /// Asynchronously 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// 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.
+ /// 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// 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.
+ /// 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);
+
+ ///
+ /// 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.
+ /// 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);
+
+ ///
+ /// 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.
+ /// 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// 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.
+ /// 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);
+ }
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// Asynchronously 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);
+
+ ///
+ /// 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.
@@ -52,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.
@@ -66,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.
@@ -84,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.
@@ -104,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.
@@ -119,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.
@@ -133,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.
@@ -147,7 +296,60 @@ public static async Task> GetEnvironmentVariablesAsyn
}
///
- /// Uninstalls a package from the device.
+ /// 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.
///
/// The connection to the adb server.
/// The device on which to uninstall the package.
@@ -156,12 +358,27 @@ 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);
}
///
- /// Requests the version information from the device.
+ /// 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.
///
/// The connection to the adb server.
/// The device on which to uninstall the package.
@@ -170,12 +387,12 @@ 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);
}
///
- /// 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/DeviceExtensions.cs b/AdvancedSharpAdbClient/DeviceCommands/DeviceExtensions.cs
index b3e0f4fb..a0dac92a 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,134 @@ 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.
+ ///
+ /// 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");
+
+ ///
+ /// 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.
///
@@ -142,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);
}
///
@@ -162,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/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 82%
rename from AdvancedSharpAdbClient/Models/Element.cs
rename to AdvancedSharpAdbClient/DeviceCommands/Models/Element.cs
index 454a89a5..532e4142 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)
@@ -252,14 +280,28 @@ 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 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.
+ /// Asynchronously send text to device. Doesn't support Russian.
///
/// The text to send.
/// A which can be used to cancel the asynchronous operation.
@@ -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.
+ /// 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.
@@ -280,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/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/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/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.Async.cs b/AdvancedSharpAdbClient/DeviceCommands/PackageManager.Async.cs
index 1f157221..3a9abb57 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,15 +39,18 @@ 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.
+ /// 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 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) =>
@@ -65,10 +68,11 @@ 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.
+ /// 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.
@@ -101,16 +105,19 @@ 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.
- /// 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.
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();
@@ -174,16 +181,19 @@ 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.
- /// 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.
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();
@@ -239,11 +249,12 @@ 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.
- /// 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.
@@ -287,11 +298,12 @@ 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.
- /// 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.
@@ -331,7 +343,7 @@ await Extensions.WhenAll(splitRemoteFilePaths.Select(async (splitRemoteFilePath)
}
///
- /// Uninstalls 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.
@@ -340,7 +352,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 +384,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 +399,20 @@ public virtual async Task GetVersionInfoAsync(string packageName, C
}
///
- /// Pushes a file to device
+ /// 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
+
+ ///