using System.Reflection; using AdvancedSharpAdbClient; using AdvancedSharpAdbClient.DeviceCommands; using AdvancedSharpAdbClient.Models; using AdvancedSharpAdbClient.Receivers; 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; private DeviceMonitor? _deviceMonitor; public event EventHandler? ProgressChanged; public event EventHandler? StatusChanged; public EventHandler? DeviceConnected; public EventHandler? DeviceDisconnected; public EventHandler? DeviceChanged; public bool IsDeviceConnected => _deviceData != null && _deviceData.State == DeviceState.Online; 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(); RefreshDeviceData(); StartDeviceMonitoring(); } private void StartDeviceMonitoring() { try { _deviceMonitor = new DeviceMonitor(new AdbSocket(_adbClient.EndPoint)); _deviceMonitor.DeviceConnected += OnDeviceConnected; _deviceMonitor.DeviceDisconnected += OnDeviceDisconnected; _deviceMonitor.DeviceChanged += OnDeviceChanged; _deviceMonitor.Start(); _logger.LogInformation("Device monitoring started"); } catch (Exception ex) { _logger.LogError($"Failed to start device monitoring: {ex.Message}"); } } private void OnDeviceConnected(object? sender, DeviceDataEventArgs e) { _logger.LogInformation($"Device conn: {e.Device.Serial}"); RefreshDeviceData(); DeviceConnected?.Invoke(this, e); } private void OnDeviceDisconnected(object? sender, DeviceDataEventArgs e) { _logger.LogInformation($"Device disconnected: {e.Device.Serial}"); RefreshDeviceData(); DeviceDisconnected?.Invoke(this, e); } private void OnDeviceChanged(object? sender, DeviceDataEventArgs e) { _logger.LogInformation($"Device changed: {e.Device.Serial}"); RefreshDeviceData(); DeviceChanged?.Invoke(this, e); } public void RefreshDeviceData() { 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.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 }); var remoteDir = Path.GetDirectoryName(remotePath)?.Replace('\\', '/'); if (!string.IsNullOrEmpty(remoteDir)) { var reciever = new ConsoleOutputReceiver(); await Task.Run( () => { _adbClient.ExecuteRemoteCommand($"mkdir -p \"{remoteDir}\"", _deviceData, reciever); }, cancellationToken); } _logger.LogInformation($"Ensured remote directory: {remoteDir}"); 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); } public void Dispose() { if (_deviceMonitor != null) { _deviceMonitor.DeviceConnected -= OnDeviceConnected; _deviceMonitor.DeviceDisconnected -= OnDeviceDisconnected; _deviceMonitor.DeviceChanged -= OnDeviceChanged; _deviceMonitor.Dispose(); } } }