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
256
client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml
Normal file
256
client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml
Normal file
|
@ -0,0 +1,256 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ProtocolEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
QtObject {
|
||||
id: routeMode
|
||||
property int allApps: 0
|
||||
property int onlyForwardApps: 1
|
||||
property int allExceptApps: 2
|
||||
}
|
||||
|
||||
property list<QtObject> routeModesModel: [
|
||||
onlyForwardApps,
|
||||
allExceptApps
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: onlyForwardApps
|
||||
property string name: qsTr("Only the Apps listed here will be accessed through the VPN")
|
||||
property int type: routeMode.onlyForwardApps
|
||||
}
|
||||
QtObject {
|
||||
id: allExceptApps
|
||||
property string name: qsTr("Apps from the list should not be accessed via VPN")
|
||||
property int type: routeMode.allExceptApps
|
||||
}
|
||||
|
||||
function getRouteModesModelIndex() {
|
||||
var currentRouteMode = AppSplitTunnelingModel.routeMode
|
||||
if ((routeMode.onlyForwardApps === currentRouteMode) || (routeMode.allApps === currentRouteMode)) {
|
||||
return 0
|
||||
} else if (routeMode.allExceptApps === currentRouteMode) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("App split tunneling")
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
|
||||
checked: AppSplitTunnelingModel.isTunnelingEnabled
|
||||
onToggled: {
|
||||
AppSplitTunnelingModel.toggleSplitTunneling(checked)
|
||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: selector
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
drawerHeight: 0.4375
|
||||
drawerParent: root
|
||||
|
||||
headerText: qsTr("Mode")
|
||||
|
||||
enabled: Qt.platform.os === "android"
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
|
||||
model: root.routeModesModel
|
||||
|
||||
currentIndex: getRouteModesModelIndex()
|
||||
|
||||
clickedFunction: function() {
|
||||
selector.text = selectedText
|
||||
selector.close()
|
||||
if (AppSplitTunnelingModel.routeMode !== root.routeModesModel[currentIndex].type) {
|
||||
AppSplitTunnelingModel.routeMode = root.routeModesModel[currentIndex].type
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.routeModesModel[currentIndex].type === AppSplitTunnelingModel.routeMode) {
|
||||
selector.text = selectedText
|
||||
} else {
|
||||
selector.text = root.routeModesModel[0].name
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AppSplitTunnelingModel
|
||||
function onRouteModeChanged() {
|
||||
currentIndex = getRouteModesModelIndex()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
|
||||
|
||||
Column {
|
||||
id: col
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
ListView {
|
||||
id: apps
|
||||
width: parent.width
|
||||
height: apps.contentItem.height
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyAppSplitTunnelingModel
|
||||
sourceModel: AppSplitTunnelingModel
|
||||
filters: RegExpFilter {
|
||||
roleName: "appPath"
|
||||
pattern: ".*" + searchField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: apps.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: appPath
|
||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Remove ") + appPath + "?"
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: addAppButton
|
||||
anchors.bottomMargin: -24
|
||||
color: "#0E0E11"
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: addAppButton
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 24
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
textFieldPlaceholderText: qsTr("application name")
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
searchField.focus = false
|
||||
PageController.showBusyIndicator(true)
|
||||
|
||||
if (Qt.platform.os === "windows") {
|
||||
var fileName = SystemController.getFileName(qsTr("Open executable file"),
|
||||
qsTr("Executable file (*.*)"))
|
||||
if (fileName !== "") {
|
||||
AppSplitTunnelingController.addApp(fileName)
|
||||
}
|
||||
} else if (Qt.platform.os === "android"){
|
||||
installedAppDrawer.open()
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstalledAppsDrawer {
|
||||
id: installedAppDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ import "../Config"
|
|||
PageType {
|
||||
id: root
|
||||
|
||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
|
@ -73,8 +75,6 @@ PageType {
|
|||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
visible: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
|
@ -87,11 +87,11 @@ PageType {
|
|||
}
|
||||
|
||||
DividerType {
|
||||
visible: GC.isDesktop()
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
visible: false
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
|
@ -100,11 +100,12 @@ PageType {
|
|||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: false
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ PageType {
|
|||
|
||||
property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi")
|
||||
|
||||
defaultActiveFocusItem: website_ip_field.textField
|
||||
defaultActiveFocusItem: searchField.textField
|
||||
|
||||
property bool pageEnabled: {
|
||||
return !ConnectionController.isConnected && !isServerFromApi
|
||||
|
@ -188,7 +188,24 @@ PageType {
|
|||
width: parent.width
|
||||
height: sites.contentItem.height
|
||||
|
||||
model: SitesModel
|
||||
model: SortFilterProxyModel {
|
||||
id: proxySitesModel
|
||||
sourceModel: SitesModel
|
||||
filters: [
|
||||
AnyOf {
|
||||
RegExpFilter {
|
||||
roleName: "url"
|
||||
pattern: ".*" + searchField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
RegExpFilter {
|
||||
roleName: "ip"
|
||||
pattern: ".*" + searchField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
@ -218,7 +235,7 @@ PageType {
|
|||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
SitesController.removeSite(index)
|
||||
SitesController.removeSite(proxySitesModel.mapToSource(index))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
@ -255,7 +272,7 @@ PageType {
|
|||
anchors.bottomMargin: 24
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: website_ip_field
|
||||
id: searchField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
|
@ -430,8 +447,4 @@ PageType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuestionDrawer {
|
||||
id: questionDrawer
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue