mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cf0e477c1 | |||
| 426a95c425 | |||
| 5e52f7fcb0 | |||
| c9a1b2e451 | |||
| 6087375fb0 |
@@ -24,5 +24,8 @@
|
||||
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
||||
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
||||
|
||||
<string name="vpnStateEventChannelName">Уведомления о VPN</string>
|
||||
<string name="vpnStateEventChannelDescription">Краткие оповещения при подключении и отключении VPN</string>
|
||||
|
||||
<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
|
||||
</resources>
|
||||
@@ -24,5 +24,8 @@
|
||||
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
||||
<string name="openNotificationSettings">Open notification settings</string>
|
||||
|
||||
<string name="vpnStateEventChannelName">VPN connection alerts</string>
|
||||
<string name="vpnStateEventChannelDescription">Brief alerts when VPN connects or disconnects</string>
|
||||
|
||||
<string name="tvNoFileBrowser">Please install a file management utility to browse files</string>
|
||||
</resources>
|
||||
@@ -1003,6 +1003,11 @@ class AmneziaActivity : QtActivity() {
|
||||
@Suppress("unused")
|
||||
fun isNotificationPermissionGranted(): Boolean = applicationContext.isNotificationPermissionGranted()
|
||||
|
||||
@Suppress("unused")
|
||||
fun showVpnStateNotification(title: String, message: String) {
|
||||
ServiceNotification.showVpnStateEvent(applicationContext, title, message)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun requestNotificationPermission() {
|
||||
val shouldShowPreRequest = shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
|
||||
|
||||
@@ -26,6 +26,9 @@ private const val OLD_NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notific
|
||||
private const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notifications"
|
||||
const val NOTIFICATION_ID = 1337
|
||||
|
||||
const val VPN_STATE_EVENT_NOTIFICATION_ID = 1338
|
||||
private const val VPN_STATE_EVENT_CHANNEL_ID = "org.amnezia.vpn.vpn_state_events"
|
||||
|
||||
private const val GET_ACTIVITY_REQUEST_CODE = 0
|
||||
private const val CONNECT_REQUEST_CODE = 1
|
||||
private const val DISCONNECT_REQUEST_CODE = 2
|
||||
@@ -162,8 +165,42 @@ class ServiceNotification(private val context: Context) {
|
||||
.setDescription(context.resources.getString(R.string.notificationChannelDescription))
|
||||
.build()
|
||||
)
|
||||
createNotificationChannel(
|
||||
Builder(VPN_STATE_EVENT_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT)
|
||||
.setShowBadge(false)
|
||||
.setSound(null, null)
|
||||
.setVibrationEnabled(false)
|
||||
.setLightsEnabled(false)
|
||||
.setName(context.getString(R.string.vpnStateEventChannelName))
|
||||
.setDescription(context.getString(R.string.vpnStateEventChannelDescription))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Brief alert when VPN connects or disconnects (invoked from Qt via AmneziaActivity). */
|
||||
fun showVpnStateEvent(context: Context, title: String, message: String) {
|
||||
if (!context.isNotificationPermissionGranted()) return
|
||||
val nm = NotificationManagerCompat.from(context)
|
||||
val notification = NotificationCompat.Builder(context, VPN_STATE_EVENT_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_amnezia_round)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setAutoCancel(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
GET_ACTIVITY_REQUEST_CODE,
|
||||
Intent(context, AmneziaActivity::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
.build()
|
||||
nm.notify(VPN_STATE_EVENT_NOTIFICATION_ID, notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ set(LIBS ${LIBS}
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/macos/macos_ne_vpn_notification.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
@@ -42,6 +43,7 @@ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_contro
|
||||
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/macos/macos_ne_vpn_notification.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
|
||||
@@ -85,6 +85,11 @@ if(NOT ANDROID)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.h
|
||||
)
|
||||
else()
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.h
|
||||
${CLIENT_ROOT_DIR}/platforms/android/android_notificationhandler.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
@@ -178,6 +183,11 @@ if(NOT ANDROID)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.cpp
|
||||
)
|
||||
else()
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.cpp
|
||||
${CLIENT_ROOT_DIR}/platforms/android/android_notificationhandler.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(COMMON_FILES_H
|
||||
|
||||
@@ -77,9 +77,7 @@
|
||||
#include "ui/models/ipSplitTunnelingModel.h"
|
||||
#include "ui/models/newsModel.h"
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#include "ui/utils/notificationHandler.h"
|
||||
#endif
|
||||
class NotificationHandler;
|
||||
|
||||
class CoreSignalHandlers;
|
||||
class TestMultipleImports;
|
||||
@@ -144,9 +142,7 @@ private:
|
||||
SecureServersRepository* m_serversRepository;
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
NotificationHandler* m_notificationHandler;
|
||||
#endif
|
||||
NotificationHandler* m_notificationHandler {};
|
||||
|
||||
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
#include "ui/models/containersModel.h"
|
||||
#include "core/utils/containerEnum.h"
|
||||
|
||||
#include "ui/utils/notificationHandler.h"
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#include "ui/utils/notificationHandler.h"
|
||||
#include "ui/utils/systemTrayNotificationHandler.h"
|
||||
#endif
|
||||
|
||||
@@ -410,22 +410,23 @@ void CoreSignalHandlers::initIosSettingsHandler()
|
||||
|
||||
void CoreSignalHandlers::initNotificationHandler()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
m_coreController->m_notificationHandler = NotificationHandler::create(m_coreController);
|
||||
|
||||
connect(m_coreController->m_connectionController, &ConnectionController::connectionStateChanged, m_coreController->m_notificationHandler,
|
||||
&NotificationHandler::setConnectionState);
|
||||
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_notificationHandler, &NotificationHandler::onTranslationsUpdated);
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
connect(m_coreController->m_notificationHandler, &NotificationHandler::raiseRequested, m_coreController->m_pageController, &PageController::raiseMainWindow);
|
||||
connect(m_coreController->m_notificationHandler, &NotificationHandler::connectRequested, m_coreController->m_connectionUiController,
|
||||
static_cast<void (ConnectionUiController::*)()>(&ConnectionUiController::openConnection));
|
||||
connect(m_coreController->m_notificationHandler, &NotificationHandler::disconnectRequested, m_coreController->m_connectionUiController,
|
||||
&ConnectionUiController::closeConnection);
|
||||
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_notificationHandler, &NotificationHandler::onTranslationsUpdated);
|
||||
|
||||
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
|
||||
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||
#endif
|
||||
if (auto *trayHandler = qobject_cast<SystemTrayNotificationHandler *>(m_coreController->m_notificationHandler)) {
|
||||
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CoreSignalHandlers::initUpdateFoundHandler()
|
||||
|
||||
@@ -307,6 +307,16 @@ void AndroidController::requestNotificationPermission()
|
||||
callActivityMethod("requestNotificationPermission", "()V");
|
||||
}
|
||||
|
||||
void AndroidController::showVpnStateNotification(const QString &title, const QString &message)
|
||||
{
|
||||
if (!isNotificationPermissionGranted()) {
|
||||
return;
|
||||
}
|
||||
callActivityMethod("showVpnStateNotification", "(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
QJniObject::fromString(title).object<jstring>(),
|
||||
QJniObject::fromString(message).object<jstring>());
|
||||
}
|
||||
|
||||
bool AndroidController::requestAuthentication()
|
||||
{
|
||||
QEventLoop wait;
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
|
||||
bool isNotificationPermissionGranted();
|
||||
void requestNotificationPermission();
|
||||
void showVpnStateNotification(const QString &title, const QString &message);
|
||||
bool requestAuthentication();
|
||||
void sendTouch(float x, float y);
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "android_notificationhandler.h"
|
||||
|
||||
#include "android_controller.h"
|
||||
|
||||
AndroidNotificationHandler::AndroidNotificationHandler(QObject *parent)
|
||||
: NotificationHandler(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void AndroidNotificationHandler::notify(Message type, const QString &title, const QString &message, int timerMsec)
|
||||
{
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(timerMsec);
|
||||
// Permission is checked on the Kotlin side as well; avoid JNI if already denied.
|
||||
if (!AndroidController::instance()->isNotificationPermissionGranted()) {
|
||||
return;
|
||||
}
|
||||
AndroidController::instance()->showVpnStateNotification(title, message);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef ANDROID_NOTIFICATIONHANDLER_H
|
||||
#define ANDROID_NOTIFICATIONHANDLER_H
|
||||
|
||||
#include "ui/utils/notificationHandler.h"
|
||||
|
||||
class AndroidNotificationHandler final : public NotificationHandler {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AndroidNotificationHandler(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void notify(Message type, const QString &title, const QString &message, int timerMsec) override;
|
||||
};
|
||||
|
||||
#endif // ANDROID_NOTIFICATIONHANDLER_H
|
||||
@@ -61,6 +61,7 @@ IOSNotificationHandler::~IOSNotificationHandler() { }
|
||||
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
||||
const QString& message, int timerMsec) {
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(timerMsec);
|
||||
|
||||
if (!m_delegate) {
|
||||
return;
|
||||
@@ -71,11 +72,13 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
||||
content.body = message.toNSString();
|
||||
content.sound = [UNNotificationSound defaultSound];
|
||||
|
||||
int timerSec = timerMsec / 1000;
|
||||
NSTimeInterval delay = 0.1;
|
||||
UNTimeIntervalNotificationTrigger* trigger =
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay repeats:NO];
|
||||
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||
NSString* requestId = [NSString stringWithFormat:@"amneziavpn.vpnstate.%lld",
|
||||
(long long)([[NSDate date] timeIntervalSince1970] * 1000.0)];
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:requestId
|
||||
content:content
|
||||
trigger:trigger];
|
||||
|
||||
@@ -143,6 +146,7 @@ IOSNotificationHandler::~IOSNotificationHandler() { }
|
||||
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
||||
const QString& message, int timerMsec) {
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(timerMsec);
|
||||
|
||||
if (!m_delegate) {
|
||||
return;
|
||||
@@ -153,11 +157,13 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
||||
content.body = message.toNSString();
|
||||
content.sound = [UNNotificationSound defaultSound];
|
||||
|
||||
int timerSec = timerMsec / 1000;
|
||||
NSTimeInterval delay = 0.1;
|
||||
UNTimeIntervalNotificationTrigger* trigger =
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay repeats:NO];
|
||||
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||
NSString* requestId = [NSString stringWithFormat:@"amneziavpn.vpnstate.%lld",
|
||||
(long long)([[NSDate date] timeIntervalSince1970] * 1000.0)];
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:requestId
|
||||
content:content
|
||||
trigger:trigger];
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef MACOS_NE_VPN_NOTIFICATION_H
|
||||
#define MACOS_NE_VPN_NOTIFICATION_H
|
||||
|
||||
class QString;
|
||||
|
||||
void macosNePostVpnStateNotification(const QString &title, const QString &message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "macos_ne_vpn_notification.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QString>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
namespace {
|
||||
|
||||
@interface MacosNeVpnNotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
|
||||
@end
|
||||
|
||||
@implementation MacosNeVpnNotificationDelegate
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
|
||||
{
|
||||
Q_UNUSED(center)
|
||||
Q_UNUSED(notification)
|
||||
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler
|
||||
{
|
||||
Q_UNUSED(center)
|
||||
Q_UNUSED(response)
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
MacosNeVpnNotificationDelegate *delegateInstance()
|
||||
{
|
||||
static MacosNeVpnNotificationDelegate *d;
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
d = [[MacosNeVpnNotificationDelegate alloc] init];
|
||||
});
|
||||
return d;
|
||||
}
|
||||
|
||||
void ensureNotificationCenterSetup()
|
||||
{
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
|
||||
UNAuthorizationOptionBadge)
|
||||
completionHandler:^(BOOL granted, NSError *_Nullable error) {
|
||||
Q_UNUSED(granted);
|
||||
if (!error) {
|
||||
center.delegate = delegateInstance();
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void macosNePostVpnStateNotification(const QString &title, const QString &message)
|
||||
{
|
||||
ensureNotificationCenterSetup();
|
||||
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
content.title = title.toNSString();
|
||||
content.body = message.toNSString();
|
||||
content.sound = nil;
|
||||
|
||||
NSTimeInterval delay = 0.1;
|
||||
UNTimeIntervalNotificationTrigger *trigger =
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay repeats:NO];
|
||||
|
||||
NSString *identifier =
|
||||
[NSString stringWithFormat:@"amneziavpn.vpnstate.%lld", (long long)([[NSDate date] timeIntervalSince1970] * 1000.0)];
|
||||
|
||||
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
|
||||
content:content
|
||||
trigger:trigger];
|
||||
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center addNotificationRequest:request
|
||||
withCompletionHandler:^(NSError *_Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"macosNePostVpnStateNotification failed: %@", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
|
||||
@@ -5,16 +5,19 @@
|
||||
#include <QDebug>
|
||||
#include "notificationHandler.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID)
|
||||
# include "platforms/android/android_notificationhandler.h"
|
||||
#elif defined(Q_OS_IOS)
|
||||
# include "platforms/ios/iosnotificationhandler.h"
|
||||
#else
|
||||
# include "systemTrayNotificationHandler.h"
|
||||
#endif
|
||||
|
||||
|
||||
// static
|
||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||
#if defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID)
|
||||
return new AndroidNotificationHandler(parent);
|
||||
#elif defined(Q_OS_IOS)
|
||||
return new IOSNotificationHandler(parent);
|
||||
#else
|
||||
return new SystemTrayNotificationHandler(parent);
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
# include "platforms/macos/macosutils.h"
|
||||
#endif
|
||||
|
||||
#ifdef MACOS_NE
|
||||
# include "platforms/macos/macos_ne_vpn_notification.h"
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QIcon>
|
||||
@@ -152,6 +156,12 @@ void SystemTrayNotificationHandler::notify(NotificationHandler::Message type,
|
||||
int timerMsec) {
|
||||
Q_UNUSED(type);
|
||||
|
||||
#ifdef MACOS_NE
|
||||
Q_UNUSED(timerMsec);
|
||||
macosNePostVpnStateNotification(title, message);
|
||||
return;
|
||||
#endif
|
||||
|
||||
QIcon icon(ConnectedTrayIconName);
|
||||
m_systemTrayIcon.showMessage(title, message, icon, timerMsec);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user