mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-24 02:00:24 +07:00
feat: add UI to protocol auto-switching
This commit is contained in:
@@ -752,6 +752,12 @@ bool SubscriptionController::isVlessProtocol(const QString &serverId) const
|
|||||||
return apiV2.has_value() && apiV2->serviceProtocol() == "vless";
|
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,
|
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &productId,
|
const QString &serviceProtocol, const QString &productId,
|
||||||
int *duplicateServerIndex)
|
int *duplicateServerIndex)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ public:
|
|||||||
|
|
||||||
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
|
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
|
||||||
bool isVlessProtocol(const QString &serverId) const;
|
bool isVlessProtocol(const QString &serverId) const;
|
||||||
|
bool isAwgProtocol(const QString &serverId) const;
|
||||||
|
|
||||||
ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
|
ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
|
||||||
QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
|
QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
|
||||||
|
|||||||
@@ -510,6 +510,11 @@ bool SubscriptionUiController::isVlessProtocol(const QString &serverId)
|
|||||||
return m_subscriptionController->isVlessProtocol(serverId);
|
return m_subscriptionController->isVlessProtocol(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SubscriptionUiController::isAwgProtocol(const QString &serverId)
|
||||||
|
{
|
||||||
|
return m_subscriptionController->isAwgProtocol(serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SubscriptionUiController::removeApiConfig(const QString &serverId)
|
void SubscriptionUiController::removeApiConfig(const QString &serverId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public slots:
|
|||||||
|
|
||||||
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
|
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
|
||||||
bool isVlessProtocol(const QString &serverId);
|
bool isVlessProtocol(const QString &serverId);
|
||||||
|
bool isAwgProtocol(const QString &serverId);
|
||||||
|
|
||||||
bool isCaptchaAwaitingUser() const;
|
bool isCaptchaAwaitingUser() const;
|
||||||
void onCaptchaSolved(const QString &captchaId, const QString &solution);
|
void onCaptchaSolved(const QString &captchaId, const QString &solution);
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ namespace PageLoader
|
|||||||
PageSettingsApiDevices,
|
PageSettingsApiDevices,
|
||||||
PageSettingsApiSubscriptionKey,
|
PageSettingsApiSubscriptionKey,
|
||||||
PageSettingsKillSwitchExceptions,
|
PageSettingsKillSwitchExceptions,
|
||||||
|
PageSettingsConnectionType,
|
||||||
|
PageSettingsConnectionProtocols,
|
||||||
|
|
||||||
PageServiceSftpSettings,
|
PageServiceSftpSettings,
|
||||||
PageServiceTorWebsiteSettings,
|
PageServiceTorWebsiteSettings,
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ PageType {
|
|||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WarningType {
|
WarningType {
|
||||||
id: warning
|
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 {
|
LabelWithButtonType {
|
||||||
id: vpnKey
|
id: vpnKey
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: warning.visible ? 16 : 0
|
|
||||||
|
|
||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("Subscription Key")
|
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 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,6 +102,8 @@
|
|||||||
<file>Pages2/PageSettingsAppSplitTunneling.qml</file>
|
<file>Pages2/PageSettingsAppSplitTunneling.qml</file>
|
||||||
<file>Pages2/PageSettingsBackup.qml</file>
|
<file>Pages2/PageSettingsBackup.qml</file>
|
||||||
<file>Pages2/PageSettingsConnection.qml</file>
|
<file>Pages2/PageSettingsConnection.qml</file>
|
||||||
|
<file>Pages2/PageSettingsConnectionProtocols.qml</file>
|
||||||
|
<file>Pages2/PageSettingsConnectionType.qml</file>
|
||||||
<file>Pages2/PageSettingsDns.qml</file>
|
<file>Pages2/PageSettingsDns.qml</file>
|
||||||
<file>Pages2/PageSettingsKillSwitch.qml</file>
|
<file>Pages2/PageSettingsKillSwitch.qml</file>
|
||||||
<file>Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
<file>Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
||||||
|
|||||||
Reference in New Issue
Block a user