Initial commit.
This commit is contained in:
39
Wino.Mail/Activation/ActivationHandler.cs
Normal file
39
Wino.Mail/Activation/ActivationHandler.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
// For more information on understanding and extending activation flow see
|
||||
// https://github.com/microsoft/TemplateStudio/blob/main/docs/UWP/activation.md
|
||||
internal abstract class ActivationHandler
|
||||
{
|
||||
public abstract bool CanHandle(object args);
|
||||
|
||||
public abstract Task HandleAsync(object args);
|
||||
}
|
||||
|
||||
// Extend this class to implement new ActivationHandlers
|
||||
internal abstract class ActivationHandler<T> : ActivationHandler
|
||||
where T : class
|
||||
{
|
||||
// Override this method to add the activation logic in your activation handler
|
||||
protected abstract Task HandleInternalAsync(T args);
|
||||
|
||||
public override async Task HandleAsync(object args)
|
||||
{
|
||||
await HandleInternalAsync(args as T);
|
||||
}
|
||||
|
||||
public override bool CanHandle(object args)
|
||||
{
|
||||
// CanHandle checks the args is of type you have configured
|
||||
return args is T && CanHandleInternal(args as T);
|
||||
}
|
||||
|
||||
// You can override this method to add extra validation on activation args
|
||||
// to determine if your ActivationHandler should handle this activation args
|
||||
protected virtual bool CanHandleInternal(T args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Wino.Mail/Activation/BackgroundActivationHandler.cs
Normal file
174
Wino.Mail/Activation/BackgroundActivationHandler.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Serilog;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Notifications;
|
||||
using Wino.Core;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Core.UWP.Services;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
internal class BackgroundActivationHandler : ActivationHandler<BackgroundActivatedEventArgs>
|
||||
{
|
||||
private const string BackgroundExecutionLogTag = "[BackgroundExecution] ";
|
||||
|
||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||
private readonly IBackgroundSynchronizer _backgroundSynchronizer;
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IWinoRequestProcessor _winoRequestProcessor;
|
||||
private readonly IWinoSynchronizerFactory _winoSynchronizerFactory;
|
||||
private readonly IMailService _mailService;
|
||||
private ToastArguments _toastArguments;
|
||||
|
||||
BackgroundTaskDeferral _deferral;
|
||||
public BackgroundActivationHandler(IWinoRequestDelegator winoRequestDelegator,
|
||||
IBackgroundSynchronizer backgroundSynchronizer,
|
||||
INativeAppService nativeAppService,
|
||||
IWinoRequestProcessor winoRequestProcessor,
|
||||
IWinoSynchronizerFactory winoSynchronizerFactory,
|
||||
IMailService mailService)
|
||||
{
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
_backgroundSynchronizer = backgroundSynchronizer;
|
||||
_nativeAppService = nativeAppService;
|
||||
_winoRequestProcessor = winoRequestProcessor;
|
||||
_winoSynchronizerFactory = winoSynchronizerFactory;
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
protected override async Task HandleInternalAsync(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
var instance = args.TaskInstance;
|
||||
var taskName = instance.Task.Name;
|
||||
|
||||
instance.Canceled -= OnBackgroundExecutionCanceled;
|
||||
instance.Canceled += OnBackgroundExecutionCanceled;
|
||||
|
||||
_deferral = instance.GetDeferral();
|
||||
|
||||
if (taskName == BackgroundTaskService.ToastActivationTaskEx)
|
||||
{
|
||||
// ToastNotificationActionTriggerDetail somehow use UI thread.
|
||||
// Calling this without a dispatcher will result in error.
|
||||
|
||||
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
|
||||
_toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
|
||||
});
|
||||
|
||||
// All toast activation mail actions are handled here like mark as read or delete.
|
||||
// This should not launch the application on the foreground.
|
||||
|
||||
// Get the action and mail item id.
|
||||
// Prepare package and send to delegator.
|
||||
|
||||
if (_toastArguments.TryGetValue(Constants.ToastMailItemIdKey, out string mailItemId) &&
|
||||
_toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation action))
|
||||
{
|
||||
// TODO: Remote folder id.
|
||||
var mailItem = await _mailService.GetSingleMailItemAsync(mailItemId, string.Empty);
|
||||
|
||||
if (mailItem == null) return;
|
||||
|
||||
if (_nativeAppService.IsAppRunning())
|
||||
{
|
||||
// Just send the package. We should reflect the UI changes as well.
|
||||
var package = new MailOperationPreperationRequest(action, mailItem);
|
||||
|
||||
await _winoRequestDelegator.ExecuteAsync(package);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to synchronize changes without reflection the UI changes.
|
||||
|
||||
var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItem.AssignedAccount.Id);
|
||||
var prepRequest = new MailOperationPreperationRequest(action, mailItem);
|
||||
|
||||
var requests = await _winoRequestProcessor.PrepareRequestsAsync(prepRequest);
|
||||
|
||||
foreach (var request in requests)
|
||||
{
|
||||
synchronizer.QueueRequest(request);
|
||||
}
|
||||
|
||||
var options = new SynchronizationOptions()
|
||||
{
|
||||
Type = SynchronizationType.ExecuteRequests,
|
||||
AccountId = mailItem.AssignedAccount.Id
|
||||
};
|
||||
|
||||
await synchronizer.SynchronizeAsync(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx)
|
||||
{
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
// Run timer based background synchronization.
|
||||
|
||||
await _backgroundSynchronizer.RunBackgroundSynchronizationAsync(BackgroundSynchronizationReason.Timer);
|
||||
|
||||
watch.Stop();
|
||||
Log.Information($"{BackgroundExecutionLogTag}Background synchronization is completed in {watch.Elapsed.TotalSeconds} seconds.");
|
||||
}
|
||||
|
||||
instance.Canceled -= OnBackgroundExecutionCanceled;
|
||||
|
||||
_deferral.Complete();
|
||||
}
|
||||
|
||||
private void OnBackgroundExecutionCanceled(Windows.ApplicationModel.Background.IBackgroundTaskInstance sender, Windows.ApplicationModel.Background.BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
Log.Error($"{BackgroundExecutionLogTag} ({sender.Task.Name}) Background task is canceled. Reason -> {reason}");
|
||||
|
||||
_deferral?.Complete();
|
||||
}
|
||||
|
||||
protected override bool CanHandleInternal(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
var instance = args.TaskInstance;
|
||||
var taskName = instance.Task.Name;
|
||||
|
||||
if (taskName == BackgroundTaskService.ToastActivationTaskEx)
|
||||
{
|
||||
// User clicked Mark as Read or Delete in toast notification.
|
||||
// MailId and Action must present in the arguments.
|
||||
|
||||
return true;
|
||||
|
||||
//if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
|
||||
//{
|
||||
// _toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
|
||||
|
||||
// return
|
||||
// _toastArguments.Contains(Constants.ToastMailItemIdKey) &&
|
||||
// _toastArguments.Contains(Constants.ToastActionKey);
|
||||
//}
|
||||
|
||||
}
|
||||
else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx)
|
||||
{
|
||||
// This is timer based background synchronization.
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Wino.Mail/Activation/DefaultActivationHandler.cs
Normal file
23
Wino.Mail/Activation/DefaultActivationHandler.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Wino.Views;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
internal class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
|
||||
{
|
||||
protected override Task HandleInternalAsync(IActivatedEventArgs args)
|
||||
{
|
||||
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Only navigate if Frame content doesn't exist.
|
||||
protected override bool CanHandleInternal(IActivatedEventArgs args)
|
||||
=> (Window.Current?.Content as Frame)?.Content == null;
|
||||
}
|
||||
}
|
||||
68
Wino.Mail/Activation/FileActivationHandler.cs
Normal file
68
Wino.Mail/Activation/FileActivationHandler.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Services;
|
||||
using Wino.Helpers;
|
||||
using Wino.Views;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
internal class FileActivationHandler : ActivationHandler<FileActivatedEventArgs>
|
||||
{
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IMimeFileService _mimeFileService;
|
||||
private readonly IStatePersistanceService _statePersistanceService;
|
||||
private readonly IWinoNavigationService _winoNavigationService;
|
||||
|
||||
public FileActivationHandler(INativeAppService nativeAppService,
|
||||
IMimeFileService mimeFileService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IWinoNavigationService winoNavigationService)
|
||||
{
|
||||
_nativeAppService = nativeAppService;
|
||||
_mimeFileService = mimeFileService;
|
||||
_statePersistanceService = statePersistanceService;
|
||||
_winoNavigationService = winoNavigationService;
|
||||
}
|
||||
|
||||
protected override async Task HandleInternalAsync(FileActivatedEventArgs args)
|
||||
{
|
||||
// Always handle the last item passed.
|
||||
// Multiple files are not supported.
|
||||
|
||||
var file = args.Files.Last() as StorageFile;
|
||||
|
||||
// Only EML files are supported now.
|
||||
var fileExtension = Path.GetExtension(file.Path);
|
||||
|
||||
if (string.Equals(fileExtension, ".eml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var fileBytes = await file.ReadBytesAsync();
|
||||
var directoryName = Path.GetDirectoryName(file.Path);
|
||||
|
||||
var messageInformation = await _mimeFileService.GetMimeMessageInformationAsync(fileBytes, directoryName).ConfigureAwait(false);
|
||||
|
||||
if (_nativeAppService.IsAppRunning())
|
||||
{
|
||||
// TODO: Activate another Window and go to mail rendering page.
|
||||
_winoNavigationService.NavigateRendering(messageInformation);
|
||||
}
|
||||
else
|
||||
{
|
||||
_statePersistanceService.ShouldShiftMailRenderingDesign = true;
|
||||
(Window.Current.Content as Frame).Navigate(typeof(MailRenderingPage), messageInformation, new DrillInNavigationTransitionInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanHandleInternal(FileActivatedEventArgs args) => args.Files.Any();
|
||||
|
||||
}
|
||||
}
|
||||
56
Wino.Mail/Activation/ProtocolActivationHandler.cs
Normal file
56
Wino.Mail/Activation/ProtocolActivationHandler.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Messages.Authorization;
|
||||
using Wino.Core.Messages.Shell;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
internal class ProtocolActivationHandler : ActivationHandler<ProtocolActivatedEventArgs>
|
||||
{
|
||||
private const string GoogleAuthorizationProtocolTag = "google.pw.oauth2";
|
||||
private const string MailtoProtocolTag = "mailto:";
|
||||
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly ILaunchProtocolService _launchProtocolService;
|
||||
|
||||
public ProtocolActivationHandler(INativeAppService nativeAppService, ILaunchProtocolService launchProtocolService)
|
||||
{
|
||||
_nativeAppService = nativeAppService;
|
||||
_launchProtocolService = launchProtocolService;
|
||||
}
|
||||
|
||||
protected override Task HandleInternalAsync(ProtocolActivatedEventArgs args)
|
||||
{
|
||||
// Check URI prefix.
|
||||
|
||||
var protocolString = args.Uri.AbsoluteUri;
|
||||
|
||||
// Google OAuth Response
|
||||
if (protocolString.StartsWith(GoogleAuthorizationProtocolTag))
|
||||
{
|
||||
// App must be working already. No need to check for running state.
|
||||
WeakReferenceMessenger.Default.Send(new ProtocolAuthorizationCallbackReceived(args.Uri));
|
||||
}
|
||||
else if (protocolString.StartsWith(MailtoProtocolTag))
|
||||
{
|
||||
// mailto activation. Try to parse params.
|
||||
|
||||
var replaced = protocolString.Replace(MailtoProtocolTag, "mailto=");
|
||||
replaced = Wino.Core.Extensions.StringExtensions.ReplaceFirst(replaced, "?", "&");
|
||||
|
||||
_launchProtocolService.MailtoParameters = HttpUtility.ParseQueryString(replaced);
|
||||
|
||||
if (_nativeAppService.IsAppRunning())
|
||||
{
|
||||
// Just send publish a message. Shell will continue.
|
||||
WeakReferenceMessenger.Default.Send(new MailtoProtocolMessageRequested());
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Wino.Mail/Activation/ToastNotificationActivationHandler.cs
Normal file
79
Wino.Mail/Activation/ToastNotificationActivationHandler.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Serilog;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Messages.Accounts;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
/// <summary>
|
||||
/// This handler will only handle the toasts that runs on foreground.
|
||||
/// Background executions are not handled here like mark as read or delete.
|
||||
/// </summary>
|
||||
internal class ToastNotificationActivationHandler : ActivationHandler<ToastNotificationActivatedEventArgs>
|
||||
{
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly IFolderService _folderService;
|
||||
|
||||
private ToastArguments _toastArguments;
|
||||
|
||||
public ToastNotificationActivationHandler(INativeAppService nativeAppService,
|
||||
IMailService mailService,
|
||||
IFolderService folderService)
|
||||
{
|
||||
_nativeAppService = nativeAppService;
|
||||
_mailService = mailService;
|
||||
_folderService = folderService;
|
||||
}
|
||||
|
||||
protected override async Task HandleInternalAsync(ToastNotificationActivatedEventArgs args)
|
||||
{
|
||||
// Create the mail item navigation event.
|
||||
// If the app is running, it'll be picked up by the Messenger.
|
||||
// Otherwise we'll save it and handle it when the shell loads all accounts.
|
||||
|
||||
// Parse the mail unique id and perform above actions.
|
||||
if (Guid.TryParse(_toastArguments[Constants.ToastMailItemIdKey], out Guid mailItemUniqueId))
|
||||
{
|
||||
var account = await _mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false);
|
||||
if (account == null) return;
|
||||
|
||||
var mailItem = await _mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false);
|
||||
if (mailItem == null) return;
|
||||
|
||||
var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem);
|
||||
|
||||
// Delegate this event to LaunchProtocolService so app shell can pick it up on launch if app doesn't work.
|
||||
var launchProtocolService = App.Current.Services.GetService<ILaunchProtocolService>();
|
||||
launchProtocolService.LaunchParameter = message;
|
||||
|
||||
// Send the messsage anyways. Launch protocol service will be ignored if the message is picked up by subscriber shell.
|
||||
WeakReferenceMessenger.Default.Send(message);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanHandleInternal(ToastNotificationActivatedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
_toastArguments = ToastArguments.Parse(args.Argument);
|
||||
|
||||
return
|
||||
_toastArguments.Contains(Constants.ToastMailItemIdKey) &&
|
||||
_toastArguments.Contains(Constants.ToastActionKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Couldn't handle parsing toast notification arguments for foreground navigate.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user