/*
* Copyright (c) 2023 Proton AG
*
* This file is part of ProtonVPN.
*
* ProtonVPN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonVPN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonVPN. If not, see .
*/
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using ProtonVPN.Client.Common.Dispatching;
using ProtonVPN.Client.Common.Messages;
using ProtonVPN.Client.Common.Models;
using ProtonVPN.Client.Contracts.Messages;
using ProtonVPN.Client.Core.Extensions;
using ProtonVPN.Client.Core.Messages;
using ProtonVPN.Client.Core.Services.Activation;
using ProtonVPN.Client.Core.Services.Activation.Bases;
using ProtonVPN.Client.Core.Services.Enabling;
using ProtonVPN.Client.Core.Services.Selection;
using ProtonVPN.Client.EventMessaging.Contracts;
using ProtonVPN.Client.Localization.Contracts;
using ProtonVPN.Client.Logic.Auth.Contracts;
using ProtonVPN.Client.Logic.Auth.Contracts.Enums;
using ProtonVPN.Client.Logic.Auth.Contracts.Messages;
using ProtonVPN.Client.Settings.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.AppLogs;
using Windows.Foundation;
using Windows.Graphics;
using WinUIEx;
namespace ProtonVPN.Client.Services.Activation;
public class MainWindowActivator : WindowActivatorBase, IMainWindowActivator,
IEventMessageReceiver,
IEventMessageReceiver,
IEventMessageReceiver
{
private const int LOGIN_WINDOW_WIDTH = 1016;
private const int LOGIN_WINDOW_HEIGHT = 659;
private SizeInt32? _lastKnownWindowSize;
private PointInt32? _lastKnownWindowPosition;
private bool _isWindowMovable = true;
private readonly IUserAuthenticator _userAuthenticator;
private readonly IEventMessageSender _eventMessageSender;
private readonly IEfficiencyModeEnabler _efficiencyModeEnabler;
public Size CurrentWindowSize => new(Host?.Width ?? 0, Host?.Height ?? 0);
public Window? Window => Host;
public override string WindowTitle { get; } = App.APPLICATION_NAME;
protected bool IsUserInScope => _userAuthenticator.AuthenticationStatus is AuthenticationStatus.LoggedIn or AuthenticationStatus.LoggingOut;
protected bool IsWindowInRestoredState => Host?.AppWindow?.Presenter is OverlappedPresenter presenter
&& presenter.State == OverlappedPresenterState.Restored;
protected override bool ShouldCreateInstanceIfMissing => false;
public MainWindowActivator(
ILogger logger,
IUIThreadDispatcher uiThreadDispatcher,
IApplicationThemeSelector themeSelector,
ISettings settings,
ILocalizationService localizationService,
ILocalizationProvider localizer,
IApplicationIconSelector iconSelector,
IUserAuthenticator userAuthenticator,
IEventMessageSender eventMessageSender,
IEfficiencyModeEnabler efficiencyModeEnabler)
: base(logger,
uiThreadDispatcher,
themeSelector,
settings,
localizationService,
localizer,
iconSelector)
{
_userAuthenticator = userAuthenticator;
_eventMessageSender = eventMessageSender;
_efficiencyModeEnabler = efficiencyModeEnabler;
}
public void Receive(AuthenticationStatusChanged message)
{
bool shouldInvalidateWindowPosition =
_isWindowMovable &&
_userAuthenticator.AuthenticationStatus is AuthenticationStatus.LoggingOut or AuthenticationStatus.LoggedIn;
UIThreadDispatcher.TryEnqueue(() =>
{
if (_userAuthenticator.AuthenticationStatus is AuthenticationStatus.LoggingOut)
{
SaveWindowPosition();
}
InvalidateWindowTitleBarVisibility();
if (shouldInvalidateWindowPosition)
{
InvalidateWindowPosition();
}
InvalidateWindowState();
});
}
public void SetWindowMovable(bool isMovable)
{
_isWindowMovable = isMovable;
}
public void Receive(AppIconStatusChangedMessage message)
{
UIThreadDispatcher.TryEnqueue(InvalidateBadgeIcon);
}
protected override void OnInitialized()
{
base.OnInitialized();
InvalidateBadgeIcon();
InvalidateWindowTitleBarVisibility();
_eventMessageSender.Send(new ApplicationStartingMessage());
_efficiencyModeEnabler.TryEnableEfficiencyMode();
}
protected override void RegisterToHostEvents()
{
base.RegisterToHostEvents();
if (Host?.AppWindow != null)
{
Host.AppWindow.Changed += OnAppWindowChanged;
}
}
protected override void UnregisterFromHostEvents()
{
base.UnregisterFromHostEvents();
if (Host?.AppWindow != null)
{
Host.AppWindow.Changed -= OnAppWindowChanged;
}
}
protected override void InvalidateWindowPosition()
{
if (Host == null)
{
Logger.Info($"Cannot invalidate {typeof(MainWindow)?.Name} position, the window has not been initialized");
return;
}
WindowPositionParameters parameters =
_userAuthenticator.IsLoggedIn
? new()
{
Width = Settings.WindowWidth,
Height = Settings.WindowHeight,
XPosition = Settings.WindowXPosition,
YPosition = Settings.WindowYPosition,
}
: new()
{
Width = LOGIN_WINDOW_WIDTH,
Height = LOGIN_WINDOW_HEIGHT,
};
// Set window state to normal to apply size and position.
Host.WindowState = WindowState.Normal;
Host.MoveAndResize(parameters);
}
protected override void InvalidateWindowState()
{
if (Host == null)
{
Logger.Info($"Cannot invalidate {typeof(MainWindow)?.Name} state, the window has not been initialized");
return;
}
bool isToMaximize = _userAuthenticator.IsLoggedIn && Settings.IsWindowMaximized;
if (isToMaximize && (IsWindowVisible || Host.WindowState == WindowState.Minimized))
{
Host.WindowState = WindowState.Maximized;
}
}
protected override void OnWindowOpened()
{
base.OnWindowOpened();
_efficiencyModeEnabler.TryDisableEfficiencyMode();
_eventMessageSender.Send(new MainWindowVisibilityChangedMessage { IsMainWindowVisible = true });
InvalidateBadgeIcon();
}
protected override void OnWindowHidden()
{
base.OnWindowHidden();
_eventMessageSender.Send(new MainWindowVisibilityChangedMessage { IsMainWindowVisible = false });
SaveWindowPosition();
_efficiencyModeEnabler.TryEnableEfficiencyMode();
}
protected override void OnWindowStateChanged()
{
base.OnWindowStateChanged();
SaveWindowState();
SaveWindowPosition();
}
protected override void OnWindowClosing(WindowEventArgs e)
{
base.OnWindowClosing(e);
if (_userAuthenticator.IsLoggedIn)
{
e.Handled = true;
}
}
protected override void OnWindowCloseAborted()
{
base.OnWindowCloseAborted();
Hide();
}
protected override void OnWindowClosed()
{
base.OnWindowClosed();
SaveWindowPosition();
_eventMessageSender.Send(new ApplicationStoppedMessage());
}
protected override void OnWindowFocused()
{
base.OnWindowFocused();
_eventMessageSender.Send(new MainWindowFocusedMessage());
}
private void OnAppWindowChanged(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidSizeChange)
{
UpdateLastKnownWindowSize();
}
if (args.DidPositionChange)
{
UpdateLastKnownWindowPosition();
}
}
private void UpdateLastKnownWindowSize()
{
if (IsUserInScope && IsWindowInRestoredState)
{
_lastKnownWindowSize = new SizeInt32(
Convert.ToInt32(Host!.Width),
Convert.ToInt32(Host!.Height));
}
}
private void UpdateLastKnownWindowPosition()
{
if (IsUserInScope && IsWindowInRestoredState)
{
_lastKnownWindowPosition = Host!.AppWindow.Position;
}
}
private void InvalidateBadgeIcon()
{
Host?.SetBadge(IconSelector.GetTaskbarBadgeIcon());
}
private void SaveWindowState()
{
if (_userAuthenticator.IsLoggedIn && CurrentWindowState != WindowState.Minimized)
{
Settings.IsWindowMaximized = CurrentWindowState == WindowState.Maximized;
}
}
private void SaveWindowPosition()
{
try
{
if (IsUserInScope)
{
if (_lastKnownWindowPosition.HasValue)
{
Settings.WindowXPosition = _lastKnownWindowPosition.Value.X;
Settings.WindowYPosition = _lastKnownWindowPosition.Value.Y;
}
if (_lastKnownWindowSize.HasValue)
{
Settings.WindowWidth = _lastKnownWindowSize.Value.Width;
Settings.WindowHeight = _lastKnownWindowSize.Value.Height;
}
}
}
catch (Exception ex)
{
Logger.Error("An exception occurred when saving the window position.", ex);
}
}
private void InvalidateWindowTitleBarVisibility()
{
Host?.InvalidateTitleBarVisibility(isTitleBarVisible: _userAuthenticator.IsLoggedIn);
}
public void Receive(WindowsSessionEndingMessage message)
{
SaveWindowPosition();
}
}