Remove single entry and mode launch on ctrl press.
This commit is contained in:
@@ -7,25 +7,32 @@ internal static class AppModeActivationResolver
|
|||||||
{
|
{
|
||||||
public static WinoApplicationMode Resolve(string? launchArguments, string? tileId, string? appId, WinoApplicationMode defaultMode = WinoApplicationMode.Mail)
|
public static WinoApplicationMode Resolve(string? launchArguments, string? tileId, string? appId, WinoApplicationMode defaultMode = WinoApplicationMode.Mail)
|
||||||
{
|
{
|
||||||
if (TryResolveFromText(launchArguments, out var mode))
|
if (TryResolveFromText(launchArguments, defaultMode, out var mode))
|
||||||
return mode;
|
return mode;
|
||||||
|
|
||||||
if (TryResolveFromText(tileId, out mode))
|
if (TryResolveFromText(tileId, defaultMode, out mode))
|
||||||
return mode;
|
return mode;
|
||||||
|
|
||||||
if (TryResolveFromText(appId, out mode))
|
if (TryResolveFromText(appId, defaultMode, out mode))
|
||||||
return mode;
|
return mode;
|
||||||
|
|
||||||
return defaultMode;
|
return defaultMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryResolveFromText(string? value, out WinoApplicationMode mode)
|
private static bool TryResolveFromText(string? value, WinoApplicationMode defaultMode, out WinoApplicationMode mode)
|
||||||
{
|
{
|
||||||
mode = WinoApplicationMode.Mail;
|
mode = defaultMode;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (Contains(value, "--mode=toggle-default") ||
|
||||||
|
Contains(value, "mode=toggle-default"))
|
||||||
|
{
|
||||||
|
mode = GetOpposite(defaultMode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (Contains(value, "wino-calendar") ||
|
if (Contains(value, "wino-calendar") ||
|
||||||
Contains(value, "--mode=calendar") ||
|
Contains(value, "--mode=calendar") ||
|
||||||
Contains(value, "mode=calendar") ||
|
Contains(value, "mode=calendar") ||
|
||||||
@@ -54,4 +61,9 @@ internal static class AppModeActivationResolver
|
|||||||
|
|
||||||
private static bool EqualsToken(string source, string token)
|
private static bool EqualsToken(string source, string token)
|
||||||
=> string.Equals(source.Trim(), token, StringComparison.OrdinalIgnoreCase);
|
=> string.Equals(source.Trim(), token, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static WinoApplicationMode GetOpposite(WinoApplicationMode defaultMode)
|
||||||
|
=> defaultMode == WinoApplicationMode.Mail
|
||||||
|
? WinoApplicationMode.Calendar
|
||||||
|
: WinoApplicationMode.Mail;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public partial class App : WinoApplication,
|
|||||||
IRecipient<NewCalendarSynchronizationRequested>
|
IRecipient<NewCalendarSynchronizationRequested>
|
||||||
{
|
{
|
||||||
private const int InboxSyncsPerFullSync = 20;
|
private const int InboxSyncsPerFullSync = 20;
|
||||||
|
private const string ToggleDefaultModeLaunchArgument = "--mode=toggle-default";
|
||||||
private ISynchronizationManager? _synchronizationManager;
|
private ISynchronizationManager? _synchronizationManager;
|
||||||
private IPreferencesService? _preferencesService;
|
private IPreferencesService? _preferencesService;
|
||||||
private IAccountService? _accountService;
|
private IAccountService? _accountService;
|
||||||
@@ -136,7 +137,7 @@ public partial class App : WinoApplication,
|
|||||||
{
|
{
|
||||||
base.OnLaunched(args);
|
base.OnLaunched(args);
|
||||||
|
|
||||||
// Always register notification callbacks for both app entries (Mail and Calendar).
|
// Always register notification callbacks.
|
||||||
TryRegisterAppNotifications();
|
TryRegisterAppNotifications();
|
||||||
|
|
||||||
// Initialize required services regardless of launch activation type.
|
// Initialize required services regardless of launch activation type.
|
||||||
@@ -429,7 +430,14 @@ public partial class App : WinoApplication,
|
|||||||
if (activationArgs.Kind == ExtendedActivationKind.Launch &&
|
if (activationArgs.Kind == ExtendedActivationKind.Launch &&
|
||||||
activationArgs.Data is ILaunchActivatedEventArgs launchArgs)
|
activationArgs.Data is ILaunchActivatedEventArgs launchArgs)
|
||||||
{
|
{
|
||||||
shellWindow.HandleAppActivation(launchArgs.Arguments, launchArgs.TileId, Environment.CommandLine);
|
var launchArguments = launchArgs.Arguments;
|
||||||
|
|
||||||
|
if (Program.TryConsumeCurrentProcessAlternateModeOverride())
|
||||||
|
{
|
||||||
|
launchArguments = AppendLaunchArgument(launchArguments, ToggleDefaultModeLaunchArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
shellWindow.HandleAppActivation(launchArguments, launchArgs.TileId, Environment.CommandLine);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,7 +670,14 @@ public partial class App : WinoApplication,
|
|||||||
if (args.Kind == ExtendedActivationKind.Launch &&
|
if (args.Kind == ExtendedActivationKind.Launch &&
|
||||||
args.Data is ILaunchActivatedEventArgs launchArgs)
|
args.Data is ILaunchActivatedEventArgs launchArgs)
|
||||||
{
|
{
|
||||||
shellWindow.HandleAppActivation(launchArgs.Arguments, launchArgs.TileId);
|
var launchArguments = launchArgs.Arguments;
|
||||||
|
|
||||||
|
if (Program.TryConsumeRedirectedAlternateModeOverride())
|
||||||
|
{
|
||||||
|
launchArguments = AppendLaunchArgument(launchArguments, ToggleDefaultModeLaunchArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
shellWindow.HandleAppActivation(launchArguments, launchArgs.TileId);
|
||||||
}
|
}
|
||||||
else if (TryResolveActivationMode(args, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var redirectedMode))
|
else if (TryResolveActivationMode(args, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var redirectedMode))
|
||||||
{
|
{
|
||||||
@@ -680,6 +695,13 @@ public partial class App : WinoApplication,
|
|||||||
private static string GetModeLaunchArgument(WinoApplicationMode mode)
|
private static string GetModeLaunchArgument(WinoApplicationMode mode)
|
||||||
=> mode == WinoApplicationMode.Calendar ? "--mode=calendar" : "--mode=mail";
|
=> mode == WinoApplicationMode.Calendar ? "--mode=calendar" : "--mode=mail";
|
||||||
|
|
||||||
|
private static string AppendLaunchArgument(string? launchArguments, string launchArgument)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(launchArguments)
|
||||||
|
? launchArgument
|
||||||
|
: $"{launchArguments} {launchArgument}";
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryResolveActivationMode(AppActivationArguments activationArgs, WinoApplicationMode defaultMode, out WinoApplicationMode mode)
|
private static bool TryResolveActivationMode(AppActivationArguments activationArgs, WinoApplicationMode defaultMode, out WinoApplicationMode mode)
|
||||||
{
|
{
|
||||||
mode = defaultMode;
|
mode = defaultMode;
|
||||||
|
|||||||
@@ -90,6 +90,19 @@
|
|||||||
</uap:Protocol>
|
</uap:Protocol>
|
||||||
</uap:Extension>
|
</uap:Extension>
|
||||||
|
|
||||||
|
<!-- Protocol activation: webcal -->
|
||||||
|
<uap:Extension Category="windows.protocol">
|
||||||
|
<uap:Protocol Name="webcal">
|
||||||
|
<uap:DisplayName>Calendar Protocol</uap:DisplayName>
|
||||||
|
</uap:Protocol>
|
||||||
|
</uap:Extension>
|
||||||
|
|
||||||
|
<uap:Extension Category="windows.protocol">
|
||||||
|
<uap:Protocol Name="webcals">
|
||||||
|
<uap:DisplayName>Calendar Protocol (Secure)</uap:DisplayName>
|
||||||
|
</uap:Protocol>
|
||||||
|
</uap:Extension>
|
||||||
|
|
||||||
<!-- File Assosication: EML -->
|
<!-- File Assosication: EML -->
|
||||||
<uap:Extension Category="windows.fileTypeAssociation">
|
<uap:Extension Category="windows.fileTypeAssociation">
|
||||||
<uap:FileTypeAssociation Name="eml">
|
<uap:FileTypeAssociation Name="eml">
|
||||||
@@ -99,48 +112,18 @@
|
|||||||
</uap:SupportedFileTypes>
|
</uap:SupportedFileTypes>
|
||||||
</uap:FileTypeAssociation>
|
</uap:FileTypeAssociation>
|
||||||
</uap:Extension>
|
</uap:Extension>
|
||||||
|
|
||||||
|
<!-- File Association: ICS -->
|
||||||
|
<uap:Extension Category="windows.fileTypeAssociation">
|
||||||
|
<uap:FileTypeAssociation Name="ics">
|
||||||
|
<uap:Logo>Assets\AppEntries\CalendarAssets\Square44x44Logo.png</uap:Logo>
|
||||||
|
<uap:SupportedFileTypes>
|
||||||
|
<uap:FileType>.ics</uap:FileType>
|
||||||
|
</uap:SupportedFileTypes>
|
||||||
|
</uap:FileTypeAssociation>
|
||||||
|
</uap:Extension>
|
||||||
</Extensions>
|
</Extensions>
|
||||||
</Application>
|
</Application>
|
||||||
|
|
||||||
<Application Id="CalendarApp"
|
|
||||||
Executable="$targetnametoken$.exe"
|
|
||||||
EntryPoint="$targetentrypoint$"
|
|
||||||
uap10:Parameters="--mode=calendar">
|
|
||||||
<uap:VisualElements
|
|
||||||
DisplayName="Wino Calendar"
|
|
||||||
Description="Wino.Mail.WinUI.Calendar"
|
|
||||||
BackgroundColor="transparent"
|
|
||||||
Square150x150Logo="Assets\AppEntries\CalendarAssets\Square150x150Logo.png"
|
|
||||||
Square44x44Logo="Assets\AppEntries\CalendarAssets\Square44x44Logo.png">
|
|
||||||
<uap:DefaultTile Wide310x150Logo="Assets\AppEntries\CalendarAssets\Wide310x150Logo.png" Square71x71Logo="Assets\AppEntries\CalendarAssets\SmallTile.png" Square310x310Logo="Assets\AppEntries\CalendarAssets\LargeTile.png"/>
|
|
||||||
<uap:SplashScreen Image="Assets\AppEntries\CalendarAssets\SplashScreen.png" />
|
|
||||||
</uap:VisualElements>
|
|
||||||
|
|
||||||
<Extensions>
|
|
||||||
<!-- Protocol activation: webcal -->
|
|
||||||
<uap:Extension Category="windows.protocol">
|
|
||||||
<uap:Protocol Name="webcal">
|
|
||||||
<uap:DisplayName>Calendar Protocol</uap:DisplayName>
|
|
||||||
</uap:Protocol>
|
|
||||||
</uap:Extension>
|
|
||||||
|
|
||||||
<uap:Extension Category="windows.protocol">
|
|
||||||
<uap:Protocol Name="webcals">
|
|
||||||
<uap:DisplayName>Calendar Protocol (Secure)</uap:DisplayName>
|
|
||||||
</uap:Protocol>
|
|
||||||
</uap:Extension>
|
|
||||||
|
|
||||||
<!-- File Association: ICS -->
|
|
||||||
<uap:Extension Category="windows.fileTypeAssociation">
|
|
||||||
<uap:FileTypeAssociation Name="ics">
|
|
||||||
<uap:Logo>Assets\AppEntries\CalendarAssets\Square44x44Logo.png</uap:Logo>
|
|
||||||
<uap:SupportedFileTypes>
|
|
||||||
<uap:FileType>.ics</uap:FileType>
|
|
||||||
</uap:SupportedFileTypes>
|
|
||||||
</uap:FileTypeAssociation>
|
|
||||||
</uap:Extension>
|
|
||||||
</Extensions>
|
|
||||||
</Application>
|
|
||||||
</Applications>
|
</Applications>
|
||||||
|
|
||||||
<Capabilities>
|
<Capabilities>
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ namespace Wino.Mail.WinUI;
|
|||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
|
private const string SingleInstanceKey = "WinoMailSingleInstance";
|
||||||
|
private const string ForceAlternateModeSignalEventName = "Local\\WinoMailForceAlternateMode";
|
||||||
|
private const int VkControl = 0x11;
|
||||||
|
|
||||||
|
private static bool _forceAlternateModeOnLaunch;
|
||||||
|
private static EventWaitHandle? _forceAlternateModeSignalHandle;
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static int Main(string[] args)
|
static int Main(string[] args)
|
||||||
{
|
{
|
||||||
@@ -35,15 +42,24 @@ public class Program
|
|||||||
{
|
{
|
||||||
bool isRedirect = false;
|
bool isRedirect = false;
|
||||||
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
|
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||||
AppInstance keyInstance = AppInstance.FindOrRegisterForKey("WinoMailSingleInstance");
|
_forceAlternateModeOnLaunch = args.Kind == ExtendedActivationKind.Launch && IsCtrlKeyDown();
|
||||||
|
|
||||||
|
AppInstance keyInstance = AppInstance.FindOrRegisterForKey(SingleInstanceKey);
|
||||||
|
|
||||||
if (keyInstance.IsCurrent)
|
if (keyInstance.IsCurrent)
|
||||||
{
|
{
|
||||||
|
EnsureAlternateModeOverrideSignalHandle();
|
||||||
keyInstance.Activated += OnActivated;
|
keyInstance.Activated += OnActivated;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isRedirect = true;
|
isRedirect = true;
|
||||||
|
|
||||||
|
if (_forceAlternateModeOnLaunch)
|
||||||
|
{
|
||||||
|
SignalForceAlternateMode();
|
||||||
|
}
|
||||||
|
|
||||||
RedirectActivationTo(args, keyInstance);
|
RedirectActivationTo(args, keyInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +82,83 @@ public class Program
|
|||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
static extern bool SetForegroundWindow(IntPtr hWnd);
|
static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern short GetAsyncKeyState(int vKey);
|
||||||
|
|
||||||
private static IntPtr redirectEventHandle = IntPtr.Zero;
|
private static IntPtr redirectEventHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
public static bool TryConsumeCurrentProcessAlternateModeOverride()
|
||||||
|
{
|
||||||
|
if (!_forceAlternateModeOnLaunch)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_forceAlternateModeOnLaunch = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryConsumeRedirectedAlternateModeOverride()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_forceAlternateModeSignalHandle != null)
|
||||||
|
{
|
||||||
|
return _forceAlternateModeSignalHandle.WaitOne(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EventWaitHandle.TryOpenExisting(ForceAlternateModeSignalEventName, out EventWaitHandle? signal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (signal)
|
||||||
|
{
|
||||||
|
return signal.WaitOne(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCtrlKeyDown() => (GetAsyncKeyState(VkControl) & 0x8000) != 0;
|
||||||
|
|
||||||
|
private static void EnsureAlternateModeOverrideSignalHandle()
|
||||||
|
{
|
||||||
|
if (_forceAlternateModeSignalHandle != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_forceAlternateModeSignalHandle = new EventWaitHandle(false, EventResetMode.AutoReset, ForceAlternateModeSignalEventName);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_forceAlternateModeSignalHandle = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SignalForceAlternateMode()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (EventWaitHandle.TryOpenExisting(ForceAlternateModeSignalEventName, out EventWaitHandle? signal))
|
||||||
|
{
|
||||||
|
using (signal)
|
||||||
|
{
|
||||||
|
signal.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using EventWaitHandle fallbackSignal = new(false, EventResetMode.AutoReset, ForceAlternateModeSignalEventName);
|
||||||
|
fallbackSignal.Set();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore signaling failures and continue with normal activation redirection.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do the redirection on another thread, and use a non-blocking
|
// Do the redirection on another thread, and use a non-blocking
|
||||||
// wait method to wait for the redirection to complete.
|
// wait method to wait for the redirection to complete.
|
||||||
public static void RedirectActivationTo(AppActivationArguments args,
|
public static void RedirectActivationTo(AppActivationArguments args,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace Wino.Mail.WinUI.Services;
|
|||||||
public class NotificationBuilder : INotificationBuilder
|
public class NotificationBuilder : INotificationBuilder
|
||||||
{
|
{
|
||||||
private const string MailApplicationId = "App";
|
private const string MailApplicationId = "App";
|
||||||
private const string CalendarApplicationId = "CalendarApp";
|
|
||||||
|
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
@@ -326,8 +325,8 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
|
|
||||||
private static string GetAppUserModelId(ToastTargetApp targetApp)
|
private static string GetAppUserModelId(ToastTargetApp targetApp)
|
||||||
{
|
{
|
||||||
var appId = targetApp == ToastTargetApp.Mail ? MailApplicationId : CalendarApplicationId;
|
_ = targetApp;
|
||||||
return $"{Package.Current.Id.FamilyName}!{appId}";
|
return $"{Package.Current.Id.FamilyName}!{MailApplicationId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ToastTargetApp
|
private enum ToastTargetApp
|
||||||
|
|||||||
Reference in New Issue
Block a user