using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Threading; using StreamPlayer.Desktop.Models; using StreamPlayer.Desktop.ViewModels; namespace StreamPlayer.Desktop.Views; public partial class MainWindow : Window { private readonly CancellationTokenSource _lifetimeCts = new(); private MainWindowViewModel? _viewModel; public MainWindow() { InitializeComponent(); DataContextChanged += OnDataContextChanged; Opened += OnOpened; Closed += OnClosed; AttachViewModel(DataContext as MainWindowViewModel); } private void OnDataContextChanged(object? sender, EventArgs e) { AttachViewModel(DataContext as MainWindowViewModel); } private void AttachViewModel(MainWindowViewModel? newViewModel) { if (_viewModel != null) { _viewModel.ChannelRequested -= OnChannelRequested; _viewModel.UpdateAvailable -= OnUpdateAvailable; _viewModel.DeviceStatusEvaluated -= OnDeviceStatusEvaluated; _viewModel.ErrorRaised -= OnErrorRaised; } _viewModel = newViewModel; if (_viewModel != null) { _viewModel.ChannelRequested += OnChannelRequested; _viewModel.UpdateAvailable += OnUpdateAvailable; _viewModel.DeviceStatusEvaluated += OnDeviceStatusEvaluated; _viewModel.ErrorRaised += OnErrorRaised; } } private async void OnOpened(object? sender, EventArgs e) { if (_viewModel == null) { return; } try { await _viewModel.InitializeAsync(_lifetimeCts.Token); await _viewModel.CheckForUpdatesAsync(_lifetimeCts.Token); await _viewModel.VerifyDeviceAsync(_lifetimeCts.Token); } catch (OperationCanceledException) { // ignored } } private void OnClosed(object? sender, EventArgs e) { _lifetimeCts.Cancel(); _lifetimeCts.Dispose(); AttachViewModel(null); } private void OnChannelRequested(object? sender, StreamChannel channel) { if (channel == null) { return; } Dispatcher.UIThread.Post(() => { var player = new PlayerWindow(channel); player.Show(this); }); } private async void OnUpdateAvailable(object? sender, UpdateInfo info) { var mandatory = info.IsMandatory(AppVersion.VersionCode); var dialog = new UpdateDialog(info, mandatory); var result = await dialog.ShowDialog(this); switch (result) { case UpdateDialogAction.Download: LaunchUrl(info.DownloadUrl); if (mandatory) { Close(); } break; case UpdateDialogAction.ViewRelease: LaunchUrl(string.IsNullOrWhiteSpace(info.ReleasePageUrl) ? info.DownloadUrl : info.ReleasePageUrl); if (mandatory) { Close(); } break; case UpdateDialogAction.Exit: Close(); break; case UpdateDialogAction.Skip: default: break; } } private async void OnDeviceStatusEvaluated(object? sender, DeviceStatus status) { if (!status.IsBlocked) { return; } var dialog = new BlockedDialog(status.Reason, status.TokenPart); await dialog.ShowDialog(this); Close(); } private async void OnErrorRaised(object? sender, string message) { if (string.IsNullOrWhiteSpace(message)) { return; } await ShowInfoAsync("Aviso", message); } private static void LaunchUrl(string? url) { if (string.IsNullOrWhiteSpace(url)) { return; } try { Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); } catch { // ignore } } private Task ShowInfoAsync(string title, string message) { var okButton = new Button { Content = "Aceptar", HorizontalAlignment = HorizontalAlignment.Right, Width = 120 }; var stack = new StackPanel { Spacing = 12 }; stack.Children.Add(new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap }); stack.Children.Add(okButton); var dialog = new Window { Title = title, Width = 420, SizeToContent = SizeToContent.Height, WindowStartupLocation = WindowStartupLocation.CenterOwner, CanResize = false, Content = new Border { Padding = new Thickness(20), Child = stack } }; okButton.Click += (_, _) => dialog.Close(); return dialog.ShowDialog(this); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } }