From 236daf6b3b258317ccaec261ffc25296d0208c4e Mon Sep 17 00:00:00 2001 From: vkamn Date: Mon, 3 Nov 2025 10:26:22 +0800 Subject: [PATCH 1/8] feat: ad label (#1966) * refactor: ad label desing refatroing * feat: add ad label settings processing * chore: fix ru translations * chore: minor fixes --- client/core/api/apiDefs.h | 8 + client/translations/amneziavpn_ru_RU.ts | 97 +++++++----- .../controllers/api/apiConfigsController.cpp | 16 +- .../controllers/api/apiSettingsController.cpp | 1 + client/ui/models/api/apiAccountInfoModel.cpp | 16 +- client/ui/models/api/apiAccountInfoModel.h | 2 + client/ui/models/servers_model.cpp | 45 +++++- client/ui/models/servers_model.h | 12 ++ client/ui/qml/Components/AdLabel.qml | 149 +++++++++++++----- client/ui/qml/Modules/Style/AmneziaStyle.qml | 2 + client/ui/qml/Pages2/PageHome.qml | 20 +-- 11 files changed, 262 insertions(+), 106 deletions(-) diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 151746a45..8e5428558 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -47,12 +47,14 @@ namespace apiDefs constexpr QLatin1String serverCountryName("server_country_name"); constexpr QLatin1String osVersion("os_version"); + constexpr QLatin1String appLanguage("app_language"); constexpr QLatin1String availableCountries("available_countries"); constexpr QLatin1String activeDeviceCount("active_device_count"); constexpr QLatin1String maxDeviceCount("max_device_count"); constexpr QLatin1String subscriptionEndDate("subscription_end_date"); constexpr QLatin1String issuedConfigs("issued_configs"); + constexpr QLatin1String subscriptionDescription("subscription_description"); constexpr QLatin1String supportInfo("support_info"); constexpr QLatin1String email("email"); @@ -68,6 +70,12 @@ namespace apiDefs constexpr QLatin1String transactionId("transaction_id"); constexpr QLatin1String userCountryCode("user_country_code"); + + constexpr QLatin1String serviceInfo("service_info"); + constexpr QLatin1String isAdVisible("is_ad_visible"); + constexpr QLatin1String adHeader("ad_header"); + constexpr QLatin1String adDescription("ad_description"); + constexpr QLatin1String adEndpoint("ad_endpoint"); } const int requestTimeoutMsecs = 12 * 1000; // 12 secs diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 5c9869306..185c054ea 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -4,9 +4,8 @@ AdLabel - Amnezia Premium - for access to all websites and online resources - Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам + Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам @@ -61,7 +60,7 @@ ApiAccountInfoModel - + Active Активна @@ -71,35 +70,33 @@ Не активна - + %1 out of %2 %1 из %2 - Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps - Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с + Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с - Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. - Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф. + Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф. ApiConfigsController - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 @@ -627,27 +624,32 @@ Thank you for staying with us! Продолжить - + Logging enabled Логирование включено - + + Dev gateway enabled + + + + Split tunneling enabled Раздельное туннелирование включено - + Split tunneling disabled Раздельное туннелирование выключено - + VPN protocol VPN-протокол - + Servers Серверы @@ -1579,32 +1581,37 @@ Thank you for staying with us! Настройки - + Servers Серверы - + Connection Соединение - + Application Приложение - + + News & Notifications + Новости и Уведомления + + + Backup Резервное копирование - + About AmneziaVPN Об AmneziaVPN - + Dev console Dev console @@ -2763,6 +2770,14 @@ Thank you for staying with us! Очистить логи + + PageSettingsNewsNotifications + + + News & Notifications + Новости и Уведомления + + PageSettingsServerData @@ -3012,13 +3027,13 @@ Thank you for staying with us! - + Continue Продолжить - + Cancel Отменить @@ -3059,8 +3074,8 @@ Thank you for staying with us! - - + + Sites files (*.json) Файлы сайтов (*.json) @@ -3070,33 +3085,33 @@ Thank you for staying with us! Очистить список сайтов - + Clear site list? Очистить список сайтов? - + All sites will be removed from list. Все сайты будут удалены из списка. - + Import a list of sites Импортировать список с сайтами - + Replace site list Заменить список с сайтами - - + + Open sites file Открыть список с сайтами - + Add imported sites to existing ones Добавить импортированные сайты к существующим @@ -3521,32 +3536,32 @@ Thank you for staying with us! PageSetupWizardViewConfig - + New connection Новое соединение - + Collapse content Свернуть - + Show content Показать - + Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider. Включить обфускацию WireGuard. Это может быть полезно, если WireGuard блокируется вашим провайдером. - + Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data. Используйте файлы конфигурации только из тех источников, которым вы доверяете. Файлы из общедоступных источников могли быть созданы с целью перехвата ваших личных данных. - + Connect Подключиться @@ -4950,12 +4965,12 @@ FileZilla или другие SFTP-клиенты, а также смонтир SettingsController - + All settings have been reset to default values Все настройки сброшены до значений по умолчанию - + Backup file is corrupted Файл резервной копии поврежден diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 026224c5d..41756462c 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -29,7 +29,6 @@ namespace constexpr char uuid[] = "installation_uuid"; constexpr char osVersion[] = "os_version"; constexpr char appVersion[] = "app_version"; - constexpr char appLanguage[] = "app_language"; constexpr char userCountryCode[] = "user_country_code"; constexpr char serverCountryCode[] = "server_country_code"; @@ -65,6 +64,7 @@ namespace { QString osVersion; QString appVersion; + QString appLanguage; QString installationUuid; @@ -84,6 +84,9 @@ namespace if (!appVersion.isEmpty()) { obj[configKey::appVersion] = appVersion; } + if (!appLanguage.isEmpty()) { + obj[apiDefs::key::appLanguage] = appLanguage; + } if (!installationUuid.isEmpty()) { obj[configKey::uuid] = installationUuid; } @@ -223,6 +226,9 @@ namespace if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { apiConfig.insert(apiDefs::key::supportedProtocols, QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray()); + + apiConfig.insert(apiDefs::key::serviceInfo, + QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::serviceInfo).toObject()); } serverConfig[configKey::apiConfig] = apiConfig; @@ -285,6 +291,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), m_settings->getInstallationUuid(true), apiConfigObject.value(configKey::userCountryCode).toString(), serverCountryCode, @@ -325,6 +332,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), m_settings->getInstallationUuid(true), apiConfigObject.value(configKey::userCountryCode).toString(), serverCountryCode, @@ -375,7 +383,7 @@ bool ApiConfigsController::fillAvailableServices() { QJsonObject apiPayload; apiPayload[configKey::osVersion] = QSysInfo::productType(); - apiPayload[configKey::appLanguage] = m_settings->getAppLanguage().name().split("_").first(); + apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first(); QByteArray responseBody; ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody); @@ -399,6 +407,7 @@ bool ApiConfigsController::importServiceFromGateway() { GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), "", @@ -457,6 +466,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), newCountryCode, @@ -577,6 +587,7 @@ bool ApiConfigsController::deactivateDevice(const bool isRemoveEvent) GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), m_settings->getInstallationUuid(true), apiConfigObject.value(configKey::userCountryCode).toString(), apiConfigObject.value(configKey::serverCountryCode).toString(), @@ -616,6 +627,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), + m_settings->getAppLanguage().name().split("_").first(), uuid, apiConfigObject.value(configKey::userCountryCode).toString(), serverCountryCode, diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index c4a75a5b8..58ba9af9f 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -62,6 +62,7 @@ bool ApiSettingsController::getAccountInfo(bool reload) apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); apiPayload[configKey::authData] = authData; apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); + apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first(); QByteArray responseBody; diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index bdd7d68d4..0f3a8a4ed 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -31,7 +31,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return tr("Active"); } - return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("

Inactive") : tr("Active"); + return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("

Inactive") + : tr("Active"); } case EndDateRole: { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { @@ -47,16 +48,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount); } case ServiceDescriptionRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) { - return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online " - "resources. " - "Speeds up to 200 Mbps"); - } else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { - return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and " - "more. YouTube is not included in the free plan."); - } else { - return ""; - } + return m_accountInfoData.subscriptionDescription; } case IsComponentVisibleRole: { return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2 @@ -101,6 +93,8 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons accountInfoData.configType = apiUtils::getConfigType(serverConfig); + accountInfoData.subscriptionDescription = accountInfoObject.value(apiDefs::key::subscriptionDescription).toString(); + for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) { accountInfoData.supportedProtocols.push_back(protocol.toString()); } diff --git a/client/ui/models/api/apiAccountInfoModel.h b/client/ui/models/api/apiAccountInfoModel.h index f02039677..836bc8926 100644 --- a/client/ui/models/api/apiAccountInfoModel.h +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -54,6 +54,8 @@ private: apiDefs::ConfigType configType; QStringList supportedProtocols; + + QString subscriptionDescription; }; AccountInfoData m_accountInfoData; diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 5d8910d00..1a2bb150a 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -158,6 +158,18 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const QString primaryDns = server.value(config_key::dns1).toString(); return primaryDns == protocols::dns::amneziaDnsIp; } + case IsAdVisibleRole:{ + return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::isAdVisible).toBool(false); + } + case AdHeaderRole: { + return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adHeader).toString(); + } + case AdDescriptionRole: { + return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adDescription).toString(); + } + case AdEndpointRole: { + return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adEndpoint).toString(); + } } return QVariant(); @@ -403,6 +415,12 @@ QHash ServersModel::roleNames() const roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable"; roles[ApiAvailableCountriesRole] = "apiAvailableCountries"; roles[ApiServerCountryCodeRole] = "apiServerCountryCode"; + + roles[IsAdVisibleRole] = "isAdVisible"; + roles[AdHeaderRole] = "adHeader"; + roles[AdDescriptionRole] = "adDescription"; + roles[AdEndpointRole] = "adEndpoint"; + return roles; } @@ -784,22 +802,22 @@ void ServersModel::recomputeGatewayStacks() const bool wasEmpty = m_gatewayStacks.isEmpty(); GatewayStacks computed; bool hasNewTags = false; - + for (int i = 0; i < m_servers.count(); ++i) { if (data(i, IsServerFromGatewayApiRole).toBool()) { const QJsonObject server = m_servers.at(i).toObject(); const QJsonObject apiConfig = server.value(configKey::apiConfig).toObject(); - + const QString userCountryCode = apiConfig.value(configKey::userCountryCode).toString(); const QString serviceType = apiConfig.value(configKey::serviceType).toString(); - + if (!userCountryCode.isEmpty()) { if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) { hasNewTags = true; } computed.userCountryCodes.insert(userCountryCode); } - + if (!serviceType.isEmpty()) { if (!m_gatewayStacks.serviceTypes.contains(serviceType)) { hasNewTags = true; @@ -808,12 +826,12 @@ void ServersModel::recomputeGatewayStacks() } } } - + m_gatewayStacks = std::move(computed); if (hasNewTags) { emit gatewayStacksExpanded(); } - + if (wasEmpty != m_gatewayStacks.isEmpty()) { emit hasServersFromGatewayApiChanged(); } @@ -885,3 +903,18 @@ bool ServersModel::processedServerIsPremium() const { return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex)); } + +bool ServersModel::isAdVisible() +{ + return data(m_defaultServerIndex, IsAdVisibleRole).toBool(); +} + +QString ServersModel::adHeader() +{ + return data(m_defaultServerIndex, AdHeaderRole).toString(); +} + +QString ServersModel::adDescription() +{ + return data(m_defaultServerIndex, AdDescriptionRole).toString(); +} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 973b54189..8a0406bbe 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -47,6 +47,10 @@ public: IsCountrySelectionAvailableRole, ApiAvailableCountriesRole, ApiServerCountryCodeRole, + IsAdVisibleRole, + AdHeaderRole, + AdDescriptionRole, + AdEndpointRole, HasAmneziaDns }; @@ -79,6 +83,10 @@ public: Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged) + Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIndexChanged) + Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIndexChanged) + Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIndexChanged) + bool processedServerIsPremium() const; public slots: @@ -144,6 +152,10 @@ public slots: bool isApiKeyExpired(const int serverIndex); void removeApiConfig(const int serverIndex); + bool isAdVisible(); + QString adHeader(); + QString adDescription(); + protected: QHash roleNames() const override; diff --git a/client/ui/qml/Components/AdLabel.qml b/client/ui/qml/Components/AdLabel.qml index 3ef0fc699..fedaa0e56 100644 --- a/client/ui/qml/Components/AdLabel.qml +++ b/client/ui/qml/Components/AdLabel.qml @@ -2,7 +2,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import QtQuick.Shapes -import Qt5Compat.GraphicalEffects import Style 1.0 @@ -13,61 +12,139 @@ import "../Controls2/TextTypes" Rectangle { id: root - property real contentHeight: ad.implicitHeight + ad.anchors.topMargin + ad.anchors.bottomMargin + property real contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin + property bool isFocusable: true + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: AmneziaStyle.color.translucentSlateGray } + GradientStop { position: 1.0; color: AmneziaStyle.color.translucentOnyxBlack } + } border.width: 1 - border.color: AmneziaStyle.color.goldenApricot - color: AmneziaStyle.color.transparent + border.color: AmneziaStyle.color.onyxBlack radius: 13 - visible: false - // visible: GC.isDesktop() && ServersModel.isDefaultServerFromApi - // && ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && SettingsController.isHomeAdLabelVisible + visible: ServersModel.isAdVisible - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - onClicked: function() { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium")) - } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + Keys.onEnterPressed: { + Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint")) + } + + Keys.onReturnPressed: { + Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint")) } RowLayout { - id: ad + id: content anchors.fill: parent - anchors.margins: 16 + anchors.leftMargin: 16 + anchors.rightMargin: 12 + anchors.topMargin: 12 + anchors.bottomMargin: 12 + spacing: 20 - Image { - source: "qrc:/images/controls/amnezia.svg" - sourceSize: Qt.size(36, 36) + ColumnLayout { + Layout.fillWidth: true + spacing: 4 - layer { - effect: ColorOverlay { - color: AmneziaStyle.color.paleGray - } + CaptionTextType { + Layout.fillWidth: true + text: ServersModel.adHeader + color: AmneziaStyle.color.paleGray + font.pixelSize: 14 + font.weight: 700 + + textFormat: Text.RichText + } + + CaptionTextType { + Layout.fillWidth: true + text: ServersModel.adDescription + color: AmneziaStyle.color.mutedGray + wrapMode: Text.WordWrap + lineHeight: 18 + lineHeightMode: Text.FixedHeight + font.pixelSize: 14 + + visible: text !== "" } } - CaptionTextType { - Layout.fillWidth: true - Layout.rightMargin: 10 - Layout.leftMargin: 10 + Item { + implicitWidth: 40 + implicitHeight: 40 + Layout.alignment: Qt.AlignVCenter - text: qsTr("Amnezia Premium - for access to all websites and online resources") - color: AmneziaStyle.color.pearlGray + Rectangle { + id: chevronBackground + anchors.fill: parent + radius: 12 + color: AmneziaStyle.color.transparent + border.width: root.activeFocus ? 1 : 0 + border.color: AmneziaStyle.color.paleGray - lineHeight: 18 - font.pixelSize: 15 - } + Behavior on color { + PropertyAnimation { duration: 200 } + } - ImageButtonType { - image: "qrc:/images/controls/close.svg" - imageColor: AmneziaStyle.color.paleGray + Behavior on border.width { + PropertyAnimation { duration: 200 } + } + } - onClicked: function() { - SettingsController.disableHomeAdLabel() + Image { + anchors.centerIn: parent + source: "qrc:/images/controls/chevron-right.svg" + sourceSize: Qt.size(24, 24) } } } + + MouseArea { + id: mouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + chevronBackground.color = AmneziaStyle.color.slateGray + } + + onExited: { + chevronBackground.color = AmneziaStyle.color.transparent + } + + onPressedChanged: { + chevronBackground.color = pressed ? AmneziaStyle.color.charcoalGray : containsMouse ? AmneziaStyle.color.slateGray : AmneziaStyle.color.transparent + } + + onClicked: function() { + root.forceActiveFocus() + Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint")) + } + } } diff --git a/client/ui/qml/Modules/Style/AmneziaStyle.qml b/client/ui/qml/Modules/Style/AmneziaStyle.qml index 4e2e80f06..20b563360 100644 --- a/client/ui/qml/Modules/Style/AmneziaStyle.qml +++ b/client/ui/qml/Modules/Style/AmneziaStyle.qml @@ -28,5 +28,7 @@ QtObject { readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65) readonly property color pearlGray: '#EAEAEC' readonly property color translucentRichBrown: Qt.rgba(99/255, 51/255, 3/255, 0.26) + readonly property color translucentSlateGray: Qt.rgba(85/255, 86/255, 92/255, 0.13) + readonly property color translucentOnyxBlack: Qt.rgba(28/255, 29/255, 33/255, 0.13) } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index d2289ab03..09a13e67d 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -71,16 +71,6 @@ PageType { anchors.topMargin: 12 anchors.bottomMargin: 16 - AdLabel { - id: adLabel - - Layout.fillWidth: true - Layout.preferredHeight: adLabel.contentHeight - Layout.leftMargin: 16 - Layout.rightMargin: 16 - Layout.bottomMargin: 22 - } - BasicButtonType { id: loggingButton objectName: "loggingButton" @@ -189,6 +179,16 @@ PageType { parent: root } } + + AdLabel { + id: adLabel + + Layout.fillWidth: true + Layout.preferredHeight: adLabel.contentHeight + Layout.leftMargin: 16 + Layout.rightMargin: 16 + Layout.topMargin: 22 + } } } From e0e126eda87cc45a6df74b5e5f65d72ba6ef9f70 Mon Sep 17 00:00:00 2001 From: vkamn Date: Mon, 3 Nov 2025 10:26:33 +0800 Subject: [PATCH 2/8] chore: bump version (#1969) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa641b691..6d8a40f7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -set(AMNEZIAVPN_VERSION 4.8.11.0) +set(AMNEZIAVPN_VERSION 4.8.11.1) project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} DESCRIPTION "AmneziaVPN" @@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2095) +set(APP_ANDROID_VERSION_CODE 2096) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") From aaf0e070dcfa541b73496552f31ff7e343d1ce82 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 3 Nov 2025 04:27:01 +0200 Subject: [PATCH 3/8] fix: hide description (#1959) --- client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml index dc3f63c6a..8388d388d 100644 --- a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml +++ b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml @@ -71,7 +71,7 @@ PageType { text: countryName descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : "" - hideDescription: isWorkerExpired ? true : false + hideDescription: isWorkerExpired ? false : true descriptionColor: AmneziaStyle.color.vibrantRed leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" From 741b5cc0f9c282a8ad0efe41f0b26d12de34e8e5 Mon Sep 17 00:00:00 2001 From: vkamn Date: Tue, 4 Nov 2025 11:43:36 +0800 Subject: [PATCH 4/8] fix: qt6 9 support (#1973) * Fix qt 6.9 support * add support android sdk 36 * feat: add support SafeMargins from Android * Fix black screen --------- Co-authored-by: NickVs2015 --- .github/workflows/deploy.yml | 4 +- client/android/AndroidManifest.xml | 1 + client/android/res/values/styles.xml | 3 + .../src/org/amnezia/vpn/AmneziaActivity.kt | 88 ++++++++++++++++++- .../src/org/amnezia/vpn/AppListProvider.kt | 8 +- client/cmake/android.cmake | 6 +- .../platforms/android/android_controller.cpp | 15 ++++ client/platforms/android/android_controller.h | 3 + client/resources.qrc | 1 - client/ui/controllers/settingsController.cpp | 57 ++++++++++++ client/ui/controllers/settingsController.h | 10 +++ client/ui/qml/Components/QuestionDrawer.qml | 2 +- client/ui/qml/Controls2/PopupType.qml | 2 +- client/ui/qml/Pages2/PageHome.qml | 2 +- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 2 +- .../qml/Pages2/PageProtocolCloakSettings.qml | 2 +- .../Pages2/PageProtocolOpenVpnSettings.qml | 2 +- client/ui/qml/Pages2/PageProtocolRaw.qml | 2 +- .../PageProtocolShadowSocksSettings.qml | 2 +- .../PageProtocolWireGuardClientSettings.qml | 2 +- .../Pages2/PageProtocolWireGuardSettings.qml | 2 +- .../qml/Pages2/PageProtocolXraySettings.qml | 2 +- .../ui/qml/Pages2/PageServiceDnsSettings.qml | 2 +- .../ui/qml/Pages2/PageServiceSftpSettings.qml | 2 +- .../Pages2/PageServiceSocksProxySettings.qml | 2 +- .../Pages2/PageServiceTorWebsiteSettings.qml | 2 +- client/ui/qml/Pages2/PageSettings.qml | 2 +- client/ui/qml/Pages2/PageSettingsAbout.qml | 2 +- .../ui/qml/Pages2/PageSettingsApiDevices.qml | 2 +- .../Pages2/PageSettingsApiInstructions.qml | 2 +- .../Pages2/PageSettingsApiNativeConfigs.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- .../ui/qml/Pages2/PageSettingsApiSupport.qml | 2 +- .../Pages2/PageSettingsAppSplitTunneling.qml | 6 +- .../ui/qml/Pages2/PageSettingsApplication.qml | 2 +- client/ui/qml/Pages2/PageSettingsBackup.qml | 2 +- .../ui/qml/Pages2/PageSettingsConnection.qml | 2 +- client/ui/qml/Pages2/PageSettingsDns.qml | 2 +- .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 2 +- .../PageSettingsKillSwitchExceptions.qml | 2 +- client/ui/qml/Pages2/PageSettingsLogging.qml | 2 +- .../ui/qml/Pages2/PageSettingsNewsDetail.qml | 2 +- .../Pages2/PageSettingsNewsNotifications.qml | 2 +- .../ui/qml/Pages2/PageSettingsServerInfo.qml | 2 +- .../qml/Pages2/PageSettingsServerProtocol.qml | 2 +- .../ui/qml/Pages2/PageSettingsServersList.qml | 2 +- .../qml/Pages2/PageSettingsSplitTunneling.qml | 2 +- .../Pages2/PageSetupWizardApiServiceInfo.qml | 2 +- .../Pages2/PageSetupWizardApiServicesList.qml | 2 +- .../Pages2/PageSetupWizardConfigSource.qml | 2 +- .../qml/Pages2/PageSetupWizardCredentials.qml | 2 +- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 2 +- .../PageSetupWizardProtocolSettings.qml | 2 +- .../qml/Pages2/PageSetupWizardProtocols.qml | 2 +- .../ui/qml/Pages2/PageSetupWizardTextKey.qml | 2 +- .../qml/Pages2/PageSetupWizardViewConfig.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 2 +- client/ui/qml/Pages2/PageShareConnection.qml | 4 +- client/ui/qml/Pages2/PageShareFullAccess.qml | 2 +- client/ui/qml/Pages2/PageStart.qml | 2 +- client/ui/qml/main2.qml | 23 ++++- 61 files changed, 255 insertions(+), 68 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3975e7109..1384eae42 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -469,8 +469,8 @@ jobs: runs-on: ubuntu-latest env: - ANDROID_BUILD_PLATFORM: android-34 - QT_VERSION: 6.7.3 + ANDROID_BUILD_PLATFORM: android-36 + QT_VERSION: 6.9.3 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index b28f754b2..04001f63d 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -46,6 +46,7 @@ |fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc" android:launchMode="singleInstance" android:windowSoftInputMode="stateUnchanged|adjustResize" + android:enableOnBackInvokedCallback="false" android:exported="true"> diff --git a/client/android/res/values/styles.xml b/client/android/res/values/styles.xml index bc67beb98..f11a3796a 100644 --- a/client/android/res/values/styles.xml +++ b/client/android/res/values/styles.xml @@ -6,6 +6,9 @@ @color/black false true + shortEdges + false + false