feat: add UI to protocol auto-switching

This commit is contained in:
NickVs2015
2025-12-26 18:32:55 +03:00
parent d7f301ea3b
commit 65ba95f344
9 changed files with 409 additions and 2 deletions
@@ -752,6 +752,12 @@ bool SubscriptionController::isVlessProtocol(const QString &serverId) const
return apiV2.has_value() && apiV2->serviceProtocol() == "vless";
}
bool SubscriptionController::isAwgProtocol(const QString &serverId) const
{
auto apiV2 = m_serversRepository->apiV2Config(serverId);
return apiV2.has_value() && apiV2->serviceProtocol() == "awg";
}
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId,
int *duplicateServerIndex)
@@ -86,6 +86,7 @@ public:
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(const QString &serverId) const;
bool isAwgProtocol(const QString &serverId) const;
ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
@@ -510,6 +510,11 @@ bool SubscriptionUiController::isVlessProtocol(const QString &serverId)
return m_subscriptionController->isVlessProtocol(serverId);
}
bool SubscriptionUiController::isAwgProtocol(const QString &serverId)
{
return m_subscriptionController->isAwgProtocol(serverId);
}
void SubscriptionUiController::removeApiConfig(const QString &serverId)
{
@@ -57,6 +57,7 @@ public slots:
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(const QString &serverId);
bool isAwgProtocol(const QString &serverId);
bool isCaptchaAwaitingUser() const;
void onCaptchaSolved(const QString &captchaId, const QString &solution);
@@ -45,6 +45,8 @@ namespace PageLoader
PageSettingsApiDevices,
PageSettingsApiSubscriptionKey,
PageSettingsKillSwitchExceptions,
PageSettingsConnectionType,
PageSettingsConnectionProtocols,
PageServiceSftpSettings,
PageServiceTorWebsiteSettings,
@@ -296,6 +296,7 @@ PageType {
visible: footer.isVisibleForAmneziaFree
}
WarningType {
id: warning
@@ -319,12 +320,26 @@ PageType {
}
}
LabelWithButtonType {
id: connectionSwitcher
Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32
text: qsTr("Connection")
descriptionText: qsTr("Protocol selection and local proxy setup")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsConnectionType)
}
}
DividerType {}
LabelWithButtonType {
id: vpnKey
Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 0
visible: footer.isVisibleForAmneziaFree
text: qsTr("Subscription Key")
@@ -0,0 +1,283 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
PageType {
id: root
Timer {
id: updateProtocolTimer
interval: 100
repeat: false
property string savedProtocol: ""
onTriggered: {
if (!root || !root.visible) {
PageController.showBusyIndicator(false)
return
}
SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true)
// After update, restore the protocol we set (because updateServiceFromGateway may overwrite it)
if (savedProtocol !== "") {
Qt.callLater(function() {
try {
if (!root || !root.visible) {
PageController.showBusyIndicator(false)
return
}
var protocolToSet = savedProtocol === "auto" ? "" : savedProtocol
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), protocolToSet)
PageController.showBusyIndicator(false)
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error updating protocol display:", e)
}
})
} catch (e) {
console.log("Error in updateProtocolTimer:", e)
PageController.showBusyIndicator(false)
}
})
} else {
PageController.showBusyIndicator(false)
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error updating protocol display:", e)
}
})
}
}
}
function getCurrentProtocol() {
try {
if (SubscriptionUiController.isVlessProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex))) {
return "vless"
}
if (SubscriptionUiController.isAwgProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex))) {
return "awg"
}
// If neither VLESS nor AWG, it's auto mode
return "auto"
} catch (e) {
console.log("Error getting current protocol:", e)
return "auto"
}
}
property string currentProtocol: "auto"
Connections {
target: ServersModel
function onDataChanged() {
if (!root || !root.visible) {
return
}
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error in ServersModel.onDataChanged:", e)
}
})
}
}
Connections {
target: SubscriptionUiController
function onUpdateServerFromApiFinished() {
if (!root || !root.visible) {
return
}
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error in ApiConfigsController.onUpdateServerFromApiFinished:", e)
}
})
}
}
Component.onCompleted: {
root.currentProtocol = root.getCurrentProtocol()
}
onVisibleChanged: {
if (visible) {
try {
if (typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error in onVisibleChanged:", e)
}
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
flickable.contentY = 0
}
}
}
FlickableType {
id: flickable
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
headerText: qsTr("VPN Protocol")
}
ButtonGroup {
id: protocolButtonGroup
}
VerticalRadioButton {
id: autoProtocolButton
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
ButtonGroup.group: protocolButtonGroup
checked: root.currentProtocol === "auto"
enabled: !ConnectionController.isConnected || !ServersModel.isDefaultServerCurrentlyProcessed()
text: qsTr("Choose automatically")
descriptionText: qsTr("AmneziaWG is used by default. If the connection is unstable, the app will switch to VLESS. It won't switch back automatically")
onClicked: function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "")
updateProtocolTimer.savedProtocol = "auto"
updateProtocolTimer.start()
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
DividerType {}
VerticalRadioButton {
id: awgProtocolButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
ButtonGroup.group: protocolButtonGroup
checked: root.currentProtocol === "awg"
enabled: !ConnectionController.isConnected || !ServersModel.isDefaultServerCurrentlyProcessed()
text: qsTr("AmneziaWG")
onClicked: function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "awg")
updateProtocolTimer.savedProtocol = "awg"
updateProtocolTimer.start()
root.currentProtocol = "awg"
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
DividerType {}
VerticalRadioButton {
id: vlessProtocolButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
ButtonGroup.group: protocolButtonGroup
checked: root.currentProtocol === "vless"
enabled: !ConnectionController.isConnected || !ServersModel.isDefaultServerCurrentlyProcessed()
text: qsTr("XRay VLESS Reality")
onClicked: function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "vless")
updateProtocolTimer.savedProtocol = "vless"
updateProtocolTimer.start()
root.currentProtocol = "vless"
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
DividerType {}
}
}
}
@@ -0,0 +1,92 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
PageType {
id: root
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Connection")
}
}
model: 1
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType {
id: vpnProtocolButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("VPN protocol")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsConnectionProtocols)
}
}
DividerType {}
LabelWithButtonType {
id: localProxyButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Local proxy")
descriptionText: qsTr("Running: 127.0.0.1:1080")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
// Placeholder - no action
}
}
DividerType {}
}
}
}
+2
View File
@@ -102,6 +102,8 @@
<file>Pages2/PageSettingsAppSplitTunneling.qml</file>
<file>Pages2/PageSettingsBackup.qml</file>
<file>Pages2/PageSettingsConnection.qml</file>
<file>Pages2/PageSettingsConnectionProtocols.qml</file>
<file>Pages2/PageSettingsConnectionType.qml</file>
<file>Pages2/PageSettingsDns.qml</file>
<file>Pages2/PageSettingsKillSwitch.qml</file>
<file>Pages2/PageSettingsKillSwitchExceptions.qml</file>