mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3122c02f13 | |||
| 61e7cd073a | |||
| 2e7e65c1e0 | |||
| 3a6d160bce |
@@ -470,7 +470,7 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-36
|
ANDROID_BUILD_PLATFORM: android-36
|
||||||
QT_VERSION: 6.8.3
|
QT_VERSION: 6.9.3
|
||||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
set(AMNEZIAVPN_VERSION 4.8.11.3)
|
set(AMNEZIAVPN_VERSION 4.8.11.0)
|
||||||
|
|
||||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2098)
|
set(APP_ANDROID_VERSION_CODE 2095)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|
||||||
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
|
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
android:windowSoftInputMode="adjustResize|stateUnchanged"
|
android:windowSoftInputMode="stateUnchanged|adjustResize"
|
||||||
android:enableOnBackInvokedCallback="false"
|
android:enableOnBackInvokedCallback="false"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.Insets
|
|
||||||
import androidx.core.view.OnApplyWindowInsetsListener
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
@@ -304,11 +300,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
isAppearanceLightStatusBars = false
|
isAppearanceLightStatusBars = false
|
||||||
isAppearanceLightNavigationBars = false
|
isAppearanceLightNavigationBars = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for Android 14 (API 34+) IME adjustResize bug
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
setupImeInsetsListener()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
window.apply {
|
window.apply {
|
||||||
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
@@ -317,34 +308,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupImeInsetsListener() {
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, windowInsets ->
|
|
||||||
val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
|
|
||||||
val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
|
|
||||||
|
|
||||||
val imeHeight = if (imeVisible) imeInsets.bottom else 0
|
|
||||||
|
|
||||||
val density = resources.displayMetrics.density
|
|
||||||
val imeHeightDp = (imeHeight / density).toInt()
|
|
||||||
|
|
||||||
// Also track system bars (navigation bar, status bar) changes
|
|
||||||
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
||||||
val navBarHeight = systemBarsInsets.bottom
|
|
||||||
val navBarHeightDp = (navBarHeight / density).toInt()
|
|
||||||
val statusBarHeight = systemBarsInsets.top
|
|
||||||
val statusBarHeightDp = (statusBarHeight / density).toInt()
|
|
||||||
|
|
||||||
mainScope.launch {
|
|
||||||
qtInitialized.await()
|
|
||||||
QtAndroidController.onImeInsetsChanged(imeHeightDp)
|
|
||||||
QtAndroidController.onSystemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return windowInsets instead of CONSUMED to allow proper handling
|
|
||||||
windowInsets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(TAG, "Destroy Amnezia activity")
|
Log.d(TAG, "Destroy Amnezia activity")
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
unregisterBroadcastReceiver(notificationStateReceiver)
|
||||||
|
|||||||
@@ -28,7 +28,4 @@ object QtAndroidController {
|
|||||||
external fun onAuthResult(result: Boolean)
|
external fun onAuthResult(result: Boolean)
|
||||||
|
|
||||||
external fun decodeQrCode(data: String): Boolean
|
external fun decodeQrCode(data: String): Boolean
|
||||||
|
|
||||||
external fun onImeInsetsChanged(heightDp: Int)
|
|
||||||
external fun onSystemBarsInsetsChanged(navBarHeightDp: Int, statusBarHeightDp: Int)
|
|
||||||
}
|
}
|
||||||
+15
-4
@@ -136,10 +136,21 @@ set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
|||||||
add_subdirectory(ios/networkextension)
|
add_subdirectory(ios/networkextension)
|
||||||
add_dependencies(${PROJECT} networkextension)
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
set(OPENVPN_FRAMEWORK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios")
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
set(OPENVPN_EMBEDDED_FRAMEWORKS
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNAdapter.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNClient.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/mbedTLS.framework"
|
||||||
|
"${OPENVPN_FRAMEWORK_DIR}/LZ4.framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
|
||||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
|
||||||
|
|
||||||
|
foreach(_framework ${OPENVPN_EMBEDDED_FRAMEWORKS})
|
||||||
|
target_link_libraries(networkextension PRIVATE "${_framework}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON)
|
||||||
|
set_property(TARGET networkextension PROPERTY XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
|
||||||
|
|||||||
@@ -47,14 +47,12 @@ namespace apiDefs
|
|||||||
constexpr QLatin1String serverCountryName("server_country_name");
|
constexpr QLatin1String serverCountryName("server_country_name");
|
||||||
|
|
||||||
constexpr QLatin1String osVersion("os_version");
|
constexpr QLatin1String osVersion("os_version");
|
||||||
constexpr QLatin1String appLanguage("app_language");
|
|
||||||
|
|
||||||
constexpr QLatin1String availableCountries("available_countries");
|
constexpr QLatin1String availableCountries("available_countries");
|
||||||
constexpr QLatin1String activeDeviceCount("active_device_count");
|
constexpr QLatin1String activeDeviceCount("active_device_count");
|
||||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
constexpr QLatin1String maxDeviceCount("max_device_count");
|
||||||
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
||||||
constexpr QLatin1String issuedConfigs("issued_configs");
|
constexpr QLatin1String issuedConfigs("issued_configs");
|
||||||
constexpr QLatin1String subscriptionDescription("subscription_description");
|
|
||||||
|
|
||||||
constexpr QLatin1String supportInfo("support_info");
|
constexpr QLatin1String supportInfo("support_info");
|
||||||
constexpr QLatin1String email("email");
|
constexpr QLatin1String email("email");
|
||||||
@@ -70,12 +68,6 @@ namespace apiDefs
|
|||||||
constexpr QLatin1String transactionId("transaction_id");
|
constexpr QLatin1String transactionId("transaction_id");
|
||||||
|
|
||||||
constexpr QLatin1String userCountryCode("user_country_code");
|
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
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QPromise>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "QBlockCipher.h"
|
#include "QBlockCipher.h"
|
||||||
@@ -51,25 +50,24 @@ GatewayController::GatewayController(const QString &gatewayEndpoint, const bool
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController::EncryptedRequestData GatewayController::prepareRequest(const QString &endpoint, const QJsonObject &apiPayload)
|
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
||||||
{
|
{
|
||||||
EncryptedRequestData encRequestData;
|
|
||||||
encRequestData.errorCode = ErrorCode::NoError;
|
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
IosController::Instance()->requestInetAccess();
|
IosController::Instance()->requestInetAccess();
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
encRequestData.request.setTransferTimeout(m_requestTimeoutMsecs);
|
QNetworkRequest request;
|
||||||
encRequestData.request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||||
encRequestData.request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
encRequestData.request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
|
||||||
|
|
||||||
|
request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
||||||
|
|
||||||
// bypass killSwitch exceptions for API-gateway
|
// bypass killSwitch exceptions for API-gateway
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
if (m_isStrictKillSwitchEnabled) {
|
if (m_isStrictKillSwitchEnabled) {
|
||||||
QString host = QUrl(encRequestData.request.url()).host();
|
QString host = QUrl(request.url()).host();
|
||||||
QString ip = NetworkUtilities::getIPAddress(host);
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
if (!ip.isEmpty()) {
|
if (!ip.isEmpty()) {
|
||||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
||||||
@@ -78,14 +76,14 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
encRequestData.key = blockCipher.generatePrivateSalt(32);
|
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||||
encRequestData.iv = blockCipher.generatePrivateSalt(32);
|
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||||
encRequestData.salt = blockCipher.generatePrivateSalt(8);
|
QByteArray salt = blockCipher.generatePrivateSalt(8);
|
||||||
|
|
||||||
QJsonObject keyPayload;
|
QJsonObject keyPayload;
|
||||||
keyPayload[configKey::aesKey] = QString(encRequestData.key.toBase64());
|
keyPayload[configKey::aesKey] = QString(key.toBase64());
|
||||||
keyPayload[configKey::aesIv] = QString(encRequestData.iv.toBase64());
|
keyPayload[configKey::aesIv] = QString(iv.toBase64());
|
||||||
keyPayload[configKey::aesSalt] = QString(encRequestData.salt.toBase64());
|
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
|
||||||
|
|
||||||
QByteArray encryptedKeyPayload;
|
QByteArray encryptedKeyPayload;
|
||||||
QByteArray encryptedApiPayload;
|
QByteArray encryptedApiPayload;
|
||||||
@@ -100,44 +98,31 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
Utils::logException();
|
Utils::logException();
|
||||||
qCritical() << "error loading public key from environment variables";
|
qCritical() << "error loading public key from environment variables";
|
||||||
encRequestData.errorCode = ErrorCode::ApiMissingAgwPublicKey;
|
return ErrorCode::ApiMissingAgwPublicKey;
|
||||||
return encRequestData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
||||||
EVP_PKEY_free(publicKey);
|
EVP_PKEY_free(publicKey);
|
||||||
|
|
||||||
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), encRequestData.key, encRequestData.iv, "", encRequestData.salt);
|
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
|
||||||
} catch (...) {
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
Utils::logException();
|
Utils::logException();
|
||||||
qCritical() << "error when encrypting the request body";
|
qCritical() << "error when encrypting the request body";
|
||||||
encRequestData.errorCode = ErrorCode::ApiConfigDecryptionError;
|
return ErrorCode::ApiConfigDecryptionError;
|
||||||
return encRequestData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject requestBody;
|
QJsonObject requestBody;
|
||||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||||
|
|
||||||
encRequestData.requestBody = QJsonDocument(requestBody).toJson();
|
QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
return encRequestData;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
|
||||||
{
|
|
||||||
EncryptedRequestData encRequestData = prepareRequest(endpoint, apiPayload);
|
|
||||||
if (encRequestData.errorCode != ErrorCode::NoError) {
|
|
||||||
return encRequestData.errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
|
|
||||||
|
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
|
||||||
QList<QSslError> sslErrors;
|
QList<QSslError> sslErrors;
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
|
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
QByteArray encryptedResponseBody = reply->readAll();
|
||||||
QString replyErrorString = reply->errorString();
|
QString replyErrorString = reply->errorString();
|
||||||
@@ -146,19 +131,19 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
|
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
auto requestFunction = [&encRequestData, &encryptedResponseBody](const QString &url) {
|
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
||||||
encRequestData.request.setUrl(url);
|
request.setUrl(url);
|
||||||
return amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
|
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
};
|
};
|
||||||
|
|
||||||
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &encRequestData,
|
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &key, &iv,
|
||||||
this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
|
&salt, this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
|
||||||
encryptedResponseBody = reply->readAll();
|
encryptedResponseBody = reply->readAll();
|
||||||
replyErrorString = reply->errorString();
|
replyErrorString = reply->errorString();
|
||||||
replyError = reply->error();
|
replyError = reply->error();
|
||||||
httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
if (!sslErrors.isEmpty() || shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
|
if (!sslErrors.isEmpty() || shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
sslErrors = nestedSslErrors;
|
sslErrors = nestedSslErrors;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -176,8 +161,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||||
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, encRequestData.key, encRequestData.iv, "", encRequestData.salt);
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
Utils::logException();
|
Utils::logException();
|
||||||
@@ -186,57 +170,6 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString &endpoint, const QJsonObject apiPayload)
|
|
||||||
{
|
|
||||||
auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QByteArray>>>::create();
|
|
||||||
promise->start();
|
|
||||||
|
|
||||||
EncryptedRequestData encRequestData = prepareRequest(endpoint, apiPayload);
|
|
||||||
if (encRequestData.errorCode != ErrorCode::NoError) {
|
|
||||||
promise->addResult(qMakePair(encRequestData.errorCode, QByteArray()));
|
|
||||||
promise->finish();
|
|
||||||
return promise->future();
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
|
|
||||||
|
|
||||||
auto sslErrors = QSharedPointer<QList<QSslError>>::create();
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) {
|
|
||||||
*sslErrors = errors;
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, reply, [=]() {
|
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
|
||||||
QString replyErrorString = reply->errorString();
|
|
||||||
auto replyError = reply->error();
|
|
||||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
auto errorCode = apiUtils::checkNetworkReplyErrors(*sslErrors, replyErrorString, replyError, httpStatusCode, encryptedResponseBody);
|
|
||||||
if (errorCode) {
|
|
||||||
promise->addResult(qMakePair(errorCode, QByteArray()));
|
|
||||||
promise->finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
|
||||||
try {
|
|
||||||
QByteArray responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, encRequestData.key, encRequestData.iv, "", encRequestData.salt);
|
|
||||||
promise->addResult(qMakePair(ErrorCode::NoError, responseBody));
|
|
||||||
promise->finish();
|
|
||||||
} catch (...) {
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error when decrypting the request body";
|
|
||||||
promise->addResult(qMakePair(ErrorCode::ApiConfigDecryptionError, QByteArray()));
|
|
||||||
promise->finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise->future();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
|
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
|
||||||
{
|
{
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
@@ -273,7 +206,7 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
|
|||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
auto encryptedResponseBody = reply->readAll();
|
auto encryptedResponseBody = reply->readAll();
|
||||||
@@ -385,7 +318,7 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv
|
|||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
|
|
||||||
auto result = replyProcessingFunction(reply, sslErrors);
|
auto result = replyProcessingFunction(reply, sslErrors);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
@@ -407,7 +340,7 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv
|
|||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
#ifndef GATEWAYCONTROLLER_H
|
#ifndef GATEWAYCONTROLLER_H
|
||||||
#define GATEWAYCONTROLLER_H
|
#define GATEWAYCONTROLLER_H
|
||||||
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
|
||||||
|
|
||||||
#include "core/defs.h"
|
#include "core/defs.h"
|
||||||
|
|
||||||
@@ -21,20 +19,8 @@ public:
|
|||||||
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
||||||
|
|
||||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||||
QFuture<QPair<amnezia::ErrorCode, QByteArray>> postAsync(const QString &endpoint, const QJsonObject apiPayload);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct EncryptedRequestData {
|
|
||||||
QNetworkRequest request;
|
|
||||||
QByteArray requestBody;
|
|
||||||
QByteArray key;
|
|
||||||
QByteArray iv;
|
|
||||||
QByteArray salt;
|
|
||||||
amnezia::ErrorCode errorCode;
|
|
||||||
};
|
|
||||||
|
|
||||||
EncryptedRequestData prepareRequest(const QString &endpoint, const QJsonObject &apiPayload);
|
|
||||||
|
|
||||||
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
|
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
|
||||||
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
|
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
|
||||||
const QByteArray &key = "", const QByteArray &iv = "", const QByteArray &salt = "");
|
const QByteArray &key = "", const QByteArray &iv = "", const QByteArray &salt = "");
|
||||||
|
|||||||
@@ -99,9 +99,7 @@ bool AndroidController::initialize()
|
|||||||
{"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)},
|
{"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)},
|
||||||
{"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)},
|
{"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)},
|
||||||
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
|
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
|
||||||
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)},
|
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
|
||||||
{"onImeInsetsChanged", "(I)V", reinterpret_cast<void *>(onImeInsetsChanged)},
|
|
||||||
{"onSystemBarsInsetsChanged", "(II)V", reinterpret_cast<void *>(onSystemBarsInsetsChanged)}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QJniEnvironment env;
|
QJniEnvironment env;
|
||||||
@@ -538,23 +536,3 @@ bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
|
|||||||
|
|
||||||
return ImportController::decodeQrCode(AndroidUtils::convertJString(env, data));
|
return ImportController::decodeQrCode(AndroidUtils::convertJString(env, data));
|
||||||
}
|
}
|
||||||
// static
|
|
||||||
void AndroidController::onImeInsetsChanged(JNIEnv *env, jobject thiz, jint heightDp)
|
|
||||||
{
|
|
||||||
Q_UNUSED(env);
|
|
||||||
Q_UNUSED(thiz);
|
|
||||||
|
|
||||||
qDebug() << "Android IME insets changed: height =" << heightDp << "dp";
|
|
||||||
emit AndroidController::instance()->imeInsetsChanged(heightDp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void AndroidController::onSystemBarsInsetsChanged(JNIEnv *env, jobject thiz, jint navBarHeightDp, jint statusBarHeightDp)
|
|
||||||
{
|
|
||||||
Q_UNUSED(env);
|
|
||||||
Q_UNUSED(thiz);
|
|
||||||
|
|
||||||
qDebug() << "Android system bars insets changed: nav bar =" << navBarHeightDp << "dp, status bar =" << statusBarHeightDp << "dp";
|
|
||||||
emit AndroidController::instance()->systemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,6 @@ signals:
|
|||||||
void importConfigFromOutside(QString config);
|
void importConfigFromOutside(QString config);
|
||||||
void initConnectionState(Vpn::ConnectionState state);
|
void initConnectionState(Vpn::ConnectionState state);
|
||||||
void authenticationResult(bool result);
|
void authenticationResult(bool result);
|
||||||
void imeInsetsChanged(int heightDp);
|
|
||||||
void systemBarsInsetsChanged(int navBarHeightDp, int statusBarHeightDp);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isWaitingStatus = true;
|
bool isWaitingStatus = true;
|
||||||
@@ -103,8 +101,6 @@ private:
|
|||||||
static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri);
|
static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri);
|
||||||
static void onAuthResult(JNIEnv *env, jobject thiz, jboolean result);
|
static void onAuthResult(JNIEnv *env, jobject thiz, jboolean result);
|
||||||
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
|
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
|
||||||
static void onImeInsetsChanged(JNIEnv *env, jobject thiz, jint heightDp);
|
|
||||||
static void onSystemBarsInsetsChanged(JNIEnv *env, jobject thiz, jint navBarHeightDp, jint statusBarHeightDp);
|
|
||||||
|
|
||||||
template <typename Ret, typename ...Args>
|
template <typename Ret, typename ...Args>
|
||||||
static auto callActivityMethod(const char *methodName, const char *signature, Args &&...args);
|
static auto callActivityMethod(const char *methodName, const char *signature, Args &&...args);
|
||||||
|
|||||||
@@ -247,7 +247,6 @@
|
|||||||
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
||||||
<file>ui/qml/Components/AwgTextField.qml</file>
|
<file>ui/qml/Components/AwgTextField.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
||||||
<file>ui/qml/Components/SmartScroll.qml</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/countriesFlags">
|
<qresource prefix="/countriesFlags">
|
||||||
<file>images/flagKit/ZW.svg</file>
|
<file>images/flagKit/ZW.svg</file>
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>AdLabel</name>
|
<name>AdLabel</name>
|
||||||
<message>
|
<message>
|
||||||
|
<location filename="../ui/qml/Components/AdLabel.qml" line="57"/>
|
||||||
<source>Amnezia Premium - for access to all websites and online resources</source>
|
<source>Amnezia Premium - for access to all websites and online resources</source>
|
||||||
<translation type="vanished">Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам</translation>
|
<translation>Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
<name>ApiAccountInfoModel</name>
|
<name>ApiAccountInfoModel</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/>
|
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/>
|
||||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="35"/>
|
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
|
||||||
<source>Active</source>
|
<source>Active</source>
|
||||||
<translation>Активна</translation>
|
<translation>Активна</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -70,33 +71,35 @@
|
|||||||
<translation>Не активна</translation>
|
<translation>Не активна</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="48"/>
|
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="47"/>
|
||||||
<source>%1 out of %2</source>
|
<source>%1 out of %2</source>
|
||||||
<translation>%1 из %2</translation>
|
<translation>%1 из %2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="51"/>
|
||||||
<source>Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps</source>
|
<source>Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps</source>
|
||||||
<translation type="vanished">Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с</translation>
|
<translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="55"/>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="vanished">Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф.</translation>
|
<translation>Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ApiConfigsController</name>
|
<name>ApiConfigsController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="448"/>
|
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="413"/>
|
||||||
<source>%1 installed successfully.</source>
|
<source>%1 installed successfully.</source>
|
||||||
<translation>%1 успешно установлен.</translation>
|
<translation>%1 успешно установлен.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="513"/>
|
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="473"/>
|
||||||
<source>API config reloaded</source>
|
<source>API config reloaded</source>
|
||||||
<translation>Конфигурация API перезагружена</translation>
|
<translation>Конфигурация API перезагружена</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="517"/>
|
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="477"/>
|
||||||
<source>Successfully changed the country of connection to %1</source>
|
<source>Successfully changed the country of connection to %1</source>
|
||||||
<translation>Страна подключения изменена на %1</translation>
|
<translation>Страна подключения изменена на %1</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -624,32 +627,27 @@ Thank you for staying with us!</source>
|
|||||||
<translation>Продолжить</translation>
|
<translation>Продолжить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="92"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="102"/>
|
||||||
<source>Logging enabled</source>
|
<source>Logging enabled</source>
|
||||||
<translation>Логирование включено</translation>
|
<translation>Логирование включено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="120"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="144"/>
|
||||||
<source>Dev gateway enabled</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="162"/>
|
|
||||||
<source>Split tunneling enabled</source>
|
<source>Split tunneling enabled</source>
|
||||||
<translation>Раздельное туннелирование включено</translation>
|
<translation>Раздельное туннелирование включено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="162"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="144"/>
|
||||||
<source>Split tunneling disabled</source>
|
<source>Split tunneling disabled</source>
|
||||||
<translation>Раздельное туннелирование выключено</translation>
|
<translation>Раздельное туннелирование выключено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="409"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="381"/>
|
||||||
<source>VPN protocol</source>
|
<source>VPN protocol</source>
|
||||||
<translation>VPN-протокол</translation>
|
<translation>VPN-протокол</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="462"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="434"/>
|
||||||
<source>Servers</source>
|
<source>Servers</source>
|
||||||
<translation>Серверы</translation>
|
<translation>Серверы</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -1581,37 +1579,32 @@ Thank you for staying with us!</source>
|
|||||||
<translation>Настройки</translation>
|
<translation>Настройки</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="102"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="101"/>
|
||||||
<source>Servers</source>
|
<source>Servers</source>
|
||||||
<translation>Серверы</translation>
|
<translation>Серверы</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="112"/>
|
||||||
<source>Connection</source>
|
<source>Connection</source>
|
||||||
<translation>Соединение</translation>
|
<translation>Соединение</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="124"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="123"/>
|
||||||
<source>Application</source>
|
<source>Application</source>
|
||||||
<translation>Приложение</translation>
|
<translation>Приложение</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="135"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="134"/>
|
||||||
<source>News & Notifications</source>
|
|
||||||
<translation>Новости и Уведомления</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="152"/>
|
|
||||||
<source>Backup</source>
|
<source>Backup</source>
|
||||||
<translation>Резервное копирование</translation>
|
<translation>Резервное копирование</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="163"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="145"/>
|
||||||
<source>About AmneziaVPN</source>
|
<source>About AmneziaVPN</source>
|
||||||
<translation>Об AmneziaVPN</translation>
|
<translation>Об AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSettings.qml" line="156"/>
|
||||||
<source>Dev console</source>
|
<source>Dev console</source>
|
||||||
<translation>Dev console</translation>
|
<translation>Dev console</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2770,14 +2763,6 @@ Thank you for staying with us!</source>
|
|||||||
<translation>Очистить логи</translation>
|
<translation>Очистить логи</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
|
||||||
<name>PageSettingsNewsNotifications</name>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsNewsNotifications.qml" line="33"/>
|
|
||||||
<source>News & Notifications</source>
|
|
||||||
<translation>Новости и Уведомления</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
<context>
|
||||||
<name>PageSettingsServerData</name>
|
<name>PageSettingsServerData</name>
|
||||||
<message>
|
<message>
|
||||||
@@ -3027,13 +3012,13 @@ Thank you for staying with us!</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="210"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="210"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="357"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="358"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продолжить</translation>
|
<translation>Продолжить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="211"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="211"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="358"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="359"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Отменить</translation>
|
<translation>Отменить</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3074,8 +3059,8 @@ Thank you for staying with us!</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="332"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="332"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="458"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="459"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="471"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="472"/>
|
||||||
<source>Sites files (*.json)</source>
|
<source>Sites files (*.json)</source>
|
||||||
<translation>Файлы сайтов (*.json)</translation>
|
<translation>Файлы сайтов (*.json)</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3085,33 +3070,33 @@ Thank you for staying with us!</source>
|
|||||||
<translation>Очистить список сайтов</translation>
|
<translation>Очистить список сайтов</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="355"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="356"/>
|
||||||
<source>Clear site list?</source>
|
<source>Clear site list?</source>
|
||||||
<translation>Очистить список сайтов?</translation>
|
<translation>Очистить список сайтов?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="356"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="357"/>
|
||||||
<source>All sites will be removed from list.</source>
|
<source>All sites will be removed from list.</source>
|
||||||
<translation>Все сайты будут удалены из списка.</translation>
|
<translation>Все сайты будут удалены из списка.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="420"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="421"/>
|
||||||
<source>Import a list of sites</source>
|
<source>Import a list of sites</source>
|
||||||
<translation>Импортировать список с сайтами</translation>
|
<translation>Импортировать список с сайтами</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="455"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="456"/>
|
||||||
<source>Replace site list</source>
|
<source>Replace site list</source>
|
||||||
<translation>Заменить список с сайтами</translation>
|
<translation>Заменить список с сайтами</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="457"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="458"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="470"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="471"/>
|
||||||
<source>Open sites file</source>
|
<source>Open sites file</source>
|
||||||
<translation>Открыть список с сайтами</translation>
|
<translation>Открыть список с сайтами</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="468"/>
|
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="469"/>
|
||||||
<source>Add imported sites to existing ones</source>
|
<source>Add imported sites to existing ones</source>
|
||||||
<translation>Добавить импортированные сайты к существующим</translation>
|
<translation>Добавить импортированные сайты к существующим</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3536,32 +3521,32 @@ Thank you for staying with us!</source>
|
|||||||
<context>
|
<context>
|
||||||
<name>PageSetupWizardViewConfig</name>
|
<name>PageSetupWizardViewConfig</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="72"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="70"/>
|
||||||
<source>New connection</source>
|
<source>New connection</source>
|
||||||
<translation>Новое соединение</translation>
|
<translation>Новое соединение</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="112"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="110"/>
|
||||||
<source>Collapse content</source>
|
<source>Collapse content</source>
|
||||||
<translation>Свернуть</translation>
|
<translation>Свернуть</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="112"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="110"/>
|
||||||
<source>Show content</source>
|
<source>Show content</source>
|
||||||
<translation>Показать</translation>
|
<translation>Показать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="129"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="127"/>
|
||||||
<source>Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.</source>
|
<source>Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.</source>
|
||||||
<translation>Включить обфускацию WireGuard. Это может быть полезно, если WireGuard блокируется вашим провайдером.</translation>
|
<translation>Включить обфускацию WireGuard. Это может быть полезно, если WireGuard блокируется вашим провайдером.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="160"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="158"/>
|
||||||
<source>Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.</source>
|
<source>Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.</source>
|
||||||
<translation>Используйте файлы конфигурации только из тех источников, которым вы доверяете. Файлы из общедоступных источников могли быть созданы с целью перехвата ваших личных данных.</translation>
|
<translation>Используйте файлы конфигурации только из тех источников, которым вы доверяете. Файлы из общедоступных источников могли быть созданы с целью перехвата ваших личных данных.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="204"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="202"/>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation>Подключиться</translation>
|
<translation>Подключиться</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4965,12 +4950,12 @@ FileZilla или другие SFTP-клиенты, а также смонтир
|
|||||||
<context>
|
<context>
|
||||||
<name>SettingsController</name>
|
<name>SettingsController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="258"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="250"/>
|
||||||
<source>All settings have been reset to default values</source>
|
<source>All settings have been reset to default values</source>
|
||||||
<translation>Все настройки сброшены до значений по умолчанию</translation>
|
<translation>Все настройки сброшены до значений по умолчанию</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="235"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="227"/>
|
||||||
<source>Backup file is corrupted</source>
|
<source>Backup file is corrupted</source>
|
||||||
<translation>Файл резервной копии поврежден</translation>
|
<translation>Файл резервной копии поврежден</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace
|
|||||||
constexpr char uuid[] = "installation_uuid";
|
constexpr char uuid[] = "installation_uuid";
|
||||||
constexpr char osVersion[] = "os_version";
|
constexpr char osVersion[] = "os_version";
|
||||||
constexpr char appVersion[] = "app_version";
|
constexpr char appVersion[] = "app_version";
|
||||||
|
constexpr char appLanguage[] = "app_language";
|
||||||
|
|
||||||
constexpr char userCountryCode[] = "user_country_code";
|
constexpr char userCountryCode[] = "user_country_code";
|
||||||
constexpr char serverCountryCode[] = "server_country_code";
|
constexpr char serverCountryCode[] = "server_country_code";
|
||||||
@@ -64,7 +65,6 @@ namespace
|
|||||||
{
|
{
|
||||||
QString osVersion;
|
QString osVersion;
|
||||||
QString appVersion;
|
QString appVersion;
|
||||||
QString appLanguage;
|
|
||||||
|
|
||||||
QString installationUuid;
|
QString installationUuid;
|
||||||
|
|
||||||
@@ -84,9 +84,6 @@ namespace
|
|||||||
if (!appVersion.isEmpty()) {
|
if (!appVersion.isEmpty()) {
|
||||||
obj[configKey::appVersion] = appVersion;
|
obj[configKey::appVersion] = appVersion;
|
||||||
}
|
}
|
||||||
if (!appLanguage.isEmpty()) {
|
|
||||||
obj[apiDefs::key::appLanguage] = appLanguage;
|
|
||||||
}
|
|
||||||
if (!installationUuid.isEmpty()) {
|
if (!installationUuid.isEmpty()) {
|
||||||
obj[configKey::uuid] = installationUuid;
|
obj[configKey::uuid] = installationUuid;
|
||||||
}
|
}
|
||||||
@@ -226,9 +223,6 @@ namespace
|
|||||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||||
apiConfig.insert(apiDefs::key::supportedProtocols,
|
apiConfig.insert(apiDefs::key::supportedProtocols,
|
||||||
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray());
|
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;
|
serverConfig[configKey::apiConfig] = apiConfig;
|
||||||
@@ -291,7 +285,6 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
|||||||
|
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
m_settings->getInstallationUuid(true),
|
m_settings->getInstallationUuid(true),
|
||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
serverCountryCode,
|
serverCountryCode,
|
||||||
@@ -332,7 +325,6 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
|||||||
|
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
m_settings->getInstallationUuid(true),
|
m_settings->getInstallationUuid(true),
|
||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
serverCountryCode,
|
serverCountryCode,
|
||||||
@@ -383,7 +375,7 @@ bool ApiConfigsController::fillAvailableServices()
|
|||||||
{
|
{
|
||||||
QJsonObject apiPayload;
|
QJsonObject apiPayload;
|
||||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||||
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
apiPayload[configKey::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||||
@@ -407,7 +399,6 @@ bool ApiConfigsController::importServiceFromGateway()
|
|||||||
{
|
{
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
m_settings->getInstallationUuid(true),
|
m_settings->getInstallationUuid(true),
|
||||||
m_apiServicesModel->getCountryCode(),
|
m_apiServicesModel->getCountryCode(),
|
||||||
"",
|
"",
|
||||||
@@ -466,7 +457,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
|||||||
|
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
m_settings->getInstallationUuid(true),
|
m_settings->getInstallationUuid(true),
|
||||||
apiConfig.value(configKey::userCountryCode).toString(),
|
apiConfig.value(configKey::userCountryCode).toString(),
|
||||||
newCountryCode,
|
newCountryCode,
|
||||||
@@ -587,7 +577,6 @@ bool ApiConfigsController::deactivateDevice(const bool isRemoveEvent)
|
|||||||
|
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
m_settings->getInstallationUuid(true),
|
m_settings->getInstallationUuid(true),
|
||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
apiConfigObject.value(configKey::serverCountryCode).toString(),
|
apiConfigObject.value(configKey::serverCountryCode).toString(),
|
||||||
@@ -627,7 +616,6 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
|
|||||||
|
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
m_settings->getAppLanguage().name().split("_").first(),
|
|
||||||
uuid,
|
uuid,
|
||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
serverCountryCode,
|
serverCountryCode,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ void ApiNewsController::fetchNews()
|
|||||||
}
|
}
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
m_settings->isStrictKillSwitchEnabled());
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
QByteArray responseBody;
|
||||||
QJsonObject payload;
|
QJsonObject payload;
|
||||||
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
|
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
|
||||||
|
|
||||||
@@ -43,9 +44,7 @@ void ApiNewsController::fetchNews()
|
|||||||
payload.insert(configKey::serviceType, stacksJson.value(configKey::serviceType));
|
payload.insert(configKey::serviceType, stacksJson.value(configKey::serviceType));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto future = gatewayController.postAsync(QString("%1v1/news"), payload);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/news"), payload, responseBody);
|
||||||
future.then(this, [this](QPair<ErrorCode, QByteArray> result) {
|
|
||||||
auto [errorCode, responseBody] = result;
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
emit errorOccurred(errorCode);
|
emit errorOccurred(errorCode);
|
||||||
return;
|
return;
|
||||||
@@ -63,6 +62,4 @@ void ApiNewsController::fetchNews()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_newsModel->updateModel(newsArray);
|
m_newsModel->updateModel(newsArray);
|
||||||
emit fetchNewsFinished();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccurred(ErrorCode errorCode);
|
void errorOccurred(ErrorCode errorCode);
|
||||||
void fetchNewsFinished();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<NewsModel> m_newsModel;
|
QSharedPointer<NewsModel> m_newsModel;
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex
|
|||||||
{
|
{
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
m_settings->isStrictKillSwitchEnabled());
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
|||||||
if (reload) {
|
if (reload) {
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
wait.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
|
||||||
@@ -62,7 +62,6 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
|||||||
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||||
apiPayload[configKey::authData] = authData;
|
apiPayload[configKey::authData] = authData;
|
||||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "settingsController.h"
|
#include "settingsController.h"
|
||||||
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QOperatingSystemVersion>
|
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "systemController.h"
|
#include "systemController.h"
|
||||||
@@ -34,17 +33,6 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
|||||||
checkIfNeedDisableLogs();
|
checkIfNeedDisableLogs();
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsController::onNotificationStateChanged);
|
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsController::onNotificationStateChanged);
|
||||||
connect(AndroidController::instance(), &AndroidController::imeInsetsChanged, this, [this](int heightDp) {
|
|
||||||
m_imeHeight = heightDp;
|
|
||||||
emit imeHeightChanged(heightDp);
|
|
||||||
emit safeAreaBottomMarginChanged();
|
|
||||||
});
|
|
||||||
connect(AndroidController::instance(), &AndroidController::systemBarsInsetsChanged, this, [this](int navBarHeightDp, int statusBarHeightDp) {
|
|
||||||
m_cachedNavigationBarHeight = navBarHeightDp;
|
|
||||||
m_cachedStatusBarHeight = statusBarHeightDp;
|
|
||||||
emit safeAreaBottomMarginChanged();
|
|
||||||
emit safeAreaTopMarginChanged();
|
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_isDevModeEnabled = m_settings->isDevGatewayEnv();
|
m_isDevModeEnabled = m_settings->isDevGatewayEnv();
|
||||||
@@ -446,11 +434,7 @@ bool SettingsController::isOnTv()
|
|||||||
bool SettingsController::isEdgeToEdgeEnabled()
|
bool SettingsController::isEdgeToEdgeEnabled()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
if (!m_edgeToEdgeCached) {
|
return AndroidController::instance()->isEdgeToEdgeEnabled();
|
||||||
m_cachedEdgeToEdgeEnabled = AndroidController::instance()->isEdgeToEdgeEnabled();
|
|
||||||
m_edgeToEdgeCached = true;
|
|
||||||
}
|
|
||||||
return m_cachedEdgeToEdgeEnabled;
|
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -496,10 +480,6 @@ int SettingsController::getSafeAreaBottomMargin()
|
|||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
if (isEdgeToEdgeEnabled()) {
|
if (isEdgeToEdgeEnabled()) {
|
||||||
if (m_imeHeight > 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int height = getNavigationBarHeight();
|
int height = getNavigationBarHeight();
|
||||||
int result = height > 0 ? height : 56; // fallback to 56 if system returns 0
|
int result = height > 0 ? height : 56; // fallback to 56 if system returns 0
|
||||||
return result;
|
return result;
|
||||||
@@ -508,11 +488,6 @@ int SettingsController::getSafeAreaBottomMargin()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SettingsController::getImeHeight()
|
|
||||||
{
|
|
||||||
return m_imeHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsController::isHomeAdLabelVisible()
|
bool SettingsController::isHomeAdLabelVisible()
|
||||||
{
|
{
|
||||||
return m_settings->isHomeAdLabelVisible();
|
return m_settings->isHomeAdLabelVisible();
|
||||||
|
|||||||
@@ -33,9 +33,8 @@ public:
|
|||||||
|
|
||||||
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
|
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
|
||||||
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
|
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
|
||||||
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged)
|
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin CONSTANT)
|
||||||
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin NOTIFY safeAreaBottomMarginChanged)
|
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin CONSTANT)
|
||||||
Q_PROPERTY(int imeHeight READ getImeHeight NOTIFY imeHeightChanged)
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void toggleAmneziaDns(bool enable);
|
void toggleAmneziaDns(bool enable);
|
||||||
@@ -104,7 +103,6 @@ public slots:
|
|||||||
int getNavigationBarHeight();
|
int getNavigationBarHeight();
|
||||||
int getSafeAreaTopMargin();
|
int getSafeAreaTopMargin();
|
||||||
int getSafeAreaBottomMargin();
|
int getSafeAreaBottomMargin();
|
||||||
int getImeHeight();
|
|
||||||
|
|
||||||
bool isHomeAdLabelVisible();
|
bool isHomeAdLabelVisible();
|
||||||
void disableHomeAdLabel();
|
void disableHomeAdLabel();
|
||||||
@@ -134,10 +132,6 @@ signals:
|
|||||||
void gatewayEndpointChanged(const QString &endpoint);
|
void gatewayEndpointChanged(const QString &endpoint);
|
||||||
void devGatewayEnvChanged(bool enabled);
|
void devGatewayEnvChanged(bool enabled);
|
||||||
|
|
||||||
void imeHeightChanged(int height);
|
|
||||||
void safeAreaTopMarginChanged();
|
|
||||||
void safeAreaBottomMarginChanged();
|
|
||||||
|
|
||||||
void isHomeAdLabelVisibleChanged(bool visible);
|
void isHomeAdLabelVisibleChanged(bool visible);
|
||||||
void startMinimizedChanged();
|
void startMinimizedChanged();
|
||||||
|
|
||||||
@@ -150,9 +144,6 @@ private:
|
|||||||
|
|
||||||
mutable int m_cachedStatusBarHeight = -1;
|
mutable int m_cachedStatusBarHeight = -1;
|
||||||
mutable int m_cachedNavigationBarHeight = -1;
|
mutable int m_cachedNavigationBarHeight = -1;
|
||||||
mutable bool m_cachedEdgeToEdgeEnabled = false;
|
|
||||||
mutable bool m_edgeToEdgeCached = false;
|
|
||||||
int m_imeHeight = 0;
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
|
||||||
QString m_appVersion;
|
QString m_appVersion;
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
return tr("Active");
|
return tr("Active");
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>")
|
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>") : tr("Active");
|
||||||
: tr("Active");
|
|
||||||
}
|
}
|
||||||
case EndDateRole: {
|
case EndDateRole: {
|
||||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||||
@@ -48,7 +47,16 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount);
|
return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount);
|
||||||
}
|
}
|
||||||
case ServiceDescriptionRole: {
|
case ServiceDescriptionRole: {
|
||||||
return m_accountInfoData.subscriptionDescription;
|
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 "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case IsComponentVisibleRole: {
|
case IsComponentVisibleRole: {
|
||||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|
||||||
@@ -93,8 +101,6 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
|||||||
|
|
||||||
accountInfoData.configType = apiUtils::getConfigType(serverConfig);
|
accountInfoData.configType = apiUtils::getConfigType(serverConfig);
|
||||||
|
|
||||||
accountInfoData.subscriptionDescription = accountInfoObject.value(apiDefs::key::subscriptionDescription).toString();
|
|
||||||
|
|
||||||
for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) {
|
for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) {
|
||||||
accountInfoData.supportedProtocols.push_back(protocol.toString());
|
accountInfoData.supportedProtocols.push_back(protocol.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ private:
|
|||||||
apiDefs::ConfigType configType;
|
apiDefs::ConfigType configType;
|
||||||
|
|
||||||
QStringList supportedProtocols;
|
QStringList supportedProtocols;
|
||||||
|
|
||||||
QString subscriptionDescription;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AccountInfoData m_accountInfoData;
|
AccountInfoData m_accountInfoData;
|
||||||
|
|||||||
@@ -158,18 +158,6 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
|
|||||||
QString primaryDns = server.value(config_key::dns1).toString();
|
QString primaryDns = server.value(config_key::dns1).toString();
|
||||||
return primaryDns == protocols::dns::amneziaDnsIp;
|
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();
|
return QVariant();
|
||||||
@@ -415,12 +403,6 @@ QHash<int, QByteArray> ServersModel::roleNames() const
|
|||||||
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
|
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
|
||||||
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
|
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
|
||||||
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
|
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
|
||||||
|
|
||||||
roles[IsAdVisibleRole] = "isAdVisible";
|
|
||||||
roles[AdHeaderRole] = "adHeader";
|
|
||||||
roles[AdDescriptionRole] = "adDescription";
|
|
||||||
roles[AdEndpointRole] = "adEndpoint";
|
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -903,18 +885,3 @@ bool ServersModel::processedServerIsPremium() const
|
|||||||
{
|
{
|
||||||
return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex));
|
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();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ public:
|
|||||||
IsCountrySelectionAvailableRole,
|
IsCountrySelectionAvailableRole,
|
||||||
ApiAvailableCountriesRole,
|
ApiAvailableCountriesRole,
|
||||||
ApiServerCountryCodeRole,
|
ApiServerCountryCodeRole,
|
||||||
IsAdVisibleRole,
|
|
||||||
AdHeaderRole,
|
|
||||||
AdDescriptionRole,
|
|
||||||
AdEndpointRole,
|
|
||||||
|
|
||||||
HasAmneziaDns
|
HasAmneziaDns
|
||||||
};
|
};
|
||||||
@@ -83,10 +79,6 @@ public:
|
|||||||
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
||||||
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged)
|
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;
|
bool processedServerIsPremium() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@@ -152,10 +144,6 @@ public slots:
|
|||||||
bool isApiKeyExpired(const int serverIndex);
|
bool isApiKeyExpired(const int serverIndex);
|
||||||
void removeApiConfig(const int serverIndex);
|
void removeApiConfig(const int serverIndex);
|
||||||
|
|
||||||
bool isAdVisible();
|
|
||||||
QString adHeader();
|
|
||||||
QString adDescription();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Shapes
|
import QtQuick.Shapes
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
@@ -12,139 +13,61 @@ import "../Controls2/TextTypes"
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property real contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
|
property real contentHeight: ad.implicitHeight + ad.anchors.topMargin + ad.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.width: 1
|
||||||
border.color: AmneziaStyle.color.onyxBlack
|
border.color: AmneziaStyle.color.goldenApricot
|
||||||
|
color: AmneziaStyle.color.transparent
|
||||||
radius: 13
|
radius: 13
|
||||||
|
|
||||||
visible: ServersModel.isAdVisible
|
visible: false
|
||||||
|
// visible: GC.isDesktop() && ServersModel.isDefaultServerFromApi
|
||||||
|
// && ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && SettingsController.isHomeAdLabelVisible
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
MouseArea {
|
||||||
FocusController.nextKeyTabItem()
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
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 {
|
RowLayout {
|
||||||
id: content
|
id: ad
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 16
|
anchors.margins: 16
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.topMargin: 12
|
|
||||||
anchors.bottomMargin: 12
|
|
||||||
spacing: 20
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
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 !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: chevronBackground
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? 1 : 0
|
|
||||||
border.color: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
source: "qrc:/images/controls/amnezia.svg"
|
||||||
source: "qrc:/images/controls/chevron-right.svg"
|
sourceSize: Qt.size(36, 36)
|
||||||
sourceSize: Qt.size(24, 24)
|
|
||||||
|
layer {
|
||||||
|
effect: ColorOverlay {
|
||||||
|
color: AmneziaStyle.color.paleGray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
CaptionTextType {
|
||||||
id: mouseArea
|
Layout.fillWidth: true
|
||||||
anchors.fill: parent
|
Layout.rightMargin: 10
|
||||||
cursorShape: Qt.PointingHandCursor
|
Layout.leftMargin: 10
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onEntered: {
|
text: qsTr("Amnezia Premium - for access to all websites and online resources")
|
||||||
chevronBackground.color = AmneziaStyle.color.slateGray
|
color: AmneziaStyle.color.pearlGray
|
||||||
|
|
||||||
|
lineHeight: 18
|
||||||
|
font.pixelSize: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: {
|
ImageButtonType {
|
||||||
chevronBackground.color = AmneziaStyle.color.transparent
|
image: "qrc:/images/controls/close.svg"
|
||||||
}
|
imageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
onPressedChanged: {
|
|
||||||
chevronBackground.color = pressed ? AmneziaStyle.color.charcoalGray : containsMouse ? AmneziaStyle.color.slateGray : AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
root.forceActiveFocus()
|
SettingsController.disableHomeAdLabel()
|
||||||
Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint"))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var listView: null
|
|
||||||
property var scrollToItemTarget: null
|
|
||||||
|
|
||||||
property Connections imeConnection: Connections {
|
|
||||||
target: SettingsController
|
|
||||||
function onImeHeightChanged() {
|
|
||||||
if (root.scrollToItemTarget && SettingsController.imeHeight > 0) {
|
|
||||||
scrollTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property Timer scrollTimer: Timer {
|
|
||||||
interval: 100
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (root.scrollToItemTarget && root.listView) {
|
|
||||||
if (SettingsController.imeHeight > 0) {
|
|
||||||
var item = root.scrollToItemTarget
|
|
||||||
var itemY = item.mapToItem(root.listView.contentItem, 0, 0).y
|
|
||||||
var itemHeight = item.height
|
|
||||||
var keyboardHeight = SettingsController.imeHeight + SettingsController.safeAreaBottomMargin
|
|
||||||
var visibleHeight = root.listView.height - keyboardHeight
|
|
||||||
|
|
||||||
var desiredTopOffset = visibleHeight * 0.25
|
|
||||||
var targetContentY = itemY - desiredTopOffset
|
|
||||||
|
|
||||||
if (targetContentY < 0) {
|
|
||||||
targetContentY = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxContentY = root.listView.contentHeight - root.listView.height
|
|
||||||
if (targetContentY > maxContentY) {
|
|
||||||
targetContentY = maxContentY
|
|
||||||
}
|
|
||||||
|
|
||||||
root.listView.contentY = targetContentY
|
|
||||||
root.scrollToItemTarget = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToItem(item) {
|
|
||||||
scrollToItemTarget = item
|
|
||||||
scrollTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -49,22 +49,6 @@ Item {
|
|||||||
return drawerContent.state === stateName
|
return drawerContent.state === stateName
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Qt.application
|
|
||||||
|
|
||||||
function onStateChanged() {
|
|
||||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
|
||||||
if (dragArea.drag.active) {
|
|
||||||
dragArea.drag.target = null
|
|
||||||
dragArea.drag.target = drawerContent
|
|
||||||
}
|
|
||||||
if (isOpened && !isCollapsedStateActive()) {
|
|
||||||
root.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: PageController
|
target: PageController
|
||||||
|
|
||||||
|
|||||||
@@ -74,18 +74,6 @@ Item {
|
|||||||
FocusController.nextKeyRightItem()
|
FocusController.nextKeyRightItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Qt.application
|
|
||||||
|
|
||||||
function onStateChanged() {
|
|
||||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
|
||||||
if (!menu.isClosed) {
|
|
||||||
menu.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: rootButtonContent.implicitWidth
|
implicitWidth: rootButtonContent.implicitWidth
|
||||||
implicitHeight: rootButtonContent.implicitHeight
|
implicitHeight: rootButtonContent.implicitHeight
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ Item {
|
|||||||
|
|
||||||
property string buttonText
|
property string buttonText
|
||||||
property string buttonImageSource
|
property string buttonImageSource
|
||||||
property string buttonImageColor: AmneziaStyle.color.midnightBlack
|
|
||||||
property string buttonBackgroundColor: AmneziaStyle.color.paleGray
|
|
||||||
property string buttonHoveredColor: AmneziaStyle.color.lightGray
|
|
||||||
property var clickedFunc
|
property var clickedFunc
|
||||||
|
|
||||||
property alias textField: textField
|
property alias textField: textField
|
||||||
@@ -70,7 +67,7 @@ Item {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Behavior on border.color {
|
Behavior on border.color {
|
||||||
PropertyAnimation { duration: 100 }
|
PropertyAnimation { duration: 200 }
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -197,14 +194,6 @@ Item {
|
|||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
text: root.buttonText
|
text: root.buttonText
|
||||||
leftImageSource: root.buttonImageSource
|
leftImageSource: root.buttonImageSource
|
||||||
leftImageColor: root.buttonImageColor
|
|
||||||
|
|
||||||
defaultColor: root.buttonBackgroundColor
|
|
||||||
hoveredColor: root.buttonHoveredColor
|
|
||||||
pressedColor: root.buttonHoveredColor
|
|
||||||
disabledColor: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
borderWidth: 0
|
|
||||||
|
|
||||||
anchors.top: content.top
|
anchors.top: content.top
|
||||||
anchors.bottom: content.bottom
|
anchors.bottom: content.bottom
|
||||||
@@ -212,7 +201,7 @@ Item {
|
|||||||
|
|
||||||
height: content.implicitHeight
|
height: content.implicitHeight
|
||||||
width: content.implicitHeight
|
width: content.implicitHeight
|
||||||
squareLeftSide: false
|
squareLeftSide: true
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||||
|
|||||||
@@ -28,7 +28,5 @@ QtObject {
|
|||||||
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
|
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
|
||||||
readonly property color pearlGray: '#EAEAEC'
|
readonly property color pearlGray: '#EAEAEC'
|
||||||
readonly property color translucentRichBrown: Qt.rgba(99/255, 51/255, 3/255, 0.26)
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ PageType {
|
|||||||
|
|
||||||
BaseHeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
Layout.topMargin: 20
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ PageType {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
anchors.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
|
|||||||
@@ -20,21 +20,6 @@ import "../Components"
|
|||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Qt.application
|
|
||||||
|
|
||||||
function onStateChanged() {
|
|
||||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
|
||||||
if (drawer.isOpened) {
|
|
||||||
drawer.closeTriggered()
|
|
||||||
}
|
|
||||||
if (homeSplitTunnelingDrawer.isOpened) {
|
|
||||||
homeSplitTunnelingDrawer.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
objectName: "pageControllerConnections"
|
objectName: "pageControllerConnections"
|
||||||
|
|
||||||
@@ -86,6 +71,16 @@ PageType {
|
|||||||
anchors.topMargin: 12 + SettingsController.safeAreaTopMargin
|
anchors.topMargin: 12 + SettingsController.safeAreaTopMargin
|
||||||
anchors.bottomMargin: 16
|
anchors.bottomMargin: 16
|
||||||
|
|
||||||
|
AdLabel {
|
||||||
|
id: adLabel
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: adLabel.contentHeight
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 22
|
||||||
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: loggingButton
|
id: loggingButton
|
||||||
objectName: "loggingButton"
|
objectName: "loggingButton"
|
||||||
@@ -194,16 +189,6 @@ PageType {
|
|||||||
parent: root
|
parent: root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AdLabel {
|
|
||||||
id: adLabel
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: adLabel.contentHeight
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.topMargin: 22
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ PageType {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
anchors.topMargin: 20
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
onActiveFocusChanged: {
|
||||||
if(backButton.enabled && backButton.activeFocus) {
|
if(backButton.enabled && backButton.activeFocus) {
|
||||||
@@ -31,11 +31,6 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SmartScroll {
|
|
||||||
id: smartScroll
|
|
||||||
listView: listView
|
|
||||||
}
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
@@ -85,13 +80,6 @@ PageType {
|
|||||||
clientMtu = textField.text
|
clientMtu = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(mtuTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +97,6 @@ PageType {
|
|||||||
clientJunkPacketCount = textField.text
|
clientJunkPacketCount = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketCountTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -131,12 +113,6 @@ PageType {
|
|||||||
clientJunkPacketMinSize = textField.text
|
clientJunkPacketMinSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketMinSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -153,12 +129,6 @@ PageType {
|
|||||||
clientJunkPacketMaxSize = textField.text
|
clientJunkPacketMaxSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -177,12 +147,6 @@ PageType {
|
|||||||
clientSpecialJunk1 = textField.text
|
clientSpecialJunk1 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(specialJunk1TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -201,12 +165,6 @@ PageType {
|
|||||||
clientSpecialJunk2 = textField.text
|
clientSpecialJunk2 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(specialJunk2TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -225,12 +183,6 @@ PageType {
|
|||||||
clientSpecialJunk3 = textField.text
|
clientSpecialJunk3 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(specialJunk3TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -249,12 +201,6 @@ PageType {
|
|||||||
clientSpecialJunk4 = textField.text
|
clientSpecialJunk4 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(specialJunk4TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -273,12 +219,6 @@ PageType {
|
|||||||
clientSpecialJunk5 = textField.text
|
clientSpecialJunk5 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(specialJunk5TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -297,12 +237,6 @@ PageType {
|
|||||||
clientControlledJunk1 = textField.text
|
clientControlledJunk1 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(controlledJunk1TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -321,12 +255,6 @@ PageType {
|
|||||||
clientControlledJunk2 = textField.text
|
clientControlledJunk2 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(controlledJunk2TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -345,12 +273,6 @@ PageType {
|
|||||||
clientControlledJunk3 = textField.text
|
clientControlledJunk3 = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(controlledJunk3TextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -368,12 +290,6 @@ PageType {
|
|||||||
clientSpecialHandshakeTimeout = textField.text
|
clientSpecialHandshakeTimeout = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(iTimeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2TextType {
|
Header2TextType {
|
||||||
|
|||||||
@@ -34,11 +34,6 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SmartScroll {
|
|
||||||
id: smartScroll
|
|
||||||
listView: listView
|
|
||||||
}
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
@@ -86,12 +81,6 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(vpnAddressSubnetTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,12 +104,6 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(portTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +121,6 @@ PageType {
|
|||||||
serverJunkPacketCount = textField.text
|
serverJunkPacketCount = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketCountTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -160,12 +137,6 @@ PageType {
|
|||||||
serverJunkPacketMinSize = textField.text
|
serverJunkPacketMinSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketMinSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -182,12 +153,6 @@ PageType {
|
|||||||
serverJunkPacketMaxSize = textField.text
|
serverJunkPacketMaxSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -204,12 +169,6 @@ PageType {
|
|||||||
serverInitPacketJunkSize = textField.text
|
serverInitPacketJunkSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(initPacketJunkSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -226,12 +185,6 @@ PageType {
|
|||||||
serverResponsePacketJunkSize = textField.text
|
serverResponsePacketJunkSize = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(responsePacketJunkSizeTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AwgTextField {
|
// AwgTextField {
|
||||||
@@ -280,12 +233,6 @@ PageType {
|
|||||||
serverInitPacketMagicHeader = textField.text
|
serverInitPacketMagicHeader = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(initPacketMagicHeaderTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -302,12 +249,6 @@ PageType {
|
|||||||
serverResponsePacketMagicHeader = textField.text
|
serverResponsePacketMagicHeader = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(responsePacketMagicHeaderTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -324,12 +265,6 @@ PageType {
|
|||||||
serverUnderloadPacketMagicHeader = textField.text
|
serverUnderloadPacketMagicHeader = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(underloadPacketMagicHeaderTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AwgTextField {
|
AwgTextField {
|
||||||
@@ -346,12 +281,6 @@ PageType {
|
|||||||
serverTransportPacketMagicHeader = textField.text
|
serverTransportPacketMagicHeader = textField.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onActiveFocusChanged: {
|
|
||||||
if (textField.activeFocus) {
|
|
||||||
smartScroll.scrollToItem(transportPacketMagicHeaderTextField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
|||||||
@@ -14,19 +14,6 @@ import "../Config"
|
|||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: ApiNewsController
|
|
||||||
function onFetchNewsFinished() {
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onErrorOccurred(errorCode) {
|
|
||||||
PageController.showErrorMessage(errorCode)
|
|
||||||
PageController.closePage()
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
@@ -153,8 +140,9 @@ PageType {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
ApiNewsController.fetchNews()
|
ApiNewsController.fetchNews();
|
||||||
PageController.goToPage(PageEnum.PageSettingsNewsNotifications)
|
PageController.goToPage(PageEnum.PageSettingsNewsNotifications)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ PageType {
|
|||||||
id: backButton
|
id: backButton
|
||||||
objectName: "backButton"
|
objectName: "backButton"
|
||||||
|
|
||||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderTypeWithButton {
|
HeaderTypeWithButton {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ PageType {
|
|||||||
|
|
||||||
text: countryName
|
text: countryName
|
||||||
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||||
hideDescription: isWorkerExpired ? false : true
|
hideDescription: isWorkerExpired ? true : false
|
||||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ PageType {
|
|||||||
width: root.width
|
width: root.width
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|||||||
@@ -165,11 +165,9 @@ PageType {
|
|||||||
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
|
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
|
||||||
|
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: addAppButton.top
|
||||||
anchors.bottomMargin: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
clip: true
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
model: SortFilterProxyModel {
|
||||||
id: proxyAppSplitTunnelingModel
|
id: proxyAppSplitTunnelingModel
|
||||||
@@ -217,14 +215,11 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.left: parent.left
|
anchors.fill: addAppButton
|
||||||
anchors.right: parent.right
|
anchors.bottomMargin: -24 - SettingsController.safeAreaBottomMargin
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
height: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.midnightBlack
|
color: AmneziaStyle.color.midnightBlack
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: addAppButton
|
id: addAppButton
|
||||||
@@ -267,7 +262,6 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
InstalledAppsDrawer {
|
InstalledAppsDrawer {
|
||||||
id: installedAppDrawer
|
id: installedAppDrawer
|
||||||
|
|||||||
@@ -168,13 +168,11 @@ PageType {
|
|||||||
|
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: addSiteButton.top
|
||||||
anchors.bottomMargin: addSiteButton.implicitHeight + 48 + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
enabled: root.pageEnabled
|
enabled: root.pageEnabled
|
||||||
clip: true
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
model: SortFilterProxyModel {
|
||||||
id: proxySitesModel
|
id: proxySitesModel
|
||||||
@@ -233,14 +231,11 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.left: parent.left
|
anchors.fill: addSiteButton
|
||||||
anchors.right: parent.right
|
anchors.bottomMargin: -24
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
height: addSiteButton.implicitHeight + 48
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.midnightBlack
|
color: AmneziaStyle.color.midnightBlack
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: addSiteButton
|
id: addSiteButton
|
||||||
@@ -288,7 +283,6 @@ PageType {
|
|||||||
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
id: moreActionsDrawer
|
id: moreActionsDrawer
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ PageType {
|
|||||||
|
|
||||||
BaseHeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
Layout.topMargin: 20
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
@@ -147,7 +147,6 @@ PageType {
|
|||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
|
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ PageType {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
|
||||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
anchors.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ PageType {
|
|||||||
source: "qrc:/images/amneziaBigLogo.png"
|
source: "qrc:/images/amneziaBigLogo.png"
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
Layout.topMargin: 32 + SettingsController.safeAreaTopMargin
|
Layout.topMargin: 32
|
||||||
Layout.preferredWidth: 360
|
Layout.preferredWidth: 360
|
||||||
Layout.preferredHeight: 287
|
Layout.preferredHeight: 287
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ PageType {
|
|||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: startButton
|
id: startButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.bottomMargin: 48 + SettingsController.safeAreaBottomMargin
|
Layout.bottomMargin: 48
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|||||||
@@ -305,9 +305,6 @@ PageType {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
// Also adjust TabBar position when keyboard appears (Android 14+ workaround)
|
|
||||||
anchors.bottomMargin: SettingsController.imeHeight
|
|
||||||
|
|
||||||
topPadding: 8
|
topPadding: 8
|
||||||
bottomPadding: 8 + SettingsController.safeAreaBottomMargin
|
bottomPadding: 8 + SettingsController.safeAreaBottomMargin
|
||||||
leftPadding: 96
|
leftPadding: 96
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ Window {
|
|||||||
id: privateKeyPassphraseDrawer
|
id: privateKeyPassphraseDrawer
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin + SettingsController.imeHeight
|
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
expandedStateContent: ColumnLayout {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
|||||||
Reference in New Issue
Block a user