From 7a7cefe7b5ef3be57598675b55d24cbeb328489b Mon Sep 17 00:00:00 2001 From: shept Date: Mon, 15 Sep 2025 00:12:36 +0500 Subject: [PATCH 1/7] WIP: move logic to Core project --- .../MetaforceInstaller.Cli.csproj | 6 +- MetaforceInstaller.Cli/Program.cs | 251 ++++-------------- .../Utils/ArgumentParser.cs | 53 ++++ .../Intefaces/IAdbService.cs | 10 + .../MetaforceInstaller.Core.csproj | 19 ++ MetaforceInstaller.Core/Models/DeviceInfo.cs | 8 + .../Models/InstallationRequest.cs | 8 + .../Services/AdbService.cs | 106 ++++++++ .../adb/AdbWinApi.dll | Bin .../adb/AdbWinUsbApi.dll | Bin .../adb/adb.exe | Bin MetaforceInstaller.sln | 6 + 12 files changed, 267 insertions(+), 200 deletions(-) create mode 100644 MetaforceInstaller.Cli/Utils/ArgumentParser.cs create mode 100644 MetaforceInstaller.Core/Intefaces/IAdbService.cs create mode 100644 MetaforceInstaller.Core/MetaforceInstaller.Core.csproj create mode 100644 MetaforceInstaller.Core/Models/DeviceInfo.cs create mode 100644 MetaforceInstaller.Core/Models/InstallationRequest.cs create mode 100644 MetaforceInstaller.Core/Services/AdbService.cs rename {MetaforceInstaller.Cli => MetaforceInstaller.Core}/adb/AdbWinApi.dll (100%) rename {MetaforceInstaller.Cli => MetaforceInstaller.Core}/adb/AdbWinUsbApi.dll (100%) rename {MetaforceInstaller.Cli => MetaforceInstaller.Core}/adb/adb.exe (100%) diff --git a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj index 79e2376..0f2fdaa 100644 --- a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj +++ b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj @@ -10,11 +10,9 @@ - + - - - + diff --git a/MetaforceInstaller.Cli/Program.cs b/MetaforceInstaller.Cli/Program.cs index fa8c389..ec19c2a 100644 --- a/MetaforceInstaller.Cli/Program.cs +++ b/MetaforceInstaller.Cli/Program.cs @@ -1,57 +1,53 @@ -using System.Reflection; -using AdvancedSharpAdbClient; -using AdvancedSharpAdbClient.DeviceCommands; -using AdvancedSharpAdbClient.Models; +using MetaforceInstaller.Cli.Utils; +using MetaforceInstaller.Core.Services; namespace MetaforceInstaller.Cli; -class Program +static class Program { - // 1. Получить имя апк и зипки, если не предоставлены - забить дефолтными значениями - // 2. Распаковать в временную директорию adb (готово) - // 3. Установить апк - // 4. Получить имя пакета - // 5. Сформировать строку пути для контента - // 6. Копировать зип по сформированному пути - - static AdbClient adbClient; - static DeviceData deviceData; - static void Main(string[] args) { try { - var (apkPath, zipPath, outputPath) = ParseArguments(args); + var installationRequest = ArgumentParser.ParseArguments(args); - if (string.IsNullOrEmpty(apkPath) || string.IsNullOrEmpty(zipPath) || string.IsNullOrEmpty(outputPath)) + if (installationRequest is null || + string.IsNullOrEmpty(installationRequest.ApkPath) || + string.IsNullOrEmpty(installationRequest.ZipPath) || + string.IsNullOrEmpty(installationRequest.OutputPath)) { ShowUsage(); return; } - var adbPath = ExtractAdbFiles(); + var adbService = new AdbService(); - var server = new AdbServer(); - var result = server.StartServer(adbPath, restartServerIfNewer: false); - Console.WriteLine($"ADB сервер запущен: {result}"); + adbService.InstallApk(installationRequest.ApkPath); + adbService.CopyFile(installationRequest.ZipPath, installationRequest.OutputPath); - adbClient = new AdbClient(); - - var devices = adbClient.GetDevices(); - - if (!devices.Any()) - { - Console.WriteLine("Устройства не найдены. Подключите Android-устройство и включите отладку по USB."); - return; - } - - deviceData = devices.FirstOrDefault(); - Console.WriteLine($"Найдено устройство: {deviceData.Serial}"); - Console.WriteLine($"Состояние: {deviceData.State}"); - Console.WriteLine($"Имя устройства: {deviceData.Name} - {deviceData.Model}"); - - InstallApk(apkPath); - CopyFileToDevice(zipPath, outputPath); + // var adbPath = ExtractAdbFiles(); + // + // var server = new AdbServer(); + // var result = server.StartServer(adbPath, restartServerIfNewer: false); + // Console.WriteLine($"ADB сервер запущен: {result}"); + // + // adbClient = new AdbClient(); + // + // var devices = adbClient.GetDevices(); + // + // if (!devices.Any()) + // { + // Console.WriteLine("Устройства не найдены. Подключите Android-устройство и включите отладку по USB."); + // return; + // } + // + // deviceData = devices.FirstOrDefault(); + // Console.WriteLine($"Найдено устройство: {deviceData.Serial}"); + // Console.WriteLine($"Состояние: {deviceData.State}"); + // Console.WriteLine($"Имя устройства: {deviceData.Name} - {deviceData.Model}"); + // + // InstallApk(installationRequest.ApkPath); + // CopyFileToDevice(installationRequest.ZipPath, installationRequest.OutputPath); } catch (Exception ex) { @@ -78,168 +74,31 @@ class Program Console.WriteLine(" MetaforceInstaller.exe -a app.apk -c data.zip -o /sdcard/data.zip"); } - - private static (string? apkPath, string? zipPath, string? outputPath) ParseArguments(string[] args) + private static void DrawProgressBar(int progress, long receivedBytes, long totalBytes) { - string apkPath = null; - string zipPath = null; - string outputPath = null; + Console.SetCursorPosition(0, Console.CursorTop); - for (int i = 0; i < args.Length; i++) + var barLength = 40; + var filledLength = (int)(barLength * progress / 100.0); + + var bar = "[" + new string('█', filledLength) + new string('░', barLength - filledLength) + "]"; + var bytesText = $" {FormatBytes(receivedBytes)} / {FormatBytes(totalBytes)}"; + + Console.Write($"\r{bar} {progress}%{bytesText}"); + } + + private static string FormatBytes(long bytes) + { + string[] suffixes = ["B", "KB", "MB", "GB", "TB"]; + var counter = 0; + double number = bytes; + + while (Math.Round(number / 1024) >= 1) { - switch (args[i].ToLower()) - { - case "--apk": - case "-a": - if (i + 1 < args.Length) - { - apkPath = args[i + 1]; - i++; - } - - break; - - case "--content": - case "-c": - if (i + 1 < args.Length) - { - zipPath = args[i + 1]; - i++; - } - - break; - - case "--output": - case "-o": - if (i + 1 < args.Length) - { - outputPath = args[i + 1]; - i++; - } - - break; - - case "--help": - case "-h": - ShowUsage(); - Environment.Exit(0); - break; - } + number /= 1024; + counter++; } - return (apkPath, zipPath, outputPath); + return $"{number:N1} {suffixes[counter]}"; } - - private static void ExtractResource(string resourceName, string outputPath) - { - using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); - using var fileStream = File.Create(outputPath); - stream.CopyTo(fileStream); - } - - private static string ExtractAdbFiles() - { - var tempDir = Path.Combine(Path.GetTempPath(), "MetaforceInstaller", "adb"); - Directory.CreateDirectory(tempDir); - - var adbPath = Path.Combine(tempDir, "adb.exe"); - - if (!File.Exists(adbPath)) - { - ExtractResource("MetaforceInstaller.Cli.adb.adb.exe", adbPath); - ExtractResource("MetaforceInstaller.Cli.adb.AdbWinApi.dll", Path.Combine(tempDir, "AdbWinApi.dll")); - ExtractResource("MetaforceInstaller.Cli.adb.AdbWinUsbApi.dll", Path.Combine(tempDir, "AdbWinUsbApi.dll")); - } - - return adbPath; - } - - private static void InstallApk(string apkPath) - { - try - { - if (!File.Exists(apkPath)) - { - Console.WriteLine($"APK файл не найден: {apkPath}"); - return; - } - - Console.WriteLine($"Установка APK: {apkPath}"); - - var packageManager = new PackageManager(adbClient, deviceData); - packageManager.InstallPackage(apkPath, new Action(o => { })); - - Console.WriteLine("APK успешно установлен!"); - } - catch (Exception ex) - { - Console.WriteLine($"Ошибка установки APK: {ex.Message}"); - } - } - -private static void CopyFileToDevice(string localPath, string remotePath) -{ - try - { - if (!File.Exists(localPath)) - { - Console.WriteLine($"Локальный файл не найден: {localPath}"); - return; - } - - Console.WriteLine($"Копирование файла {localPath} в {remotePath}"); - - var lastProgress = -1; - - using var fileStream = File.OpenRead(localPath); - var syncService = new SyncService(adbClient, deviceData); - - syncService.Push(fileStream, remotePath, UnixFileStatus.DefaultFileMode, DateTime.Now, - new Action(progress => - { - var currentProgress = progress.ProgressPercentage; - - if (currentProgress != lastProgress) - { - lastProgress = (int)currentProgress; - DrawProgressBar(lastProgress, progress.ReceivedBytesSize, progress.TotalBytesToReceive); - } - })); - - Console.WriteLine(); - Console.WriteLine("Файл успешно скопирован!"); - } - catch (Exception ex) - { - Console.WriteLine($"Ошибка копирования файла: {ex.Message}"); - } -} - -private static void DrawProgressBar(int progress, long receivedBytes, long totalBytes) -{ - Console.SetCursorPosition(0, Console.CursorTop); - - var barLength = 40; - var filledLength = (int)(barLength * progress / 100.0); - - var bar = "[" + new string('█', filledLength) + new string('░', barLength - filledLength) + "]"; - var bytesText = $" {FormatBytes(receivedBytes)} / {FormatBytes(totalBytes)}"; - - Console.Write($"\r{bar} {progress}%{bytesText}"); -} - -private static string FormatBytes(long bytes) -{ - string[] suffixes = { "B", "KB", "MB", "GB", "TB" }; - var counter = 0; - double number = bytes; - - while (Math.Round(number / 1024) >= 1) - { - number /= 1024; - counter++; - } - - return $"{number:N1} {suffixes[counter]}"; -} } \ No newline at end of file diff --git a/MetaforceInstaller.Cli/Utils/ArgumentParser.cs b/MetaforceInstaller.Cli/Utils/ArgumentParser.cs new file mode 100644 index 0000000..ffdb561 --- /dev/null +++ b/MetaforceInstaller.Cli/Utils/ArgumentParser.cs @@ -0,0 +1,53 @@ +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Cli.Utils; + +public static class ArgumentParser +{ + public static InstallationRequest? ParseArguments(string[] args) + { + var result = new InstallationRequest(); + + for (var i = 0; i < args.Length; i++) + { + switch (args[i].ToLower()) + { + case "--apk": + case "-a": + if (i + 1 < args.Length) + { + result.ApkPath = args[i + 1]; + i++; + } + + break; + + case "--content": + case "-c": + if (i + 1 < args.Length) + { + result.ZipPath = args[i + 1]; + i++; + } + + break; + + case "--output": + case "-o": + if (i + 1 < args.Length) + { + result.OutputPath = args[i + 1]; + i++; + } + + break; + + case "--help": + case "-h": + return null; + } + } + + return result; + } +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Intefaces/IAdbService.cs b/MetaforceInstaller.Core/Intefaces/IAdbService.cs new file mode 100644 index 0000000..c520ed6 --- /dev/null +++ b/MetaforceInstaller.Core/Intefaces/IAdbService.cs @@ -0,0 +1,10 @@ +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Core.Intefaces; + +public interface IAdbService +{ + public void InstallApk(string apkPath); + public void CopyFile(string localPath, string remotePath); + public DeviceInfo GetDeviceInfo(); +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj b/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj new file mode 100644 index 0000000..9b6a6fd --- /dev/null +++ b/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/MetaforceInstaller.Core/Models/DeviceInfo.cs b/MetaforceInstaller.Core/Models/DeviceInfo.cs new file mode 100644 index 0000000..802cd3c --- /dev/null +++ b/MetaforceInstaller.Core/Models/DeviceInfo.cs @@ -0,0 +1,8 @@ +namespace MetaforceInstaller.Core.Models; + +public record DeviceInfo( + string SerialNumber, + string State, + string Model, + string Name +); \ No newline at end of file diff --git a/MetaforceInstaller.Core/Models/InstallationRequest.cs b/MetaforceInstaller.Core/Models/InstallationRequest.cs new file mode 100644 index 0000000..c64b271 --- /dev/null +++ b/MetaforceInstaller.Core/Models/InstallationRequest.cs @@ -0,0 +1,8 @@ +namespace MetaforceInstaller.Core.Models; + +public class InstallationRequest +{ + public string ApkPath { get; set; } + public string ZipPath { get; set; } + public string OutputPath { get; set; } +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Services/AdbService.cs b/MetaforceInstaller.Core/Services/AdbService.cs new file mode 100644 index 0000000..b646012 --- /dev/null +++ b/MetaforceInstaller.Core/Services/AdbService.cs @@ -0,0 +1,106 @@ +using System.Reflection; +using AdvancedSharpAdbClient; +using AdvancedSharpAdbClient.DeviceCommands; +using AdvancedSharpAdbClient.Logs; +using AdvancedSharpAdbClient.Models; +using MetaforceInstaller.Core.Intefaces; +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Core.Services; + +public class AdbService : IAdbService +{ + private ILogger _logger; + private readonly AdbClient _adbClient; + private DeviceData _deviceData; + + public AdbService() + { + var adbPath = GetAdbPath(); + var server = new AdbServer(); + var serverStatus = server.StartServer(adbPath, restartServerIfNewer: false); + _adbClient = new AdbClient(); + var devices = _adbClient.GetDevices(); + _deviceData = devices.FirstOrDefault(); + } + + private void ExtractResource(string resourceName, string outputPath) + { + _logger.LogInformation($"Extracting resource: {resourceName} to {outputPath}"); + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + using var fileStream = File.Create(outputPath); + stream.CopyTo(fileStream); + _logger.LogInformation($"Resource extracted: {resourceName} to {outputPath}"); + } + + private string GetAdbPath() + { + var tempDir = Path.Combine(Path.GetTempPath(), "MetaforceInstaller", "adb"); + Directory.CreateDirectory(tempDir); + + var adbPath = Path.Combine(tempDir, "adb.exe"); + + if (File.Exists(adbPath)) return adbPath; + ExtractResource("MetaforceInstaller.Cli.adb.adb.exe", adbPath); + ExtractResource("MetaforceInstaller.Cli.adb.AdbWinApi.dll", Path.Combine(tempDir, "AdbWinApi.dll")); + ExtractResource("MetaforceInstaller.Cli.adb.AdbWinUsbApi.dll", Path.Combine(tempDir, "AdbWinUsbApi.dll")); + + return adbPath; + } + + public void InstallApk(string apkPath) + { + try + { + if (!File.Exists(apkPath)) + { + _logger.LogCritical("Error: Could not find APK file."); + return; + } + + _logger.LogInformation($"Installing APK: {apkPath}"); + + var packageManager = new PackageManager(_adbClient, _deviceData); + packageManager.InstallPackage(apkPath, o => { }); + + _logger.LogInformation("APK successfully installed!"); + } + catch (Exception ex) + { + _logger.LogCritical($"Error: {ex.Message}"); + throw; + } + } + + public void CopyFile(string localPath, string remotePath) + { + try + { + if (!File.Exists(localPath)) + { + _logger.LogCritical($"Error: Could not find file: {localPath}"); + return; + } + + _logger.LogInformation($"Copying file: {localPath} to {remotePath}"); + + using var fileStream = File.OpenRead(localPath); + var syncService = new SyncService(_adbClient, _deviceData); + + syncService.Push(fileStream, remotePath, UnixFileStatus.DefaultFileMode, DateTime.Now, + new Action(progress => { })); + + _logger.LogInformation("File successfully copied!"); + } + catch (Exception ex) + { + _logger.LogCritical($"Error: {ex.Message}"); + throw; + } + } + + public DeviceInfo GetDeviceInfo() + { + return new DeviceInfo(_deviceData.Serial, _deviceData.State.ToString(), _deviceData.Model, _deviceData.Name); + } +} \ No newline at end of file diff --git a/MetaforceInstaller.Cli/adb/AdbWinApi.dll b/MetaforceInstaller.Core/adb/AdbWinApi.dll similarity index 100% rename from MetaforceInstaller.Cli/adb/AdbWinApi.dll rename to MetaforceInstaller.Core/adb/AdbWinApi.dll diff --git a/MetaforceInstaller.Cli/adb/AdbWinUsbApi.dll b/MetaforceInstaller.Core/adb/AdbWinUsbApi.dll similarity index 100% rename from MetaforceInstaller.Cli/adb/AdbWinUsbApi.dll rename to MetaforceInstaller.Core/adb/AdbWinUsbApi.dll diff --git a/MetaforceInstaller.Cli/adb/adb.exe b/MetaforceInstaller.Core/adb/adb.exe similarity index 100% rename from MetaforceInstaller.Cli/adb/adb.exe rename to MetaforceInstaller.Core/adb/adb.exe diff --git a/MetaforceInstaller.sln b/MetaforceInstaller.sln index 44d9b89..a2406c6 100644 --- a/MetaforceInstaller.sln +++ b/MetaforceInstaller.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetaforceInstaller.Cli", "MetaforceInstaller.Cli\MetaforceInstaller.Cli.csproj", "{4928C2AC-6B63-4B18-9472-705807A15893}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetaforceInstaller.Core", "MetaforceInstaller.Core\MetaforceInstaller.Core.csproj", "{83961B6E-21E7-4B7A-B4FA-6128A44BCE1F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {4928C2AC-6B63-4B18-9472-705807A15893}.Debug|Any CPU.Build.0 = Debug|Any CPU {4928C2AC-6B63-4B18-9472-705807A15893}.Release|Any CPU.ActiveCfg = Release|Any CPU {4928C2AC-6B63-4B18-9472-705807A15893}.Release|Any CPU.Build.0 = Release|Any CPU + {83961B6E-21E7-4B7A-B4FA-6128A44BCE1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83961B6E-21E7-4B7A-B4FA-6128A44BCE1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83961B6E-21E7-4B7A-B4FA-6128A44BCE1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83961B6E-21E7-4B7A-B4FA-6128A44BCE1F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal -- 2.39.5 From 243224a720069e939533b8894187d39ac6a315a1 Mon Sep 17 00:00:00 2001 From: shept Date: Mon, 15 Sep 2025 00:38:30 +0500 Subject: [PATCH 2/7] now with animations lol --- .../MetaforceInstaller.Cli.csproj | 4 + MetaforceInstaller.Cli/Program.cs | 79 +++++++----- .../Intefaces/IAdbService.cs | 14 ++- .../MetaforceInstaller.Core.csproj | 6 + .../Models/ProgressInfo.cs | 19 +++ .../Services/AdbService.cs | 113 ++++++++++++++++-- 6 files changed, 192 insertions(+), 43 deletions(-) create mode 100644 MetaforceInstaller.Core/Models/ProgressInfo.cs diff --git a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj index 0f2fdaa..dee32c2 100644 --- a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj +++ b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj @@ -7,6 +7,10 @@ enable + + + + diff --git a/MetaforceInstaller.Cli/Program.cs b/MetaforceInstaller.Cli/Program.cs index ec19c2a..88b7a88 100644 --- a/MetaforceInstaller.Cli/Program.cs +++ b/MetaforceInstaller.Cli/Program.cs @@ -5,7 +5,7 @@ namespace MetaforceInstaller.Cli; static class Program { - static void Main(string[] args) + static async Task Main(string[] args) { try { @@ -22,32 +22,29 @@ static class Program var adbService = new AdbService(); - adbService.InstallApk(installationRequest.ApkPath); - adbService.CopyFile(installationRequest.ZipPath, installationRequest.OutputPath); + // Подписка на события прогресса + adbService.ProgressChanged += OnProgressChanged; + adbService.StatusChanged += OnStatusChanged; - // var adbPath = ExtractAdbFiles(); - // - // var server = new AdbServer(); - // var result = server.StartServer(adbPath, restartServerIfNewer: false); - // Console.WriteLine($"ADB сервер запущен: {result}"); - // - // adbClient = new AdbClient(); - // - // var devices = adbClient.GetDevices(); - // - // if (!devices.Any()) - // { - // Console.WriteLine("Устройства не найдены. Подключите Android-устройство и включите отладку по USB."); - // return; - // } - // - // deviceData = devices.FirstOrDefault(); - // Console.WriteLine($"Найдено устройство: {deviceData.Serial}"); - // Console.WriteLine($"Состояние: {deviceData.State}"); - // Console.WriteLine($"Имя устройства: {deviceData.Name} - {deviceData.Model}"); - // - // InstallApk(installationRequest.ApkPath); - // CopyFileToDevice(installationRequest.ZipPath, installationRequest.OutputPath); + // Получение информации об устройстве + var deviceInfo = adbService.GetDeviceInfo(); + Console.WriteLine($"Найдено устройство: {deviceInfo.SerialNumber}"); + Console.WriteLine($"Состояние: {deviceInfo.State}"); + Console.WriteLine($"Модель: {deviceInfo.Model} - {deviceInfo.Name}"); + Console.WriteLine(); + + // Создание объекта для отслеживания прогресса + var progress = new Progress(OnProgressReport); + + // Установка APK + await adbService.InstallApkAsync(installationRequest.ApkPath, progress); + Console.WriteLine(); + + // Копирование файла + await adbService.CopyFileAsync(installationRequest.ZipPath, installationRequest.OutputPath, progress); + Console.WriteLine(); + + Console.WriteLine("Операция завершена успешно!"); } catch (Exception ex) { @@ -55,6 +52,29 @@ static class Program } } + private static void OnProgressChanged(object? sender, MetaforceInstaller.Core.Models.ProgressInfo e) + { + DrawProgressBar(e.PercentageComplete, e.BytesTransferred, e.TotalBytes); + } + + private static void OnStatusChanged(object? sender, string e) + { + Console.WriteLine(e); + } + + private static void OnProgressReport(MetaforceInstaller.Core.Models.ProgressInfo progressInfo) + { + if (progressInfo.TotalBytes > 0) + { + DrawProgressBar(progressInfo.PercentageComplete, progressInfo.BytesTransferred, progressInfo.TotalBytes); + } + else + { + // Для случаев без информации о байтах (например, установка APK) + DrawProgressBar(progressInfo.PercentageComplete, 0, 100); + } + } + static void ShowUsage() { Console.WriteLine("Использование:"); @@ -82,7 +102,12 @@ static class Program var filledLength = (int)(barLength * progress / 100.0); var bar = "[" + new string('█', filledLength) + new string('░', barLength - filledLength) + "]"; - var bytesText = $" {FormatBytes(receivedBytes)} / {FormatBytes(totalBytes)}"; + + string bytesText = ""; + if (totalBytes > 0) + { + bytesText = $" {FormatBytes(receivedBytes)} / {FormatBytes(totalBytes)}"; + } Console.Write($"\r{bar} {progress}%{bytesText}"); } diff --git a/MetaforceInstaller.Core/Intefaces/IAdbService.cs b/MetaforceInstaller.Core/Intefaces/IAdbService.cs index c520ed6..444c237 100644 --- a/MetaforceInstaller.Core/Intefaces/IAdbService.cs +++ b/MetaforceInstaller.Core/Intefaces/IAdbService.cs @@ -1,10 +1,18 @@ using MetaforceInstaller.Core.Models; +using MetaforceInstaller.Core.Models; namespace MetaforceInstaller.Core.Intefaces; public interface IAdbService { - public void InstallApk(string apkPath); - public void CopyFile(string localPath, string remotePath); - public DeviceInfo GetDeviceInfo(); + event EventHandler? ProgressChanged; + event EventHandler? StatusChanged; + + Task InstallApkAsync(string apkPath, IProgress? progress = null, CancellationToken cancellationToken = default); + Task CopyFileAsync(string localPath, string remotePath, IProgress? progress = null, CancellationToken cancellationToken = default); + DeviceInfo GetDeviceInfo(); + + // Синхронные версии для обратной совместимости + void InstallApk(string apkPath); + void CopyFile(string localPath, string remotePath); } \ No newline at end of file diff --git a/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj b/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj index 9b6a6fd..7542437 100644 --- a/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj +++ b/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj @@ -12,8 +12,14 @@ + + + + + + diff --git a/MetaforceInstaller.Core/Models/ProgressInfo.cs b/MetaforceInstaller.Core/Models/ProgressInfo.cs new file mode 100644 index 0000000..cc09a74 --- /dev/null +++ b/MetaforceInstaller.Core/Models/ProgressInfo.cs @@ -0,0 +1,19 @@ +namespace MetaforceInstaller.Core.Models; + +public class ProgressInfo +{ + public int PercentageComplete { get; set; } + public long BytesTransferred { get; set; } + public long TotalBytes { get; set; } + public string? Message { get; set; } + public string? CurrentFile { get; set; } + public ProgressType Type { get; set; } +} + +public enum ProgressType +{ + Installation, + FileCopy, + Extraction, + General +} diff --git a/MetaforceInstaller.Core/Services/AdbService.cs b/MetaforceInstaller.Core/Services/AdbService.cs index b646012..8a55bd0 100644 --- a/MetaforceInstaller.Core/Services/AdbService.cs +++ b/MetaforceInstaller.Core/Services/AdbService.cs @@ -1,33 +1,42 @@ using System.Reflection; using AdvancedSharpAdbClient; using AdvancedSharpAdbClient.DeviceCommands; -using AdvancedSharpAdbClient.Logs; using AdvancedSharpAdbClient.Models; using MetaforceInstaller.Core.Intefaces; using MetaforceInstaller.Core.Models; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace MetaforceInstaller.Core.Services; public class AdbService : IAdbService { - private ILogger _logger; + private readonly ILogger _logger; private readonly AdbClient _adbClient; private DeviceData _deviceData; - public AdbService() + public event EventHandler? ProgressChanged; + public event EventHandler? StatusChanged; + + public AdbService(ILogger? logger = null) { + _logger = logger ?? new NullLogger(); var adbPath = GetAdbPath(); var server = new AdbServer(); var serverStatus = server.StartServer(adbPath, restartServerIfNewer: false); _adbClient = new AdbClient(); var devices = _adbClient.GetDevices(); + foreach (var device in devices) + { + Console.WriteLine(device.Model); + } _deviceData = devices.FirstOrDefault(); } private void ExtractResource(string resourceName, string outputPath) { _logger.LogInformation($"Extracting resource: {resourceName} to {outputPath}"); - using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + using var stream = Assembly.GetAssembly(typeof(AdbService)).GetManifestResourceStream(resourceName); using var fileStream = File.Create(outputPath); stream.CopyTo(fileStream); _logger.LogInformation($"Resource extracted: {resourceName} to {outputPath}"); @@ -41,14 +50,30 @@ public class AdbService : IAdbService var adbPath = Path.Combine(tempDir, "adb.exe"); if (File.Exists(adbPath)) return adbPath; - ExtractResource("MetaforceInstaller.Cli.adb.adb.exe", adbPath); - ExtractResource("MetaforceInstaller.Cli.adb.AdbWinApi.dll", Path.Combine(tempDir, "AdbWinApi.dll")); - ExtractResource("MetaforceInstaller.Cli.adb.AdbWinUsbApi.dll", Path.Combine(tempDir, "AdbWinUsbApi.dll")); + ExtractResource("MetaforceInstaller.Core.adb.adb.exe", adbPath); + ExtractResource("MetaforceInstaller.Core.adb.AdbWinApi.dll", Path.Combine(tempDir, "AdbWinApi.dll")); + ExtractResource("MetaforceInstaller.Core.adb.AdbWinUsbApi.dll", Path.Combine(tempDir, "AdbWinUsbApi.dll")); return adbPath; } + private void OnProgressChanged(ProgressInfo progressInfo) + { + ProgressChanged?.Invoke(this, progressInfo); + } + + private void OnStatusChanged(string status) + { + StatusChanged?.Invoke(this, status); + } + public void InstallApk(string apkPath) + { + InstallApkAsync(apkPath).Wait(); + } + + public async Task InstallApkAsync(string apkPath, IProgress? progress = null, + CancellationToken cancellationToken = default) { try { @@ -58,11 +83,37 @@ public class AdbService : IAdbService return; } + OnStatusChanged("Начинаем установку APK..."); _logger.LogInformation($"Installing APK: {apkPath}"); - var packageManager = new PackageManager(_adbClient, _deviceData); - packageManager.InstallPackage(apkPath, o => { }); + progress?.Report(new ProgressInfo + { + PercentageComplete = 0, + Message = "Подготовка к установке APK...", + Type = ProgressType.Installation, + CurrentFile = Path.GetFileName(apkPath) + }); + var packageManager = new PackageManager(_adbClient, _deviceData); + + await Task.Run(() => + { + packageManager.InstallPackage(apkPath, installProgress => + { + var progressInfo = new ProgressInfo + { + PercentageComplete = (int)installProgress.UploadProgress, + Message = $"Установка APK: {installProgress.UploadProgress:F1}%", + Type = ProgressType.Installation, + CurrentFile = Path.GetFileName(apkPath) + }; + + progress?.Report(progressInfo); + OnProgressChanged(progressInfo); + }); + }, cancellationToken); + + OnStatusChanged("APK успешно установлен!"); _logger.LogInformation("APK successfully installed!"); } catch (Exception ex) @@ -73,6 +124,12 @@ public class AdbService : IAdbService } public void CopyFile(string localPath, string remotePath) + { + CopyFileAsync(localPath, remotePath).Wait(); + } + + public async Task CopyFileAsync(string localPath, string remotePath, IProgress? progress = null, + CancellationToken cancellationToken = default) { try { @@ -82,14 +139,44 @@ public class AdbService : IAdbService return; } + OnStatusChanged("Начинаем копирование файла..."); _logger.LogInformation($"Copying file: {localPath} to {remotePath}"); - using var fileStream = File.OpenRead(localPath); - var syncService = new SyncService(_adbClient, _deviceData); + var fileInfo = new FileInfo(localPath); - syncService.Push(fileStream, remotePath, UnixFileStatus.DefaultFileMode, DateTime.Now, - new Action(progress => { })); + progress?.Report(new ProgressInfo + { + PercentageComplete = 0, + Message = "Подготовка к копированию файла...", + Type = ProgressType.FileCopy, + CurrentFile = Path.GetFileName(localPath), + TotalBytes = fileInfo.Length + }); + await Task.Run(() => + { + using var fileStream = File.OpenRead(localPath); + var syncService = new SyncService(_adbClient, _deviceData); + + syncService.Push(fileStream, remotePath, UnixFileStatus.DefaultFileMode, DateTime.Now, + copyProgress => + { + var progressInfo = new ProgressInfo + { + PercentageComplete = (int)copyProgress.ProgressPercentage, + BytesTransferred = copyProgress.ReceivedBytesSize, + TotalBytes = copyProgress.TotalBytesToReceive, + Message = $"Копирование: {copyProgress.ProgressPercentage:F1}%", + Type = ProgressType.FileCopy, + CurrentFile = Path.GetFileName(localPath) + }; + + progress?.Report(progressInfo); + OnProgressChanged(progressInfo); + }); + }, cancellationToken); + + OnStatusChanged("Файл успешно скопирован!"); _logger.LogInformation("File successfully copied!"); } catch (Exception ex) -- 2.39.5 From 4d5371be5ea8d76cd330563cb2659d913e7edf6d Mon Sep 17 00:00:00 2001 From: shept Date: Mon, 15 Sep 2025 02:08:19 +0500 Subject: [PATCH 3/7] dummy ui --- MetaforceInstaller.UI/App.axaml | 10 ++ MetaforceInstaller.UI/App.axaml.cs | 23 +++ MetaforceInstaller.UI/MainWindow.axaml | 49 +++++++ MetaforceInstaller.UI/MainWindow.axaml.cs | 131 ++++++++++++++++++ .../MetaforceInstaller.UI.csproj | 22 +++ MetaforceInstaller.UI/Program.cs | 21 +++ MetaforceInstaller.UI/app.manifest | 18 +++ MetaforceInstaller.sln | 6 + 8 files changed, 280 insertions(+) create mode 100644 MetaforceInstaller.UI/App.axaml create mode 100644 MetaforceInstaller.UI/App.axaml.cs create mode 100644 MetaforceInstaller.UI/MainWindow.axaml create mode 100644 MetaforceInstaller.UI/MainWindow.axaml.cs create mode 100644 MetaforceInstaller.UI/MetaforceInstaller.UI.csproj create mode 100644 MetaforceInstaller.UI/Program.cs create mode 100644 MetaforceInstaller.UI/app.manifest diff --git a/MetaforceInstaller.UI/App.axaml b/MetaforceInstaller.UI/App.axaml new file mode 100644 index 0000000..d947dd9 --- /dev/null +++ b/MetaforceInstaller.UI/App.axaml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/MetaforceInstaller.UI/App.axaml.cs b/MetaforceInstaller.UI/App.axaml.cs new file mode 100644 index 0000000..16212ac --- /dev/null +++ b/MetaforceInstaller.UI/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace MetaforceInstaller.UI; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/MetaforceInstaller.UI/MainWindow.axaml b/MetaforceInstaller.UI/MainWindow.axaml new file mode 100644 index 0000000..c1e1044 --- /dev/null +++ b/MetaforceInstaller.UI/MainWindow.axaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + +