feature/app-split-tunneling (#702)
App Split Tunneling for Windows and Android
This commit is contained in:
parent
e7bd24f065
commit
adab30fc81
48 changed files with 1225 additions and 98 deletions
101
client/ui/models/appSplitTunnelingModel.cpp
Normal file
101
client/ui/models/appSplitTunnelingModel.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "appSplitTunnelingModel.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QAbstractListModel(parent), m_settings(settings)
|
||||
{
|
||||
auto routeMode = m_settings->getAppsRouteMode();
|
||||
if (routeMode == Settings::AppsRouteMode::VpnAllApps) {
|
||||
m_isSplitTunnelingEnabled = false;
|
||||
m_currentRouteMode = Settings::AppsRouteMode::VpnAllExceptApps;
|
||||
} else {
|
||||
m_isSplitTunnelingEnabled = true;
|
||||
m_currentRouteMode = routeMode;
|
||||
}
|
||||
m_apps = m_settings->getVpnApps(m_currentRouteMode);
|
||||
}
|
||||
|
||||
int AppSplitTunnelingModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_apps.size();
|
||||
}
|
||||
|
||||
QVariant AppSplitTunnelingModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case AppPathRole: {
|
||||
return m_apps.at(index.row()).appName;
|
||||
}
|
||||
default: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool AppSplitTunnelingModel::addApp(const InstalledAppInfo &appInfo)
|
||||
{
|
||||
if (m_apps.contains(appInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_apps.append(appInfo);
|
||||
m_settings->setVpnApps(m_currentRouteMode, m_apps);
|
||||
endInsertRows();
|
||||
|
||||
qDebug() << "app added " << appInfo.appName;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppSplitTunnelingModel::removeApp(QModelIndex index)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||
m_apps.removeAt(index.row());
|
||||
m_settings->setVpnApps(m_currentRouteMode, m_apps);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
int AppSplitTunnelingModel::getRouteMode()
|
||||
{
|
||||
return m_currentRouteMode;
|
||||
}
|
||||
|
||||
void AppSplitTunnelingModel::setRouteMode(int routeMode)
|
||||
{
|
||||
beginResetModel();
|
||||
m_settings->setAppsRouteMode(static_cast<Settings::AppsRouteMode>(routeMode));
|
||||
m_currentRouteMode = m_settings->getAppsRouteMode();
|
||||
m_apps = m_settings->getVpnApps(m_currentRouteMode);
|
||||
endResetModel();
|
||||
emit routeModeChanged();
|
||||
}
|
||||
|
||||
bool AppSplitTunnelingModel::isSplitTunnelingEnabled()
|
||||
{
|
||||
return m_isSplitTunnelingEnabled;
|
||||
}
|
||||
|
||||
void AppSplitTunnelingModel::toggleSplitTunneling(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
setRouteMode(m_currentRouteMode);
|
||||
} else {
|
||||
m_settings->setAppsRouteMode(Settings::AppsRouteMode::VpnAllApps);
|
||||
}
|
||||
m_isSplitTunnelingEnabled = enabled;
|
||||
emit splitTunnelingToggled();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AppSplitTunnelingModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[AppPathRole] = "appPath";
|
||||
return roles;
|
||||
}
|
55
client/ui/models/appSplitTunnelingModel.h
Normal file
55
client/ui/models/appSplitTunnelingModel.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef APPSPLITTUNNELINGMODEL_H
|
||||
#define APPSPLITTUNNELINGMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "settings.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class AppSplitTunnelingModel: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
AppPathRole = Qt::UserRole + 1,
|
||||
PackageAppNameRole,
|
||||
PackageAppIconRole
|
||||
};
|
||||
|
||||
explicit AppSplitTunnelingModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
|
||||
Q_PROPERTY(bool isTunnelingEnabled READ isSplitTunnelingEnabled NOTIFY splitTunnelingToggled)
|
||||
|
||||
public slots:
|
||||
bool addApp(const InstalledAppInfo &appInfo);
|
||||
void removeApp(QModelIndex index);
|
||||
|
||||
int getRouteMode();
|
||||
void setRouteMode(int routeMode);
|
||||
|
||||
bool isSplitTunnelingEnabled();
|
||||
void toggleSplitTunneling(bool enabled);
|
||||
|
||||
signals:
|
||||
void routeModeChanged();
|
||||
void splitTunnelingToggled();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
bool m_isSplitTunnelingEnabled;
|
||||
Settings::AppsRouteMode m_currentRouteMode;
|
||||
|
||||
QVector<InstalledAppInfo> m_apps;
|
||||
};
|
||||
|
||||
#endif // APPSPLITTUNNELINGMODEL_H
|
96
client/ui/models/installedAppsModel.cpp
Normal file
96
client/ui/models/installedAppsModel.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include "installedAppsModel.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
InstalledAppsModel::InstalledAppsModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int InstalledAppsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_installedApps.size();
|
||||
}
|
||||
|
||||
QVariant InstalledAppsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case AppNameRole: {
|
||||
auto appName = m_installedApps.at(index.row()).toObject().value("name").toString();
|
||||
auto packageName = m_installedApps.at(index.row()).toObject().value("package").toString();
|
||||
if (appName.isEmpty()) {
|
||||
appName = packageName;
|
||||
}
|
||||
return appName;
|
||||
}
|
||||
case AppIconRole: {
|
||||
return m_installedApps.at(index.row()).toObject().value("package").toString();
|
||||
}
|
||||
case PackageNameRole: {
|
||||
return m_installedApps.at(index.row()).toObject().value("package");
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void InstalledAppsModel::selectedStateChanged(const int index, const bool selected)
|
||||
{
|
||||
if (selected) {
|
||||
m_selectedAppIndexes.insert(index);
|
||||
} else {
|
||||
m_selectedAppIndexes.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QPair<QString, QString>> InstalledAppsModel::getSelectedAppsInfo()
|
||||
{
|
||||
QVector<QPair<QString, QString>> appsInfo;
|
||||
for (const auto i : m_selectedAppIndexes) {
|
||||
QString packageName = data(index(i, 0), PackageNameRole).toString();
|
||||
QString appName = data(index(i, 0), AppNameRole).toString();
|
||||
if (appName.isEmpty()) {
|
||||
appName = packageName;
|
||||
}
|
||||
|
||||
appsInfo.push_back({ appName, packageName });
|
||||
}
|
||||
|
||||
return appsInfo;
|
||||
}
|
||||
|
||||
void InstalledAppsModel::updateModel()
|
||||
{
|
||||
QFuture<void> future = QtConcurrent::run([this]() {
|
||||
beginResetModel();
|
||||
#ifdef Q_OS_ANDROID
|
||||
m_installedApps = AndroidController::instance()->getAppList();
|
||||
#endif
|
||||
endResetModel();
|
||||
});
|
||||
|
||||
QFutureWatcher<void> watcher;
|
||||
QEventLoop wait;
|
||||
connect(&watcher, &QFutureWatcher<void>::finished, &wait, &QEventLoop::quit);
|
||||
watcher.setFuture(future);
|
||||
wait.exec();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> InstalledAppsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[AppNameRole] = "appName";
|
||||
roles[AppIconRole] = "appIcon";
|
||||
roles[PackageNameRole] = "packageName";
|
||||
return roles;
|
||||
}
|
38
client/ui/models/installedAppsModel.h
Normal file
38
client/ui/models/installedAppsModel.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef INSTALLEDAPPSMODEL_H
|
||||
#define INSTALLEDAPPSMODEL_H
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class InstalledAppsModel: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
AppNameRole= Qt::UserRole + 1,
|
||||
PackageNameRole,
|
||||
AppIconRole
|
||||
};
|
||||
|
||||
explicit InstalledAppsModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
void selectedStateChanged(const int index, const bool selected);
|
||||
QVector<QPair<QString, QString>> getSelectedAppsInfo();
|
||||
|
||||
void updateModel();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
QJsonArray m_installedApps;
|
||||
QSet<int> m_selectedAppIndexes;
|
||||
};
|
||||
|
||||
#endif // INSTALLEDAPPSMODEL_H
|
Loading…
Add table
Add a link
Reference in a new issue