mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
refactor: refactor the application to the mvvm architecture (#2009)
* refactor: move business logic from servers model * refactor: move containersModel initialization * refactor: added protocol ui controller and removed settings class from protocols model * refactor: moved cli management to separate controller * refactor: moved app split to separate controller * refactor: moved site split to separate controller * refactor: moved allowed dns to separate controller * refactor: moved language logic to separate ui controller * refactor: removed Settings from devices model * refactor: moved configs and services api logit to separate core controller * refactor: added a layer with a repository between the storage and controllers * refactor: use child parent system instead of smart pointers for controllers and models initialization * refactor: moved install functions from server controller to install controller * refactor: install controller refactoring * chore: renamed exportController to exportUiController * refactor: separate export controller * refactor: removed VpnConfigurationsController * chore: renamed ServerController to SshSession * refactor: replaced ServerController to SshSession * chore: moved qml controllers to separate folder * chore: include fixes * chore: moved utils from core root to core/utils * chore: include fixes * chore: rename core/utils files to camelCase foramt * chore: include fixes * chore: moved some utils to api and selfhosted folders * chore: include fixes * chore: remove unused file * chore: moved serialization folder to core/utils * chore: include fixes * chore: moved some files from client root to core/utils * chore: include fixes * chore: moved ui utils to ui/utils folder * chore: include fixes * chore: move utils from root to ui/utils * chore: include fixes * chore: moved configurators to core/configurators * chore: include fixes * refactor: moved iap logic from ui controller to core * refactor: moved remaining core logic from ApiConfigsController to SubscriptionController * chore: rename apiNewsController to apiNewsUiController * refactor: moved core logic from news ui controller to core * chore: renamed apiConfigsController to subscriptionUiController * chore: include fixes * refactor: merge ApiSettingsController with SubscriptionUiController * chore: moved ui selfhosted controllers to separate folder * chore: include fixes * chore: rename connectionController to connectiomUiController * refactor: moved core logic from connectionUiController * chore: rename settingsController to settingsUiController * refactor: move core logic from settingsUiController * refactor: moved core controller signal/slot connections to separate class * fix: newsController fixes after refactoring * chore: rename model to camelCase * chore: include fixes * chore: remove unused code * chore: move selfhosted core to separate folder * chore: include fixes * chore: rename importController to importUiController * refactor: move core logic from importUiController * chore: minor fixes * chore: remove prem v1 migration * refactor: remove openvpn over cloak and openvpn over shadowsocks * refactor: removed protocolsForContainer function * refactor: add core models * refactor: replace json with c++ structs for server config * refactor: move getDnsPair to ServerConfigUtils * feat: add admin selfhosted config export test * feat: add multi import test * refactor: use coreController for tests * feat: add few simple tests * chore: qrepos in all core controllers * feat: add test for settings * refactor: remove repo dependency from configurators * chore: moved protocols to core folder * chore: include fixes * refactor: moved containersDefs, defs, apiDefs, protocolsDefs to different places * chore: include fixes * chore: build fixes * chore: build fixes * refactor: remove q repo and interface repo * feat: add test for ui servers model and controller * chore: renamed to camelCase * chore: include fixes * refactor: moved core logic from sites ui controller * fix: fixed api config processing * fix: fixed processed server index processing * refactor: protocol models now use c++ structs instead of json configs * refactor: servers model now use c++ struct instead of json config * fix: fixed default server index processing * fix: fix logs init * fix: fix secure settings load keys * chore: build fixes * fix: fixed clear settings * fix: fixed restore backup * fix: sshSession usage * fix: fixed export functions signatures * fix: return missing part from buildContainerWorker * fix: fixed server description on page home * refactor: add container config helpers functions * refactor: c++ structs instead of json * chore: add dns protocol config struct * refactor: move config utils functions to config structs * feat: add test for selfhosted server setup * refactor: separate resources.qrc * fix: fixed server rename * chore: return nameOverriddenByUser * fix: build fixes * fix: fixed models init * refactor: cleanup models usage * fix: fixed models init * chore: cleanup connections and functions signatures * chore: cleanup updateModel calls * feat: added cache to servers repo * chore: cleanup unused functions * chore: ssxray processing * chore: remove transportProtoWithDefault and portWithDefault functions * chore: removed proto types any and l2tp * refactor: moved some constants * fix: fixed native configs export * refactor: remove json from processConfigWith functions * fix: fixed processed server index usage * fix: qml warning fixes * chore: merge fixes * chore: update tests * fix: fixed xray config processing * fix: fixed split tunneling processing * chore: rename sites controllers and model * chore: rename fixes * chore: minor fixes * chore: remove ability to load backup from "file with connection settings" button * fix: fixed api device revoke * fix: remove full model update when renaming a user * fix: fixed premium/free server rename * fix: fixed selfhosted new server install * fix: fixed updateContainer function * fix: fixed revoke for external premium configs * feat: add native configs qr processing * chore: codestyle fixes * fix: fixed admin config create * chore: again remove ability to load backup from "file with connection settings" button * chore: minor fixes * fix: fixed variables initialization * fix: fixed qml imports * fix: minor fixes * fix: fix vpnConnection function calls * feat: add buckup error handling * fix: fixed admin config revok * fix: fixed selfhosted awg installation * fix: ad visability * feat: add empty check for primary dns * chore: minor fixes
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
template<typename Enum>
|
||||
QString enumToString(Enum value)
|
||||
{
|
||||
auto metaEnum = QMetaEnum::fromType<Enum>();
|
||||
return metaEnum.valueToKey(static_cast<int>(value));
|
||||
}
|
||||
|
||||
template<typename Enum>
|
||||
Enum enumFromString(const QString &str, Enum defaultValue = {})
|
||||
{
|
||||
auto metaEnum = QMetaEnum::fromType<Enum>();
|
||||
bool isOk;
|
||||
auto value = metaEnum.keyToValue(str.toLatin1(), &isOk);
|
||||
if (isOk) {
|
||||
return static_cast<Enum>(value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef OSXUTIL_H
|
||||
#define OSXUTIL_H
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
|
||||
void setDockIconVisible(bool visible);
|
||||
void fixWidget(QWidget *widget);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "macosUtil.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// void setDockIconVisible(bool visible)
|
||||
//{
|
||||
// QProcess process;
|
||||
// process.start(
|
||||
// "osascript",
|
||||
// { "-e tell application \"System Events\" to get properties of (get application process \"AmneziaVPN\")" });
|
||||
// process.waitForFinished(3000);
|
||||
// const auto output = QString::fromLocal8Bit(process.readAllStandardOutput());
|
||||
|
||||
// qDebug() << output;
|
||||
|
||||
// if (visible) {
|
||||
// [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||
// } else {
|
||||
// [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
// }
|
||||
//}
|
||||
|
||||
void setDockIconVisible(bool visible)
|
||||
{
|
||||
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
||||
if (visible) {
|
||||
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
||||
} else {
|
||||
TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
|
||||
}
|
||||
}
|
||||
|
||||
// this Objective-c class is used to override the action of system close button and zoom button
|
||||
// https://stackoverflow.com/questions/27643659/setting-c-function-as-selector-for-nsbutton-produces-no-results
|
||||
@interface ButtonPasser : NSObject {
|
||||
}
|
||||
@property (readwrite) QMainWindow *window;
|
||||
+ (void)closeButtonAction:(id)sender;
|
||||
- (void)zoomButtonAction:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation ButtonPasser {
|
||||
}
|
||||
+ (void)closeButtonAction:(id)sender
|
||||
{
|
||||
Q_UNUSED(sender);
|
||||
ProcessSerialNumber pn;
|
||||
GetFrontProcess(&pn);
|
||||
ShowHideProcess(&pn, false);
|
||||
}
|
||||
- (void)zoomButtonAction:(id)sender
|
||||
{
|
||||
Q_UNUSED(sender);
|
||||
if (0 == self.window)
|
||||
return;
|
||||
if (self.window->isMaximized())
|
||||
self.window->showNormal();
|
||||
else
|
||||
self.window->showMaximized();
|
||||
}
|
||||
@end
|
||||
|
||||
void fixWidget(QWidget *widget)
|
||||
{
|
||||
NSView *view = (NSView *)widget->winId();
|
||||
if (0 == view)
|
||||
return;
|
||||
NSWindow *window = view.window;
|
||||
if (0 == window)
|
||||
return;
|
||||
|
||||
// override the action of close button
|
||||
// https://stackoverflow.com/questions/27643659/setting-c-function-as-selector-for-nsbutton-produces-no-results
|
||||
// https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/Target-Action/Target-Action.html
|
||||
// NSButton *closeButton = [window standardWindowButton:NSWindowCloseButton];
|
||||
// [closeButton setTarget:[ButtonPasser class]];
|
||||
// [closeButton setAction:@selector(closeButtonAction:)];
|
||||
|
||||
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
|
||||
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef NENOTIFICATIONHANDLER_H
|
||||
#define NENOTIFICATIONHANDLER_H
|
||||
|
||||
#include "notificationHandler.h"
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
class MacOSStatusIcon;
|
||||
|
||||
class NEStatusBarNotificationHandler : public NotificationHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NEStatusBarNotificationHandler(QObject* parent);
|
||||
~NEStatusBarNotificationHandler() override;
|
||||
|
||||
void setConnectionState(Vpn::ConnectionState state) override;
|
||||
void onTranslationsUpdated() override;
|
||||
|
||||
protected:
|
||||
void notify(Message type, const QString& title,
|
||||
const QString& message, int timerMsec) override;
|
||||
|
||||
private:
|
||||
void buildMenu();
|
||||
|
||||
QMenu m_menu;
|
||||
MacOSStatusIcon* m_statusIcon;
|
||||
|
||||
QAction* m_actionShow;
|
||||
QAction* m_actionConnect;
|
||||
QAction* m_actionDisconnect;
|
||||
QAction* m_actionVisitWebsite;
|
||||
QAction* m_actionQuit;
|
||||
};
|
||||
|
||||
#endif // NENOTIFICATIONHANDLER_H
|
||||
@@ -0,0 +1,113 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <QDebug>
|
||||
#include "notificationHandler.h"
|
||||
|
||||
#if 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)
|
||||
return new IOSNotificationHandler(parent);
|
||||
#else
|
||||
return new SystemTrayNotificationHandler(parent);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
NotificationHandler* s_instance = nullptr;
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
NotificationHandler* NotificationHandler::instance() {
|
||||
Q_ASSERT(s_instance);
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
NotificationHandler::NotificationHandler(QObject* parent) : QObject(parent) {
|
||||
Q_ASSERT(!s_instance);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
NotificationHandler::~NotificationHandler() {
|
||||
Q_ASSERT(s_instance == this);
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
void NotificationHandler::setConnectionState(Vpn::ConnectionState state)
|
||||
{
|
||||
if (state != Vpn::ConnectionState::Connected && state != Vpn::ConnectionState::Disconnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString title;
|
||||
QString message;
|
||||
|
||||
switch (state) {
|
||||
case Vpn::ConnectionState::Connected:
|
||||
m_connected = true;
|
||||
|
||||
title = tr("AmneziaVPN");
|
||||
message = tr("VPN Connected");
|
||||
break;
|
||||
|
||||
case Vpn::ConnectionState::Disconnected:
|
||||
if (m_connected) {
|
||||
m_connected = false;
|
||||
title = tr("AmneziaVPN");
|
||||
message = tr("VPN Disconnected");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT(title.isEmpty() == message.isEmpty());
|
||||
|
||||
if (!title.isEmpty()) {
|
||||
notifyInternal(VpnState, title, message, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationHandler::onTranslationsUpdated()
|
||||
{
|
||||
}
|
||||
|
||||
void NotificationHandler::unsecuredNetworkNotification(const QString& networkName) {
|
||||
qDebug() << "Unsecured network notification shown";
|
||||
|
||||
|
||||
QString title = tr("AmneziaVPN notification");
|
||||
QString message = tr("Unsecured network detected: ") + networkName;
|
||||
|
||||
notifyInternal(UnsecuredNetwork, title, message, 2000);
|
||||
}
|
||||
|
||||
void NotificationHandler::notifyInternal(Message type, const QString& title,
|
||||
const QString& message,
|
||||
int timerMsec) {
|
||||
m_lastMessage = type;
|
||||
|
||||
emit notificationShown(title, message);
|
||||
notify(type, title, message, timerMsec);
|
||||
}
|
||||
|
||||
void NotificationHandler::messageClickHandle() {
|
||||
qDebug() << "Message clicked";
|
||||
|
||||
if (m_lastMessage == VpnState) {
|
||||
qCritical() << "Random message clicked received";
|
||||
return;
|
||||
}
|
||||
|
||||
emit notificationClicked(m_lastMessage);
|
||||
m_lastMessage = VpnState;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NOTIFICATIONHANDLER_H
|
||||
#define NOTIFICATIONHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include "core/protocols/vpnProtocol.h"
|
||||
|
||||
class QMenu;
|
||||
|
||||
class NotificationHandler : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NotificationHandler)
|
||||
|
||||
public:
|
||||
enum Message {
|
||||
VpnState,
|
||||
UnsecuredNetwork
|
||||
};
|
||||
|
||||
static NotificationHandler* create(QObject* parent);
|
||||
|
||||
static NotificationHandler* instance();
|
||||
|
||||
virtual ~NotificationHandler();
|
||||
|
||||
void unsecuredNetworkNotification(const QString& networkName);
|
||||
|
||||
void messageClickHandle();
|
||||
|
||||
public slots:
|
||||
virtual void setConnectionState(Vpn::ConnectionState state);
|
||||
virtual void onTranslationsUpdated();
|
||||
|
||||
signals:
|
||||
void notificationShown(const QString& title, const QString& message);
|
||||
void notificationClicked(Message message);
|
||||
|
||||
void raiseRequested();
|
||||
void connectRequested();
|
||||
void disconnectRequested();
|
||||
|
||||
protected:
|
||||
explicit NotificationHandler(QObject* parent);
|
||||
|
||||
virtual void notify(Message type, const QString& title,
|
||||
const QString& message, int timerMsec) = 0;
|
||||
|
||||
private:
|
||||
virtual void notifyInternal(Message type, const QString& title,
|
||||
const QString& message, int timerMsec);
|
||||
|
||||
protected:
|
||||
Message m_lastMessage = VpnState;
|
||||
|
||||
private:
|
||||
|
||||
// We want to show a 'disconnected' notification only if we were actually
|
||||
// connected.
|
||||
bool m_connected = false;
|
||||
};
|
||||
|
||||
#endif // NOTIFICATIONHANDLER_H
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef PAGES_H
|
||||
#define PAGES_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class PageType : public QObject
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
Basic,
|
||||
Proto,
|
||||
ShareProto,
|
||||
ClientInfo
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
};
|
||||
|
||||
namespace PageEnumNS
|
||||
{
|
||||
Q_NAMESPACE
|
||||
enum class Page { Start = 0, NewServer, NewServerProtocols, Vpn,
|
||||
Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress,
|
||||
GeneralSettings, AppSettings, NetworkSettings, ServerSettings,
|
||||
ServerContainers, ServersList, ShareConnection, Sites,
|
||||
ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig,
|
||||
AdvancedServerSettings, ClientManagement, ClientInfo};
|
||||
Q_ENUM_NS(Page)
|
||||
|
||||
static void declareQmlPageEnum() {
|
||||
qmlRegisterUncreatableMetaObject(
|
||||
PageEnumNS::staticMetaObject,
|
||||
"PageEnum",
|
||||
1, 0,
|
||||
"PageEnum",
|
||||
"Error: only enums"
|
||||
);
|
||||
}
|
||||
|
||||
} // PAGES_H
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "qAutoStart.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTextStream>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#if defined (Q_OS_WIN)
|
||||
#define REG_KEY "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
QSettings settings(REG_KEY, QSettings::NativeFormat);
|
||||
|
||||
if (settings.value(appName()).isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
QSettings settings(REG_KEY, QSettings::NativeFormat);
|
||||
|
||||
if (autostart) {
|
||||
settings.setValue(appName() , appPath().replace('/','\\'));
|
||||
} else {
|
||||
settings.remove(appName());
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QCoreApplication::applicationFilePath() + " --autostart";
|
||||
}
|
||||
|
||||
#elif defined Q_OS_MACX
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
QProcess process;
|
||||
process.start("osascript", {
|
||||
"-e tell application \"System Events\" to get the path of every login item"
|
||||
});
|
||||
process.waitForFinished(3000);
|
||||
const auto output = QString::fromLocal8Bit(process.readAllStandardOutput());
|
||||
return output.contains(appPath());
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
// Remove any existing login entry for this app first, in case there was one
|
||||
// from a previous installation, that may be under a different launch path.
|
||||
{
|
||||
QProcess::execute("osascript", {
|
||||
"-e tell application \"System Events\" to delete every login item whose name is \"" + appName() + "\""
|
||||
});
|
||||
}
|
||||
|
||||
// Now install the login item, if needed.
|
||||
if ( autostart )
|
||||
{
|
||||
QProcess::execute("osascript", {
|
||||
"-e tell application \"System Events\" to make login item at end with properties {path:\"" + appPath() + "\", hidden:true, name: \"" + appName() + "\"}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
QDir appDir = QDir(QCoreApplication::applicationDirPath());
|
||||
appDir.cdUp();
|
||||
appDir.cdUp();
|
||||
QString absolutePath = appDir.absolutePath();
|
||||
|
||||
return absolutePath;
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_LINUX)
|
||||
bool Autostart::isAutostart() {
|
||||
QFileInfo check_file(QDir::homePath() + "/.config/autostart/" + appName() +".desktop");
|
||||
|
||||
if (check_file.exists() && check_file.isFile()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
QString path = QDir::homePath() + "/.config/autostart/";
|
||||
QString name = appName() +".desktop";
|
||||
QFile file(path+name);
|
||||
|
||||
file.remove();
|
||||
|
||||
if(autostart) {
|
||||
QDir dir(path);
|
||||
if(!dir.exists()) {
|
||||
dir.mkpath(path);
|
||||
}
|
||||
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << "[Desktop Entry]" << Qt::endl;
|
||||
stream << "Exec=AmneziaVPN" << Qt::endl;
|
||||
stream << "Type=Application" << Qt::endl;
|
||||
stream << "Name=AmneziaVPN" << Qt::endl;
|
||||
stream << "Comment=Client of your self-hosted VPN" << Qt::endl;
|
||||
stream << "Icon=/usr/share/pixmaps/AmneziaVPN.png" << Qt::endl;
|
||||
stream << "Categories=Network;Qt;Security;" << Qt::endl;
|
||||
stream << "Terminal=false" << Qt::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QCoreApplication::applicationFilePath() + " --autostart";
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
Q_UNUSED(autostart);
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QString();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString Autostart::appName() {
|
||||
return QCoreApplication::applicationName();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#ifndef AUTOSTART_H
|
||||
#define AUTOSTART_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Autostart
|
||||
{
|
||||
public:
|
||||
static bool isAutostart();
|
||||
static void setAutostart(bool autostart);
|
||||
|
||||
protected:
|
||||
static QString appPath();
|
||||
static QString appName();
|
||||
};
|
||||
|
||||
#endif // AUTOSTART_H
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "qmlUtils.h"
|
||||
|
||||
#include <QPointF>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
|
||||
namespace FocusControl
|
||||
{
|
||||
QPointF getItemCenterPointOnScene(QQuickItem *item)
|
||||
{
|
||||
const auto x0 = item->x() + (item->width() / 2);
|
||||
const auto y0 = item->y() + (item->height() / 2);
|
||||
return item->parentItem()->mapToScene(QPointF { x0, y0 });
|
||||
}
|
||||
|
||||
bool isEnabled(QObject *obj)
|
||||
{
|
||||
const auto item = qobject_cast<QQuickItem *>(obj);
|
||||
return item && item->isEnabled();
|
||||
}
|
||||
|
||||
bool isVisible(QObject *item)
|
||||
{
|
||||
const auto res = item->property("visible").toBool();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isFocusable(QObject *item)
|
||||
{
|
||||
const auto res = item->property("isFocusable").toBool();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isListView(QObject *item)
|
||||
{
|
||||
return item->inherits("QQuickListView");
|
||||
}
|
||||
|
||||
bool isOnTheScene(QObject *object)
|
||||
{
|
||||
QQuickItem *item = qobject_cast<QQuickItem *>(object);
|
||||
if (!item) {
|
||||
qWarning() << "Couldn't recognize object as item";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item->isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QRectF itemRect = item->mapRectToScene(item->childrenRect());
|
||||
|
||||
QQuickWindow *window = item->window();
|
||||
if (!window) {
|
||||
qWarning() << "Couldn't get the window on the Scene check";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto contentItem = window->contentItem();
|
||||
if (!contentItem) {
|
||||
qWarning() << "Couldn't get the content item on the Scene check";
|
||||
return false;
|
||||
}
|
||||
QRectF windowRect = contentItem->childrenRect();
|
||||
const auto res = (windowRect.contains(itemRect) || isListView(item));
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isMore(QObject *item1, QObject *item2)
|
||||
{
|
||||
return !isLess(item1, item2);
|
||||
}
|
||||
|
||||
bool isLess(QObject *item1, QObject *item2)
|
||||
{
|
||||
const auto p1 = getItemCenterPointOnScene(qobject_cast<QQuickItem *>(item1));
|
||||
const auto p2 = getItemCenterPointOnScene(qobject_cast<QQuickItem *>(item2));
|
||||
return (p1.y() == p2.y()) ? (p1.x() < p2.x()) : (p1.y() < p2.y());
|
||||
}
|
||||
|
||||
QList<QObject *> getSubChain(QObject *object)
|
||||
{
|
||||
QList<QObject *> res;
|
||||
if (!object) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto children = object->children();
|
||||
|
||||
for (const auto child : children) {
|
||||
if (child && isFocusable(child) && isOnTheScene(child) && isEnabled(child)) {
|
||||
res.append(child);
|
||||
} else {
|
||||
res.append(getSubChain(child));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QList<QObject *> getItemsChain(QObject *object)
|
||||
{
|
||||
QList<QObject *> res;
|
||||
if (!object) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto children = object->children();
|
||||
|
||||
for (const auto child : children) {
|
||||
if (child && isFocusable(child) && isEnabled(child) && isVisible(child)) {
|
||||
res.append(child);
|
||||
} else {
|
||||
res.append(getItemsChain(child));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void printItems(const QList<QObject *> &items, QObject *current_item)
|
||||
{
|
||||
for (const auto &item : items) {
|
||||
QQuickItem *i = qobject_cast<QQuickItem *>(item);
|
||||
QPointF coords { getItemCenterPointOnScene(i) };
|
||||
QString prefix = current_item == i ? "==>" : " ";
|
||||
qDebug() << prefix << " Item: " << i << " with coords: " << coords;
|
||||
}
|
||||
}
|
||||
} // namespace FocusControl
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef FOCUSCONTROL_H
|
||||
#define FOCUSCONTROL_H
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
namespace FocusControl
|
||||
{
|
||||
bool isEnabled(QObject *item);
|
||||
bool isVisible(QObject *item);
|
||||
bool isFocusable(QObject *item);
|
||||
bool isListView(QObject *item);
|
||||
bool isOnTheScene(QObject *object);
|
||||
bool isMore(QObject *item1, QObject *item2);
|
||||
bool isLess(QObject *item1, QObject *item2);
|
||||
|
||||
/*!
|
||||
* \brief Make focus chain of elements which are on the scene
|
||||
*/
|
||||
QList<QObject *> getSubChain(QObject *object);
|
||||
|
||||
/*!
|
||||
* \brief Make focus chain of elements which could be not on the scene
|
||||
*/
|
||||
QList<QObject *> getItemsChain(QObject *object);
|
||||
|
||||
void printItems(const QList<QObject *> &items, QObject *current_item);
|
||||
} // namespace FocusControl
|
||||
|
||||
#endif // FOCUSCONTROL_H
|
||||
@@ -0,0 +1,173 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <QDebug>
|
||||
#include "systemTrayNotificationHandler.h"
|
||||
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
# include "platforms/macos/macosutils.h"
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QIcon>
|
||||
#include <QWindow>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) :
|
||||
NotificationHandler(parent),
|
||||
m_systemTrayIcon(parent)
|
||||
|
||||
{
|
||||
m_systemTrayIcon.show();
|
||||
connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &SystemTrayNotificationHandler::onTrayActivated);
|
||||
|
||||
m_trayActionShow = m_menu.addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, [this](){
|
||||
emit raiseRequested();
|
||||
});
|
||||
m_menu.addSeparator();
|
||||
m_trayActionConnect = m_menu.addAction(tr("Connect"), this, [this](){ emit connectRequested(); });
|
||||
m_trayActionDisconnect = m_menu.addAction(tr("Disconnect"), this, [this](){ emit disconnectRequested(); });
|
||||
|
||||
m_menu.addSeparator();
|
||||
|
||||
m_trayActionVisitWebSite = m_menu.addAction(QIcon(":/images/tray/link.png"), tr("Visit Website"), [&](){
|
||||
QDesktopServices::openUrl(QUrl(websiteUrl));
|
||||
});
|
||||
|
||||
// Quit action: disconnect VPN first on macOS NE, else quit directly
|
||||
m_trayActionQuit = m_menu.addAction(QIcon(":/images/tray/cancel.png"),
|
||||
tr("Quit") + " " + APPLICATION_NAME,
|
||||
this,
|
||||
[&](){ qApp->quit(); });
|
||||
|
||||
m_systemTrayIcon.setContextMenu(&m_menu);
|
||||
setTrayState(Vpn::ConnectionState::Disconnected);
|
||||
}
|
||||
|
||||
SystemTrayNotificationHandler::~SystemTrayNotificationHandler() {
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::setConnectionState(Vpn::ConnectionState state)
|
||||
{
|
||||
setTrayState(state);
|
||||
NotificationHandler::setConnectionState(state);
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::onTranslationsUpdated()
|
||||
{
|
||||
m_trayActionShow->setText(tr("Show") + " " + APPLICATION_NAME);
|
||||
m_trayActionConnect->setText(tr("Connect"));
|
||||
m_trayActionDisconnect->setText(tr("Disconnect"));
|
||||
m_trayActionVisitWebSite->setText(tr("Visit Website"));
|
||||
m_trayActionQuit->setText(tr("Quit")+ " " + APPLICATION_NAME);
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::updateWebsiteUrl(const QString &newWebsiteUrl) {
|
||||
qDebug() << "Updated website URL:" << newWebsiteUrl;
|
||||
websiteUrl = newWebsiteUrl;
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::setTrayIcon(const QString &iconPath)
|
||||
{
|
||||
QIcon trayIconMask(QPixmap(iconPath).scaled(128,128));
|
||||
#ifndef Q_OS_MAC
|
||||
trayIconMask.setIsMask(true);
|
||||
#endif
|
||||
m_systemTrayIcon.setIcon(trayIconMask);
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
#ifndef Q_OS_MAC
|
||||
if(reason == QSystemTrayIcon::DoubleClick || reason == QSystemTrayIcon::Trigger) {
|
||||
emit raiseRequested();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::setTrayState(Vpn::ConnectionState state)
|
||||
{
|
||||
QString resourcesPath = ":/images/tray/%1";
|
||||
|
||||
switch (state) {
|
||||
case Vpn::ConnectionState::Disconnected:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(true);
|
||||
m_trayActionDisconnect->setEnabled(false);
|
||||
break;
|
||||
case Vpn::ConnectionState::Preparing:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case Vpn::ConnectionState::Connecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case Vpn::ConnectionState::Connected:
|
||||
setTrayIcon(QString(resourcesPath).arg(ConnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case Vpn::ConnectionState::Disconnecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case Vpn::ConnectionState::Reconnecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case Vpn::ConnectionState::Error:
|
||||
setTrayIcon(QString(resourcesPath).arg(ErrorTrayIconName));
|
||||
m_trayActionConnect->setEnabled(true);
|
||||
m_trayActionDisconnect->setEnabled(false);
|
||||
break;
|
||||
case Vpn::ConnectionState::Unknown:
|
||||
default:
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
}
|
||||
|
||||
//#ifdef Q_OS_MAC
|
||||
// // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user )
|
||||
// bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme();
|
||||
// darkTaskBar = forceUseBrightIcons ? true : darkTaskBar;
|
||||
// resourcesPath = ":/images_mac/tray_icon/%1";
|
||||
// useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png");
|
||||
//#endif
|
||||
}
|
||||
|
||||
|
||||
void SystemTrayNotificationHandler::notify(NotificationHandler::Message type,
|
||||
const QString& title,
|
||||
const QString& message,
|
||||
int timerMsec) {
|
||||
Q_UNUSED(type);
|
||||
|
||||
QIcon icon(ConnectedTrayIconName);
|
||||
m_systemTrayIcon.showMessage(title, message, icon, timerMsec);
|
||||
}
|
||||
|
||||
void SystemTrayNotificationHandler::showHideWindow() {
|
||||
// QmlEngineHolder* engine = QmlEngineHolder::instance();
|
||||
// if (engine->window()->isVisible()) {
|
||||
// engine->hideWindow();
|
||||
//#ifdef MVPN_MACOS
|
||||
// MacOSUtils::hideDockIcon();
|
||||
//#endif
|
||||
// } else {
|
||||
// engine->showWindow();
|
||||
//#ifdef MVPN_MACOS
|
||||
// MacOSUtils::showDockIcon();
|
||||
//#endif
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SYSTEMTRAYNOTIFICATIONHANDLER_H
|
||||
#define SYSTEMTRAYNOTIFICATIONHANDLER_H
|
||||
|
||||
#include "notificationHandler.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
class SystemTrayNotificationHandler : public NotificationHandler {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SystemTrayNotificationHandler(QObject* parent);
|
||||
~SystemTrayNotificationHandler();
|
||||
|
||||
void setConnectionState(Vpn::ConnectionState state) override;
|
||||
|
||||
void onTranslationsUpdated() override;
|
||||
|
||||
public slots:
|
||||
void updateWebsiteUrl(const QString &newWebsiteUrl);
|
||||
|
||||
protected:
|
||||
virtual void notify(Message type, const QString& title,
|
||||
const QString& message, int timerMsec) override;
|
||||
|
||||
private:
|
||||
void showHideWindow();
|
||||
|
||||
void setTrayState(Vpn::ConnectionState state);
|
||||
void onTrayActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
|
||||
void setTrayIcon(const QString &iconPath);
|
||||
|
||||
private:
|
||||
QMenu m_menu;
|
||||
QSystemTrayIcon m_systemTrayIcon;
|
||||
|
||||
QAction* m_trayActionShow = nullptr;
|
||||
QAction* m_trayActionConnect = nullptr;
|
||||
QAction* m_trayActionDisconnect = nullptr;
|
||||
QAction* m_trayActionVisitWebSite = nullptr;
|
||||
QAction* m_trayActionQuit = nullptr;
|
||||
QAction* m_statusLabel = nullptr;
|
||||
QAction* m_separator = nullptr;
|
||||
|
||||
const QString ConnectedTrayIconName = "active.png";
|
||||
const QString DisconnectedTrayIconName = "default.png";
|
||||
const QString ErrorTrayIconName = "error.png";
|
||||
QString websiteUrl = "https://amnezia.org";
|
||||
};
|
||||
|
||||
#endif // SYSTEMTRAYNOTIFICATIONHANDLER_H
|
||||
Reference in New Issue
Block a user