/*
* Copyright (c) 2025 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.Toolkit.Uwp.Notifications;
using ProtonVPN.Client.Contracts.Messages;
using ProtonVPN.Client.EventMessaging.Contracts;
using ProtonVPN.Client.Localization.Contracts;
using ProtonVPN.Client.Localization.Extensions;
using ProtonVPN.Client.Logic.Connection.Contracts;
using ProtonVPN.Client.Logic.Connection.Contracts.Enums;
using ProtonVPN.Client.Logic.Connection.Contracts.GuestHole;
using ProtonVPN.Client.Logic.Connection.Contracts.Models;
using ProtonVPN.Client.Logic.Connection.Contracts.Models.Intents;
using ProtonVPN.Client.Logic.Connection.Contracts.Models.Intents.Features;
using ProtonVPN.Client.Logic.Profiles.Contracts.Models;
using ProtonVPN.Client.Notifications.Contracts;
using ProtonVPN.Client.Settings.Contracts;
using ProtonVPN.Client.Settings.Contracts.Extensions;
using ProtonVPN.Configurations.Contracts;
using ProtonVPN.Logging.Contracts;
namespace ProtonVPN.Client.Notifications;
public class ConnectionStatusNotificationSender : NotificationSenderBase, IConnectionStatusNotificationSender,
IEventMessageReceiver
{
private readonly ISettings _settings;
private readonly ILocalizationProvider _localizer;
private readonly IGuestHoleManager _guestHoleManager;
private readonly IConnectionManager _connectionManager;
private readonly IConfiguration _configuration;
private ConnectionStatus _lastStatus;
private bool _isMainWindowVisible;
public ConnectionStatusNotificationSender(
ILogger logger,
ISettings settings,
ILocalizationProvider localizer,
IGuestHoleManager guestHoleManager,
IConnectionManager connectionManager,
IConfiguration configuration)
: base(logger)
{
_settings = settings;
_localizer = localizer;
_guestHoleManager = guestHoleManager;
_connectionManager = connectionManager;
_configuration = configuration;
}
public void SendConnectedNotification()
{
SendNotification(ConnectionStatus.Connected);
}
public void SendDisconnectedNotification()
{
SendNotification(ConnectionStatus.Disconnected);
}
private void SendNotification(ConnectionStatus currentStatus)
{
if (_isMainWindowVisible || _guestHoleManager.IsActive || !IsToNotify(currentStatus))
{
_lastStatus = currentStatus;
return;
}
_lastStatus = currentStatus;
string title = GetNotificationTitle(currentStatus);
if (string.IsNullOrEmpty(title))
{
return;
}
ToastContentBuilder notification = new();
notification.AddText(_localizer.Get(title), AdaptiveTextStyle.Header);
AddDescription(notification, currentStatus);
Send(notification);
}
public void Receive(MainWindowVisibilityChangedMessage message)
{
_isMainWindowVisible = message.IsMainWindowVisible;
}
private bool IsToNotify(ConnectionStatus currentStatus)
{
return _settings.IsNotificationEnabled &&
(currentStatus == ConnectionStatus.Connected || (_lastStatus == ConnectionStatus.Connected && currentStatus == ConnectionStatus.Disconnected));
}
private string GetNotificationTitle(ConnectionStatus connectionStatus)
{
return connectionStatus switch
{
ConnectionStatus.Connected => "SystemNotification_Connected",
ConnectionStatus.Disconnected => "SystemNotification_Disconnected",
_ => string.Empty,
};
}
private void AddDescription(ToastContentBuilder notification, ConnectionStatus connectionStatus)
{
string? description = null;
if (connectionStatus == ConnectionStatus.Connected)
{
string? locationDetails = GetLocationDetails();
if (locationDetails != null)
{
description = _localizer.GetFormat("SystemNotification_ConnectedTo", locationDetails);
}
}
else if (connectionStatus == ConnectionStatus.Disconnected && _settings.IsAdvancedKillSwitchActive())
{
notification.AddAppLogoOverride(new Uri(Path.Combine(_configuration.AssetsFolder, "Illustrations", "kill-switch-protected.png")));
description = _localizer.Get("Notifications_KillSwitch_Description");
}
if (description is not null)
{
notification.AddText(description);
}
}
private string? GetLocationDetails()
{
ConnectionDetails? connectionDetails = _connectionManager.CurrentConnectionDetails;
if (connectionDetails == null)
{
return null;
}
IConnectionIntent connectionIntent = connectionDetails.OriginalConnectionIntent;
string locationDetails = connectionIntent is IConnectionProfile connectionProfile
? connectionProfile.Name
: _localizer.GetConnectionDetailsTitle(connectionDetails);
string connectionIntentSubtitle = connectionIntent is IConnectionProfile
? _localizer.GetConnectionProfileDetailsSubtitle(connectionDetails)
: _localizer.GetConnectionDetailsSubtitle(connectionDetails);
if (!string.IsNullOrEmpty(connectionIntentSubtitle))
{
locationDetails += $" - {connectionIntentSubtitle}";
}
switch (connectionIntent.Feature)
{
case P2PFeatureIntent:
locationDetails += $" ({_localizer.GetFeatureName(Feature.P2P)})";
break;
case TorFeatureIntent:
locationDetails += $" ({_localizer.GetFeatureName(Feature.Tor)})";
break;
default:
break;
}
return locationDetails;
}
}