using System; using System.Diagnostics; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; using Windows.ApplicationModel; using Windows.ApplicationModel.AppService; using Windows.Foundation.Collections; using Wino.Domain.Models.Synchronization; using Wino.Core.Synchronizers; using Wino.Domain.Enums; using Wino.Domain.Interfaces; using Wino.Messaging; using Wino.Messaging.Enums; using Wino.Messaging.Server; using Wino.Services.Authenticators; namespace Wino.Server { public class ServerContext : IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient, IRecipient { private static object connectionLock = new object(); private AppServiceConnection connection = null; private readonly IDatabaseService _databaseService; private readonly IApplicationConfiguration _applicationFolderConfiguration; public ServerContext(IDatabaseService databaseService, IApplicationConfiguration applicationFolderConfiguration) { _databaseService = databaseService; _applicationFolderConfiguration = applicationFolderConfiguration; WeakReferenceMessenger.Default.RegisterAll(this); } #region Message Handlers public async void Receive(MailAddedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(AccountCreatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(AccountUpdatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(AccountRemovedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(DraftCreated message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(DraftFailed message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(DraftMapped message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(FolderRenamed message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(FolderSynchronizationEnabled message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(MailDownloadedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(MailRemovedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(MailUpdatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message); public async void Receive(MergedInboxRenamed message) => await SendMessageAsync(MessageType.UIMessage, message); #endregion /// /// Open connection to UWP app service /// public async Task InitializeAppServiceConnectionAsync() { if (connection != null) DisposeConnection(); connection = new AppServiceConnection { AppServiceName = "WinoInteropService", PackageFamilyName = GetAppPackagFamilyName() }; connection.RequestReceived += OnWinRTMessageReceived; connection.ServiceClosed += OnConnectionClosed; AppServiceConnectionStatus status = await connection.OpenAsync(); if (status != AppServiceConnectionStatus.Success) { // TODO: Handle connection error DisposeConnection(); } } public async Task TestOutlookSynchronizer() { var accountService = App.Current.Services.GetService(); var accs = await accountService.GetAccountsAsync(); var acc = accs.ElementAt(0); var authenticator = App.Current.Services.GetService(); var processor = App.Current.Services.GetService(); var sync = new OutlookSynchronizer(acc, authenticator, processor); var options = new SynchronizationOptions() { AccountId = acc.Id, Type = SynchronizationType.Full }; var result = await sync.SynchronizeAsync(options); } /// /// Disposes current connection to UWP app service. /// private void DisposeConnection() { lock (connectionLock) { if (connection == null) return; connection.RequestReceived -= OnWinRTMessageReceived; connection.ServiceClosed -= OnConnectionClosed; connection.Dispose(); connection = null; } } /// /// Sends a serialized object to UWP application if connection exists with given type. /// /// Type of the message. /// IServerMessage object that will be serialized. /// /// When the message is not IServerMessage. private async Task SendMessageAsync(MessageType messageType, object message) { if (connection == null) return; if (message is not IServerMessage serverMessage) throw new ArgumentException("Server message must be a type of IServerMessage"); string json = JsonSerializer.Serialize(message); var set = new ValueSet { { MessageConstants.MessageTypeKey, (int)messageType }, { MessageConstants.MessageDataKey, json }, { MessageConstants.MessageDataTypeKey, message.GetType().Name } }; Debug.WriteLine($"S: {messageType} ({message.GetType().Name})"); await connection.SendMessageAsync(set); } private void OnConnectionClosed(AppServiceConnection sender, AppServiceClosedEventArgs args) { // TODO: Handle connection closed. // UWP app might've been terminated or suspended. // At this point, we must keep active synchronizations going, but connection is lost. // As long as this process is alive, database will be kept updated, but no messages will be sent. DisposeConnection(); } private void OnWinRTMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { // TODO: Handle incoming messages from UWP/WINUI Application. } #region Init private string GetAppPackagFamilyName() { // If running as a standalone app, Package will throw exception. // Return hardcoded value for debugging purposes. // Connection will not be available in this case. try { return Package.Current.Id.FamilyName; } catch (Exception) { return "Debug.Wino.Server.FamilyName"; } } public async Task InitializeAsync() { await InitializeAppServiceConnectionAsync(); } #endregion } }