mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +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";
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user