mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
feat: add UI to protocol auto-switching
This commit is contained in:
@@ -250,6 +250,8 @@
|
||||
<file>ui/qml/Components/AwgTextField.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
||||
<file>ui/qml/Components/SmartScroll.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsConnectionType.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsConnectionProtocols.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/countriesFlags">
|
||||
<file>images/flagKit/ZW.svg</file>
|
||||
|
||||
@@ -882,6 +882,18 @@ bool ApiConfigsController::isVlessProtocol()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isAwgProtocol()
|
||||
{
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (apiConfigObject[configKey::serviceProtocol].toString() == "awg") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QString> ApiConfigsController::getQrCodes()
|
||||
{
|
||||
return m_qrCodes;
|
||||
|
||||
@@ -40,6 +40,7 @@ public slots:
|
||||
|
||||
void setCurrentProtocol(const QString &protocolName);
|
||||
bool isVlessProtocol();
|
||||
bool isAwgProtocol();
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
|
||||
@@ -14,6 +14,15 @@
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
{
|
||||
constexpr char apiConfig[] = "api_config";
|
||||
constexpr char serviceProtocol[] = "service_protocol";
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ContainersModel> &containersModel,
|
||||
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
||||
@@ -98,31 +107,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
break;
|
||||
}
|
||||
case Vpn::ConnectionState::Connecting: {
|
||||
{
|
||||
const int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
if (serverIndex >= 0) {
|
||||
const QVariant containerVar = m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole);
|
||||
if (containerVar.isValid()) {
|
||||
const DockerContainer container = qvariant_cast<DockerContainer>(containerVar);
|
||||
const Proto proto = ContainerProps::defaultProtocol(container);
|
||||
if (proto == Proto::Awg) {
|
||||
const QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
if (apiUtils::isPremiumServer(serverConfig)) {
|
||||
m_awgStateTimer.start(10000);
|
||||
} else {
|
||||
if (m_awgStateTimer.isActive()) {
|
||||
m_awgStateTimer.stop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_awgStateTimer.isActive()) {
|
||||
m_awgStateTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkAndStartAwgStateTimer();
|
||||
m_isConnectionInProgress = true;
|
||||
break;
|
||||
}
|
||||
@@ -320,3 +305,41 @@ bool ConnectionController::isConnected() const
|
||||
{
|
||||
return m_isConnected;
|
||||
}
|
||||
|
||||
void ConnectionController::checkAndStartAwgStateTimer()
|
||||
{
|
||||
const int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
if (serverIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QVariant containerVar = m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole);
|
||||
if (!containerVar.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DockerContainer container = qvariant_cast<DockerContainer>(containerVar);
|
||||
const Proto proto = ContainerProps::defaultProtocol(container);
|
||||
if (proto != Proto::Awg) {
|
||||
if (m_awgStateTimer.isActive()) {
|
||||
m_awgStateTimer.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
|
||||
// Auto-switching only works in auto mode, not when protocol is explicitly selected
|
||||
const QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
const QString serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString();
|
||||
// Auto mode: empty serviceProtocol means use default protocol (AWG for AWG container)
|
||||
const bool isAutoMode = serviceProtocol.isEmpty();
|
||||
|
||||
if (isAutoMode && apiUtils::isPremiumServer(serverConfig)) {
|
||||
m_awgStateTimer.start(10000);
|
||||
} else {
|
||||
if (m_awgStateTimer.isActive()) {
|
||||
m_awgStateTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public slots:
|
||||
|
||||
void onTranslationsUpdated();
|
||||
|
||||
void checkAndStartAwgStateTimer();
|
||||
|
||||
private slots:
|
||||
void onAwgStateTimeout();
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace PageLoader
|
||||
PageSettingsApiDevices,
|
||||
PageSettingsApiSubscriptionKey,
|
||||
PageSettingsKillSwitchExceptions,
|
||||
PageSettingsConnectionType,
|
||||
PageSettingsConnectionProtocols,
|
||||
|
||||
PageServiceSftpSettings,
|
||||
PageServiceTorWebsiteSettings,
|
||||
|
||||
@@ -151,32 +151,6 @@ PageType {
|
||||
|
||||
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
readonly property bool isVlessProtocol: ApiConfigsController.isVlessProtocol()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
visible: ApiAccountInfoModel.data("isProtocolSelectionSupported")
|
||||
|
||||
text: qsTr("Use VLESS protocol")
|
||||
checked: switcher.isVlessProtocol
|
||||
onToggled: function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.setCurrentProtocol(switcher.isVlessProtocol ? "awg" : "vless")
|
||||
ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", true)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WarningType {
|
||||
id: warning
|
||||
|
||||
@@ -201,11 +175,25 @@ PageType {
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: vpnKey
|
||||
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
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
|
||||
text: qsTr("Subscription Key")
|
||||
|
||||
283
client/ui/qml/Pages2/PageSettingsConnectionProtocols.qml
Normal file
283
client/ui/qml/Pages2/PageSettingsConnectionProtocols.qml
Normal file
@@ -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
|
||||
}
|
||||
|
||||
ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", 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
|
||||
ApiConfigsController.setCurrentProtocol(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 (ApiConfigsController.isVlessProtocol()) {
|
||||
return "vless"
|
||||
}
|
||||
|
||||
if (ApiConfigsController.isAwgProtocol()) {
|
||||
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: ApiConfigsController
|
||||
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)
|
||||
ApiConfigsController.setCurrentProtocol("")
|
||||
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)
|
||||
ApiConfigsController.setCurrentProtocol("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)
|
||||
ApiConfigsController.setCurrentProtocol("vless")
|
||||
updateProtocolTimer.savedProtocol = "vless"
|
||||
updateProtocolTimer.start()
|
||||
root.currentProtocol = "vless"
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
client/ui/qml/Pages2/PageSettingsConnectionType.qml
Normal file
92
client/ui/qml/Pages2/PageSettingsConnectionType.qml
Normal file
@@ -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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user