diff --git a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj index dee32c2..79e2376 100644 --- a/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj +++ b/MetaforceInstaller.Cli/MetaforceInstaller.Cli.csproj @@ -7,16 +7,14 @@ enable - - - - - + - + + + diff --git a/MetaforceInstaller.Cli/Program.cs b/MetaforceInstaller.Cli/Program.cs index 88b7a88..fa8c389 100644 --- a/MetaforceInstaller.Cli/Program.cs +++ b/MetaforceInstaller.Cli/Program.cs @@ -1,50 +1,57 @@ -using MetaforceInstaller.Cli.Utils; -using MetaforceInstaller.Core.Services; +using System.Reflection; +using AdvancedSharpAdbClient; +using AdvancedSharpAdbClient.DeviceCommands; +using AdvancedSharpAdbClient.Models; namespace MetaforceInstaller.Cli; -static class Program +class Program { - static async Task Main(string[] args) + // 1. Получить имя апк и зипки, если не предоставлены - забить дефолтными значениями + // 2. Распаковать в временную директорию adb (готово) + // 3. Установить апк + // 4. Получить имя пакета + // 5. Сформировать строку пути для контента + // 6. Копировать зип по сформированному пути + + static AdbClient adbClient; + static DeviceData deviceData; + + static void Main(string[] args) { try { - var installationRequest = ArgumentParser.ParseArguments(args); + var (apkPath, zipPath, outputPath) = ParseArguments(args); - if (installationRequest is null || - string.IsNullOrEmpty(installationRequest.ApkPath) || - string.IsNullOrEmpty(installationRequest.ZipPath) || - string.IsNullOrEmpty(installationRequest.OutputPath)) + if (string.IsNullOrEmpty(apkPath) || string.IsNullOrEmpty(zipPath) || string.IsNullOrEmpty(outputPath)) { ShowUsage(); return; } - var adbService = new AdbService(); + var adbPath = ExtractAdbFiles(); - // Подписка на события прогресса - adbService.ProgressChanged += OnProgressChanged; - adbService.StatusChanged += OnStatusChanged; + var server = new AdbServer(); + var result = server.StartServer(adbPath, restartServerIfNewer: false); + Console.WriteLine($"ADB сервер запущен: {result}"); - // Получение информации об устройстве - var deviceInfo = adbService.GetDeviceInfo(); - Console.WriteLine($"Найдено устройство: {deviceInfo.SerialNumber}"); - Console.WriteLine($"Состояние: {deviceInfo.State}"); - Console.WriteLine($"Модель: {deviceInfo.Model} - {deviceInfo.Name}"); - Console.WriteLine(); + adbClient = new AdbClient(); - // Создание объекта для отслеживания прогресса - var progress = new Progress(OnProgressReport); + var devices = adbClient.GetDevices(); - // Установка APK - await adbService.InstallApkAsync(installationRequest.ApkPath, progress); - Console.WriteLine(); + if (!devices.Any()) + { + Console.WriteLine("Устройства не найдены. Подключите Android-устройство и включите отладку по USB."); + return; + } - // Копирование файла - await adbService.CopyFileAsync(installationRequest.ZipPath, installationRequest.OutputPath, progress); - Console.WriteLine(); + deviceData = devices.FirstOrDefault(); + Console.WriteLine($"Найдено устройство: {deviceData.Serial}"); + Console.WriteLine($"Состояние: {deviceData.State}"); + Console.WriteLine($"Имя устройства: {deviceData.Name} - {deviceData.Model}"); - Console.WriteLine("Операция завершена успешно!"); + InstallApk(apkPath); + CopyFileToDevice(zipPath, outputPath); } catch (Exception ex) { @@ -52,29 +59,6 @@ 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("Использование:"); @@ -94,36 +78,168 @@ static class Program Console.WriteLine(" MetaforceInstaller.exe -a app.apk -c data.zip -o /sdcard/data.zip"); } - private static void DrawProgressBar(int progress, long receivedBytes, long totalBytes) + + private static (string? apkPath, string? zipPath, string? outputPath) ParseArguments(string[] args) { - Console.SetCursorPosition(0, Console.CursorTop); + string apkPath = null; + string zipPath = null; + string outputPath = null; - var barLength = 40; - var filledLength = (int)(barLength * progress / 100.0); - - var bar = "[" + new string('█', filledLength) + new string('░', barLength - filledLength) + "]"; - - string bytesText = ""; - if (totalBytes > 0) + for (int i = 0; i < args.Length; i++) { - bytesText = $" {FormatBytes(receivedBytes)} / {FormatBytes(totalBytes)}"; + 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; + } } - Console.Write($"\r{bar} {progress}%{bytesText}"); + return (apkPath, zipPath, outputPath); } - private static string FormatBytes(long bytes) + private static void ExtractResource(string resourceName, string outputPath) { - string[] suffixes = ["B", "KB", "MB", "GB", "TB"]; - var counter = 0; - double number = bytes; + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + using var fileStream = File.Create(outputPath); + stream.CopyTo(fileStream); + } - while (Math.Round(number / 1024) >= 1) + 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)) { - number /= 1024; - counter++; + 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 $"{number:N1} {suffixes[counter]}"; + 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 deleted file mode 100644 index ffdb561..0000000 --- a/MetaforceInstaller.Cli/Utils/ArgumentParser.cs +++ /dev/null @@ -1,53 +0,0 @@ -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/adb/AdbWinApi.dll b/MetaforceInstaller.Cli/adb/AdbWinApi.dll similarity index 100% rename from MetaforceInstaller.Core/adb/AdbWinApi.dll rename to MetaforceInstaller.Cli/adb/AdbWinApi.dll diff --git a/MetaforceInstaller.Core/adb/AdbWinUsbApi.dll b/MetaforceInstaller.Cli/adb/AdbWinUsbApi.dll similarity index 100% rename from MetaforceInstaller.Core/adb/AdbWinUsbApi.dll rename to MetaforceInstaller.Cli/adb/AdbWinUsbApi.dll diff --git a/MetaforceInstaller.Core/adb/adb.exe b/MetaforceInstaller.Cli/adb/adb.exe similarity index 100% rename from MetaforceInstaller.Core/adb/adb.exe rename to MetaforceInstaller.Cli/adb/adb.exe diff --git a/MetaforceInstaller.Core/Intefaces/IAdbService.cs b/MetaforceInstaller.Core/Intefaces/IAdbService.cs deleted file mode 100644 index 444c237..0000000 --- a/MetaforceInstaller.Core/Intefaces/IAdbService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MetaforceInstaller.Core.Models; -using MetaforceInstaller.Core.Models; - -namespace MetaforceInstaller.Core.Intefaces; - -public interface IAdbService -{ - 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 deleted file mode 100644 index 7542437..0000000 --- a/MetaforceInstaller.Core/MetaforceInstaller.Core.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - - - - - diff --git a/MetaforceInstaller.Core/Models/DeviceInfo.cs b/MetaforceInstaller.Core/Models/DeviceInfo.cs deleted file mode 100644 index 802cd3c..0000000 --- a/MetaforceInstaller.Core/Models/DeviceInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index c64b271..0000000 --- a/MetaforceInstaller.Core/Models/InstallationRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -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/Models/ProgressInfo.cs b/MetaforceInstaller.Core/Models/ProgressInfo.cs deleted file mode 100644 index cc09a74..0000000 --- a/MetaforceInstaller.Core/Models/ProgressInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 8a55bd0..0000000 --- a/MetaforceInstaller.Core/Services/AdbService.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Reflection; -using AdvancedSharpAdbClient; -using AdvancedSharpAdbClient.DeviceCommands; -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 readonly ILogger _logger; - private readonly AdbClient _adbClient; - private DeviceData _deviceData; - - 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.GetAssembly(typeof(AdbService)).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.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 - { - if (!File.Exists(apkPath)) - { - _logger.LogCritical("Error: Could not find APK file."); - return; - } - - OnStatusChanged("Начинаем установку APK..."); - _logger.LogInformation($"Installing APK: {apkPath}"); - - 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) - { - _logger.LogCritical($"Error: {ex.Message}"); - throw; - } - } - - 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 - { - if (!File.Exists(localPath)) - { - _logger.LogCritical($"Error: Could not find file: {localPath}"); - return; - } - - OnStatusChanged("Начинаем копирование файла..."); - _logger.LogInformation($"Copying file: {localPath} to {remotePath}"); - - var fileInfo = new FileInfo(localPath); - - 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) - { - _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.UI/App.axaml b/MetaforceInstaller.UI/App.axaml deleted file mode 100644 index d947dd9..0000000 --- a/MetaforceInstaller.UI/App.axaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/MetaforceInstaller.UI/App.axaml.cs b/MetaforceInstaller.UI/App.axaml.cs deleted file mode 100644 index 16212ac..0000000 --- a/MetaforceInstaller.UI/App.axaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index c1e1044..0000000 --- a/MetaforceInstaller.UI/MainWindow.axaml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - -