mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-21 02:01:03 +07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3122c02f13 | |||
| 61e7cd073a | |||
| 2e7e65c1e0 | |||
| 3a6d160bce |
@@ -470,7 +470,7 @@ jobs:
|
||||
|
||||
env:
|
||||
ANDROID_BUILD_PLATFORM: android-36
|
||||
QT_VERSION: 6.8.3
|
||||
QT_VERSION: 6.9.3
|
||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.11.3)
|
||||
set(AMNEZIAVPN_VERSION 4.8.11.0)
|
||||
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2098)
|
||||
set(APP_ANDROID_VERSION_CODE 2095)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|
||||
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="adjustResize|stateUnchanged"
|
||||
android:windowSoftInputMode="stateUnchanged|adjustResize"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:exported="true">
|
||||
|
||||
@@ -215,4 +215,4 @@
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -35,10 +35,6 @@ import android.widget.Toast
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.RequiresApi
|
||||
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 java.io.IOException
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
@@ -304,11 +300,6 @@ class AmneziaActivity : QtActivity() {
|
||||
isAppearanceLightStatusBars = 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 {
|
||||
window.apply {
|
||||
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() {
|
||||
Log.d(TAG, "Destroy Amnezia activity")
|
||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
||||
|
||||
@@ -28,7 +28,4 @@ object QtAndroidController {
|
||||
external fun onAuthResult(result: 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_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
||||
set(OPENVPN_FRAMEWORK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios")
|
||||
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/)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
|
||||
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 osVersion("os_version");
|
||||
constexpr QLatin1String appLanguage("app_language");
|
||||
|
||||
constexpr QLatin1String availableCountries("available_countries");
|
||||
constexpr QLatin1String activeDeviceCount("active_device_count");
|
||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
||||
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
||||
constexpr QLatin1String issuedConfigs("issued_configs");
|
||||
constexpr QLatin1String subscriptionDescription("subscription_description");
|
||||
|
||||
constexpr QLatin1String supportInfo("support_info");
|
||||
constexpr QLatin1String email("email");
|
||||
@@ -70,12 +68,6 @@ namespace apiDefs
|
||||
constexpr QLatin1String transactionId("transaction_id");
|
||||
|
||||
constexpr QLatin1String userCountryCode("user_country_code");
|
||||
|
||||
constexpr QLatin1String serviceInfo("service_info");
|
||||
constexpr QLatin1String isAdVisible("is_ad_visible");
|
||||
constexpr QLatin1String adHeader("ad_header");
|
||||
constexpr QLatin1String adDescription("ad_description");
|
||||
constexpr QLatin1String adEndpoint("ad_endpoint");
|
||||
}
|
||||
|
||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QPromise>
|
||||
#include <QUrl>
|
||||
|
||||
#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
|
||||
IosController::Instance()->requestInetAccess();
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
encRequestData.request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||
encRequestData.request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
encRequestData.request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
|
||||
encRequestData.request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
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
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_isStrictKillSwitchEnabled) {
|
||||
QString host = QUrl(encRequestData.request.url()).host();
|
||||
QString host = QUrl(request.url()).host();
|
||||
QString ip = NetworkUtilities::getIPAddress(host);
|
||||
if (!ip.isEmpty()) {
|
||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
||||
@@ -78,14 +76,14 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
||||
#endif
|
||||
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
encRequestData.key = blockCipher.generatePrivateSalt(32);
|
||||
encRequestData.iv = blockCipher.generatePrivateSalt(32);
|
||||
encRequestData.salt = blockCipher.generatePrivateSalt(8);
|
||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||
QByteArray salt = blockCipher.generatePrivateSalt(8);
|
||||
|
||||
QJsonObject keyPayload;
|
||||
keyPayload[configKey::aesKey] = QString(encRequestData.key.toBase64());
|
||||
keyPayload[configKey::aesIv] = QString(encRequestData.iv.toBase64());
|
||||
keyPayload[configKey::aesSalt] = QString(encRequestData.salt.toBase64());
|
||||
keyPayload[configKey::aesKey] = QString(key.toBase64());
|
||||
keyPayload[configKey::aesIv] = QString(iv.toBase64());
|
||||
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
|
||||
|
||||
QByteArray encryptedKeyPayload;
|
||||
QByteArray encryptedApiPayload;
|
||||
@@ -100,44 +98,31 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
||||
} catch (...) {
|
||||
Utils::logException();
|
||||
qCritical() << "error loading public key from environment variables";
|
||||
encRequestData.errorCode = ErrorCode::ApiMissingAgwPublicKey;
|
||||
return encRequestData;
|
||||
return ErrorCode::ApiMissingAgwPublicKey;
|
||||
}
|
||||
|
||||
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
||||
EVP_PKEY_free(publicKey);
|
||||
|
||||
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), encRequestData.key, encRequestData.iv, "", encRequestData.salt);
|
||||
} catch (...) {
|
||||
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
|
||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||
Utils::logException();
|
||||
qCritical() << "error when encrypting the request body";
|
||||
encRequestData.errorCode = ErrorCode::ApiConfigDecryptionError;
|
||||
return encRequestData;
|
||||
return ErrorCode::ApiConfigDecryptionError;
|
||||
}
|
||||
|
||||
QJsonObject requestBody;
|
||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||
|
||||
encRequestData.requestBody = 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);
|
||||
QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
|
||||
QEventLoop wait;
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
|
||||
QList<QSslError> sslErrors;
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
|
||||
QByteArray encryptedResponseBody = reply->readAll();
|
||||
QString replyErrorString = reply->errorString();
|
||||
@@ -146,19 +131,19 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, encRequestData.key, encRequestData.iv, encRequestData.salt)) {
|
||||
auto requestFunction = [&encRequestData, &encryptedResponseBody](const QString &url) {
|
||||
encRequestData.request.setUrl(url);
|
||||
return amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
|
||||
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
||||
request.setUrl(url);
|
||||
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
};
|
||||
|
||||
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &encRequestData,
|
||||
this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
|
||||
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &key, &iv,
|
||||
&salt, this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
|
||||
encryptedResponseBody = reply->readAll();
|
||||
replyErrorString = reply->errorString();
|
||||
replyError = reply->error();
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
@@ -176,8 +161,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
}
|
||||
|
||||
try {
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, encRequestData.key, encRequestData.iv, "", encRequestData.salt);
|
||||
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||
return ErrorCode::NoError;
|
||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||
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)
|
||||
{
|
||||
QNetworkRequest request;
|
||||
@@ -273,7 +206,7 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
|
||||
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
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);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
|
||||
auto result = replyProcessingFunction(reply, sslErrors);
|
||||
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::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
reply->deleteLater();
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#ifndef GATEWAYCONTROLLER_H
|
||||
#define GATEWAYCONTROLLER_H
|
||||
|
||||
#include <QFuture>
|
||||
#include <QNetworkReply>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
|
||||
#include "core/defs.h"
|
||||
|
||||
@@ -21,20 +19,8 @@ public:
|
||||
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
||||
|
||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||
QFuture<QPair<amnezia::ErrorCode, QByteArray>> postAsync(const QString &endpoint, const QJsonObject apiPayload);
|
||||
|
||||
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);
|
||||
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
|
||||
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)},
|
||||
{"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)},
|
||||
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
|
||||
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)},
|
||||
{"onImeInsetsChanged", "(I)V", reinterpret_cast<void *>(onImeInsetsChanged)},
|
||||
{"onSystemBarsInsetsChanged", "(II)V", reinterpret_cast<void *>(onSystemBarsInsetsChanged)}
|
||||
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
|
||||
};
|
||||
|
||||
QJniEnvironment env;
|
||||
@@ -538,23 +536,3 @@ bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring 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 initConnectionState(Vpn::ConnectionState state);
|
||||
void authenticationResult(bool result);
|
||||
void imeInsetsChanged(int heightDp);
|
||||
void systemBarsInsetsChanged(int navBarHeightDp, int statusBarHeightDp);
|
||||
|
||||
private:
|
||||
bool isWaitingStatus = true;
|
||||
@@ -103,8 +101,6 @@ private:
|
||||
static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri);
|
||||
static void onAuthResult(JNIEnv *env, jobject thiz, jboolean result);
|
||||
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>
|
||||
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/AwgTextField.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
||||
<file>ui/qml/Components/SmartScroll.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/countriesFlags">
|
||||
<file>images/flagKit/ZW.svg</file>
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
<context>
|
||||
<name>AdLabel</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/AdLabel.qml" line="57"/>
|
||||
<source>Amnezia Premium - for access to all websites and online resources</source>
|
||||
<translation type="vanished">Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам</translation>
|
||||
<translation>Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -60,7 +61,7 @@
|
||||
<name>ApiAccountInfoModel</name>
|
||||
<message>
|
||||
<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>
|
||||
<translation>Активна</translation>
|
||||
</message>
|
||||
@@ -70,33 +71,35 @@
|
||||
<translation>Не активна</translation>
|
||||
</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>
|
||||
<translation>%1 из %2</translation>
|
||||
</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>
|
||||
<translation type="vanished">Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с</translation>
|
||||
<translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с</translation>
|
||||
</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>
|
||||
<translation type="vanished">Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф.</translation>
|
||||
<translation>Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ApiConfigsController</name>
|
||||
<message>
|
||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="448"/>
|
||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="413"/>
|
||||
<source>%1 installed successfully.</source>
|
||||
<translation>%1 успешно установлен.</translation>
|
||||
</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>
|
||||
<translation>Конфигурация API перезагружена</translation>
|
||||
</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>
|
||||
<translation>Страна подключения изменена на %1</translation>
|
||||
</message>
|
||||
@@ -624,32 +627,27 @@ Thank you for staying with us!</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="92"/>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="102"/>
|
||||
<source>Logging enabled</source>
|
||||
<translation>Логирование включено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="120"/>
|
||||
<source>Dev gateway enabled</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="162"/>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="144"/>
|
||||
<source>Split tunneling enabled</source>
|
||||
<translation>Раздельное туннелирование включено</translation>
|
||||
</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>
|
||||
<translation>Раздельное туннелирование выключено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="409"/>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="381"/>
|
||||
<source>VPN protocol</source>
|
||||
<translation>VPN-протокол</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="462"/>
|
||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="434"/>
|
||||
<source>Servers</source>
|
||||
<translation>Серверы</translation>
|
||||
</message>
|
||||
@@ -1581,37 +1579,32 @@ Thank you for staying with us!</source>
|
||||
<translation>Настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="102"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="101"/>
|
||||
<source>Servers</source>
|
||||
<translation>Серверы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="113"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="112"/>
|
||||
<source>Connection</source>
|
||||
<translation>Соединение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="124"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="123"/>
|
||||
<source>Application</source>
|
||||
<translation>Приложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="135"/>
|
||||
<source>News & Notifications</source>
|
||||
<translation>Новости и Уведомления</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="152"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="134"/>
|
||||
<source>Backup</source>
|
||||
<translation>Резервное копирование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="163"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="145"/>
|
||||
<source>About AmneziaVPN</source>
|
||||
<translation>Об AmneziaVPN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="174"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettings.qml" line="156"/>
|
||||
<source>Dev console</source>
|
||||
<translation>Dev console</translation>
|
||||
</message>
|
||||
@@ -2770,14 +2763,6 @@ Thank you for staying with us!</source>
|
||||
<translation>Очистить логи</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsNewsNotifications</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsNewsNotifications.qml" line="33"/>
|
||||
<source>News & Notifications</source>
|
||||
<translation>Новости и Уведомления</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsServerData</name>
|
||||
<message>
|
||||
@@ -3027,13 +3012,13 @@ Thank you for staying with us!</source>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
@@ -3074,8 +3059,8 @@ Thank you for staying with us!</source>
|
||||
</message>
|
||||
<message>
|
||||
<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="471"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="459"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="472"/>
|
||||
<source>Sites files (*.json)</source>
|
||||
<translation>Файлы сайтов (*.json)</translation>
|
||||
</message>
|
||||
@@ -3085,33 +3070,33 @@ Thank you for staying with us!</source>
|
||||
<translation>Очистить список сайтов</translation>
|
||||
</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>
|
||||
<translation>Очистить список сайтов?</translation>
|
||||
</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>
|
||||
<translation>Все сайты будут удалены из списка.</translation>
|
||||
</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>
|
||||
<translation>Импортировать список с сайтами</translation>
|
||||
</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>
|
||||
<translation>Заменить список с сайтами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="457"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="470"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="458"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="471"/>
|
||||
<source>Open sites file</source>
|
||||
<translation>Открыть список с сайтами</translation>
|
||||
</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>
|
||||
<translation>Добавить импортированные сайты к существующим</translation>
|
||||
</message>
|
||||
@@ -3536,32 +3521,32 @@ Thank you for staying with us!</source>
|
||||
<context>
|
||||
<name>PageSetupWizardViewConfig</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="72"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="70"/>
|
||||
<source>New connection</source>
|
||||
<translation>Новое соединение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="112"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="110"/>
|
||||
<source>Collapse content</source>
|
||||
<translation>Свернуть</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="112"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="110"/>
|
||||
<source>Show content</source>
|
||||
<translation>Показать</translation>
|
||||
</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>
|
||||
<translation>Включить обфускацию WireGuard. Это может быть полезно, если WireGuard блокируется вашим провайдером.</translation>
|
||||
</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>
|
||||
<translation>Используйте файлы конфигурации только из тех источников, которым вы доверяете. Файлы из общедоступных источников могли быть созданы с целью перехвата ваших личных данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="204"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="202"/>
|
||||
<source>Connect</source>
|
||||
<translation>Подключиться</translation>
|
||||
</message>
|
||||
@@ -4965,12 +4950,12 @@ FileZilla или другие SFTP-клиенты, а также смонтир
|
||||
<context>
|
||||
<name>SettingsController</name>
|
||||
<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>
|
||||
<translation>Все настройки сброшены до значений по умолчанию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/settingsController.cpp" line="235"/>
|
||||
<location filename="../ui/controllers/settingsController.cpp" line="227"/>
|
||||
<source>Backup file is corrupted</source>
|
||||
<translation>Файл резервной копии поврежден</translation>
|
||||
</message>
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace
|
||||
constexpr char uuid[] = "installation_uuid";
|
||||
constexpr char osVersion[] = "os_version";
|
||||
constexpr char appVersion[] = "app_version";
|
||||
constexpr char appLanguage[] = "app_language";
|
||||
|
||||
constexpr char userCountryCode[] = "user_country_code";
|
||||
constexpr char serverCountryCode[] = "server_country_code";
|
||||
@@ -64,7 +65,6 @@ namespace
|
||||
{
|
||||
QString osVersion;
|
||||
QString appVersion;
|
||||
QString appLanguage;
|
||||
|
||||
QString installationUuid;
|
||||
|
||||
@@ -84,9 +84,6 @@ namespace
|
||||
if (!appVersion.isEmpty()) {
|
||||
obj[configKey::appVersion] = appVersion;
|
||||
}
|
||||
if (!appLanguage.isEmpty()) {
|
||||
obj[apiDefs::key::appLanguage] = appLanguage;
|
||||
}
|
||||
if (!installationUuid.isEmpty()) {
|
||||
obj[configKey::uuid] = installationUuid;
|
||||
}
|
||||
@@ -226,9 +223,6 @@ namespace
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||
apiConfig.insert(apiDefs::key::supportedProtocols,
|
||||
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray());
|
||||
|
||||
apiConfig.insert(apiDefs::key::serviceInfo,
|
||||
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::serviceInfo).toObject());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
@@ -291,7 +285,6 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
@@ -332,7 +325,6 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
@@ -383,7 +375,7 @@ bool ApiConfigsController::fillAvailableServices()
|
||||
{
|
||||
QJsonObject apiPayload;
|
||||
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;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||
@@ -407,7 +399,6 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||
{
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
m_settings->getInstallationUuid(true),
|
||||
m_apiServicesModel->getCountryCode(),
|
||||
"",
|
||||
@@ -466,7 +457,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfig.value(configKey::userCountryCode).toString(),
|
||||
newCountryCode,
|
||||
@@ -587,7 +577,6 @@ bool ApiConfigsController::deactivateDevice(const bool isRemoveEvent)
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
apiConfigObject.value(configKey::serverCountryCode).toString(),
|
||||
@@ -627,7 +616,6 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getAppLanguage().name().split("_").first(),
|
||||
uuid,
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
|
||||
@@ -32,6 +32,7 @@ void ApiNewsController::fetchNews()
|
||||
}
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QByteArray responseBody;
|
||||
QJsonObject payload;
|
||||
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
|
||||
|
||||
@@ -43,26 +44,22 @@ void ApiNewsController::fetchNews()
|
||||
payload.insert(configKey::serviceType, stacksJson.value(configKey::serviceType));
|
||||
}
|
||||
|
||||
auto future = gatewayController.postAsync(QString("%1v1/news"), payload);
|
||||
future.then(this, [this](QPair<ErrorCode, QByteArray> result) {
|
||||
auto [errorCode, responseBody] = result;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return;
|
||||
}
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/news"), payload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
|
||||
QJsonArray newsArray;
|
||||
if (doc.isArray()) {
|
||||
newsArray = doc.array();
|
||||
} else if (doc.isObject()) {
|
||||
QJsonObject obj = doc.object();
|
||||
if (obj.value("news").isArray()) {
|
||||
newsArray = obj.value("news").toArray();
|
||||
}
|
||||
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
|
||||
QJsonArray newsArray;
|
||||
if (doc.isArray()) {
|
||||
newsArray = doc.array();
|
||||
} else if (doc.isObject()) {
|
||||
QJsonObject obj = doc.object();
|
||||
if (obj.value("news").isArray()) {
|
||||
newsArray = obj.value("news").toArray();
|
||||
}
|
||||
}
|
||||
|
||||
m_newsModel->updateModel(newsArray);
|
||||
emit fetchNewsFinished();
|
||||
});
|
||||
m_newsModel->updateModel(newsArray);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
void fetchNewsFinished();
|
||||
|
||||
private:
|
||||
QSharedPointer<NewsModel> m_newsModel;
|
||||
|
||||
@@ -82,7 +82,7 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex
|
||||
{
|
||||
QEventLoop wait;
|
||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
@@ -46,7 +46,7 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
if (reload) {
|
||||
QEventLoop wait;
|
||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||
wait.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
wait.exec();
|
||||
}
|
||||
|
||||
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::authData] = authData;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
||||
|
||||
QByteArray responseBody;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "settingsController.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QOperatingSystemVersion>
|
||||
|
||||
#include "logger.h"
|
||||
#include "systemController.h"
|
||||
@@ -34,17 +33,6 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
||||
checkIfNeedDisableLogs();
|
||||
#ifdef Q_OS_ANDROID
|
||||
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
|
||||
|
||||
m_isDevModeEnabled = m_settings->isDevGatewayEnv();
|
||||
@@ -446,11 +434,7 @@ bool SettingsController::isOnTv()
|
||||
bool SettingsController::isEdgeToEdgeEnabled()
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (!m_edgeToEdgeCached) {
|
||||
m_cachedEdgeToEdgeEnabled = AndroidController::instance()->isEdgeToEdgeEnabled();
|
||||
m_edgeToEdgeCached = true;
|
||||
}
|
||||
return m_cachedEdgeToEdgeEnabled;
|
||||
return AndroidController::instance()->isEdgeToEdgeEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -496,10 +480,6 @@ int SettingsController::getSafeAreaBottomMargin()
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (isEdgeToEdgeEnabled()) {
|
||||
if (m_imeHeight > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int height = getNavigationBarHeight();
|
||||
int result = height > 0 ? height : 56; // fallback to 56 if system returns 0
|
||||
return result;
|
||||
@@ -508,11 +488,6 @@ int SettingsController::getSafeAreaBottomMargin()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SettingsController::getImeHeight()
|
||||
{
|
||||
return m_imeHeight;
|
||||
}
|
||||
|
||||
bool SettingsController::isHomeAdLabelVisible()
|
||||
{
|
||||
return m_settings->isHomeAdLabelVisible();
|
||||
|
||||
@@ -33,9 +33,8 @@ public:
|
||||
|
||||
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
|
||||
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
|
||||
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged)
|
||||
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin NOTIFY safeAreaBottomMarginChanged)
|
||||
Q_PROPERTY(int imeHeight READ getImeHeight NOTIFY imeHeightChanged)
|
||||
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin CONSTANT)
|
||||
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin CONSTANT)
|
||||
|
||||
public slots:
|
||||
void toggleAmneziaDns(bool enable);
|
||||
@@ -104,7 +103,6 @@ public slots:
|
||||
int getNavigationBarHeight();
|
||||
int getSafeAreaTopMargin();
|
||||
int getSafeAreaBottomMargin();
|
||||
int getImeHeight();
|
||||
|
||||
bool isHomeAdLabelVisible();
|
||||
void disableHomeAdLabel();
|
||||
@@ -133,10 +131,6 @@ signals:
|
||||
void devModeEnabled();
|
||||
void gatewayEndpointChanged(const QString &endpoint);
|
||||
void devGatewayEnvChanged(bool enabled);
|
||||
|
||||
void imeHeightChanged(int height);
|
||||
void safeAreaTopMarginChanged();
|
||||
void safeAreaBottomMarginChanged();
|
||||
|
||||
void isHomeAdLabelVisibleChanged(bool visible);
|
||||
void startMinimizedChanged();
|
||||
@@ -150,9 +144,6 @@ private:
|
||||
|
||||
mutable int m_cachedStatusBarHeight = -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;
|
||||
|
||||
QString m_appVersion;
|
||||
|
||||
@@ -31,8 +31,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||
return tr("Active");
|
||||
}
|
||||
|
||||
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>")
|
||||
: tr("Active");
|
||||
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>") : tr("Active");
|
||||
}
|
||||
case EndDateRole: {
|
||||
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);
|
||||
}
|
||||
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: {
|
||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|
||||
@@ -93,8 +101,6 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
||||
|
||||
accountInfoData.configType = apiUtils::getConfigType(serverConfig);
|
||||
|
||||
accountInfoData.subscriptionDescription = accountInfoObject.value(apiDefs::key::subscriptionDescription).toString();
|
||||
|
||||
for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) {
|
||||
accountInfoData.supportedProtocols.push_back(protocol.toString());
|
||||
}
|
||||
|
||||
@@ -54,8 +54,6 @@ private:
|
||||
apiDefs::ConfigType configType;
|
||||
|
||||
QStringList supportedProtocols;
|
||||
|
||||
QString subscriptionDescription;
|
||||
};
|
||||
|
||||
AccountInfoData m_accountInfoData;
|
||||
|
||||
@@ -158,18 +158,6 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
|
||||
QString primaryDns = server.value(config_key::dns1).toString();
|
||||
return primaryDns == protocols::dns::amneziaDnsIp;
|
||||
}
|
||||
case IsAdVisibleRole:{
|
||||
return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::isAdVisible).toBool(false);
|
||||
}
|
||||
case AdHeaderRole: {
|
||||
return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adHeader).toString();
|
||||
}
|
||||
case AdDescriptionRole: {
|
||||
return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adDescription).toString();
|
||||
}
|
||||
case AdEndpointRole: {
|
||||
return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adEndpoint).toString();
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -415,12 +403,6 @@ QHash<int, QByteArray> ServersModel::roleNames() const
|
||||
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
|
||||
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
|
||||
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
|
||||
|
||||
roles[IsAdVisibleRole] = "isAdVisible";
|
||||
roles[AdHeaderRole] = "adHeader";
|
||||
roles[AdDescriptionRole] = "adDescription";
|
||||
roles[AdEndpointRole] = "adEndpoint";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -802,22 +784,22 @@ void ServersModel::recomputeGatewayStacks()
|
||||
const bool wasEmpty = m_gatewayStacks.isEmpty();
|
||||
GatewayStacks computed;
|
||||
bool hasNewTags = false;
|
||||
|
||||
|
||||
for (int i = 0; i < m_servers.count(); ++i) {
|
||||
if (data(i, IsServerFromGatewayApiRole).toBool()) {
|
||||
const QJsonObject server = m_servers.at(i).toObject();
|
||||
const QJsonObject apiConfig = server.value(configKey::apiConfig).toObject();
|
||||
|
||||
|
||||
const QString userCountryCode = apiConfig.value(configKey::userCountryCode).toString();
|
||||
const QString serviceType = apiConfig.value(configKey::serviceType).toString();
|
||||
|
||||
|
||||
if (!userCountryCode.isEmpty()) {
|
||||
if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) {
|
||||
hasNewTags = true;
|
||||
}
|
||||
computed.userCountryCodes.insert(userCountryCode);
|
||||
}
|
||||
|
||||
|
||||
if (!serviceType.isEmpty()) {
|
||||
if (!m_gatewayStacks.serviceTypes.contains(serviceType)) {
|
||||
hasNewTags = true;
|
||||
@@ -826,12 +808,12 @@ void ServersModel::recomputeGatewayStacks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_gatewayStacks = std::move(computed);
|
||||
if (hasNewTags) {
|
||||
emit gatewayStacksExpanded();
|
||||
}
|
||||
|
||||
|
||||
if (wasEmpty != m_gatewayStacks.isEmpty()) {
|
||||
emit hasServersFromGatewayApiChanged();
|
||||
}
|
||||
@@ -903,18 +885,3 @@ bool ServersModel::processedServerIsPremium() const
|
||||
{
|
||||
return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex));
|
||||
}
|
||||
|
||||
bool ServersModel::isAdVisible()
|
||||
{
|
||||
return data(m_defaultServerIndex, IsAdVisibleRole).toBool();
|
||||
}
|
||||
|
||||
QString ServersModel::adHeader()
|
||||
{
|
||||
return data(m_defaultServerIndex, AdHeaderRole).toString();
|
||||
}
|
||||
|
||||
QString ServersModel::adDescription()
|
||||
{
|
||||
return data(m_defaultServerIndex, AdDescriptionRole).toString();
|
||||
}
|
||||
|
||||
@@ -47,10 +47,6 @@ public:
|
||||
IsCountrySelectionAvailableRole,
|
||||
ApiAvailableCountriesRole,
|
||||
ApiServerCountryCodeRole,
|
||||
IsAdVisibleRole,
|
||||
AdHeaderRole,
|
||||
AdDescriptionRole,
|
||||
AdEndpointRole,
|
||||
|
||||
HasAmneziaDns
|
||||
};
|
||||
@@ -83,10 +79,6 @@ public:
|
||||
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
||||
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged)
|
||||
|
||||
Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIndexChanged)
|
||||
Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIndexChanged)
|
||||
Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIndexChanged)
|
||||
|
||||
bool processedServerIsPremium() const;
|
||||
|
||||
public slots:
|
||||
@@ -152,10 +144,6 @@ public slots:
|
||||
bool isApiKeyExpired(const int serverIndex);
|
||||
void removeApiConfig(const int serverIndex);
|
||||
|
||||
bool isAdVisible();
|
||||
QString adHeader();
|
||||
QString adDescription();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Shapes
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
@@ -12,139 +13,61 @@ import "../Controls2/TextTypes"
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property real contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
property bool isFocusable: true
|
||||
property real contentHeight: ad.implicitHeight + ad.anchors.topMargin + ad.anchors.bottomMargin
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop { position: 0.0; color: AmneziaStyle.color.translucentSlateGray }
|
||||
GradientStop { position: 1.0; color: AmneziaStyle.color.translucentOnyxBlack }
|
||||
}
|
||||
border.width: 1
|
||||
border.color: AmneziaStyle.color.onyxBlack
|
||||
border.color: AmneziaStyle.color.goldenApricot
|
||||
color: AmneziaStyle.color.transparent
|
||||
radius: 13
|
||||
|
||||
visible: ServersModel.isAdVisible
|
||||
visible: false
|
||||
// visible: GC.isDesktop() && ServersModel.isDefaultServerFromApi
|
||||
// && ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && SettingsController.isHomeAdLabelVisible
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
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"))
|
||||
onClicked: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium"))
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
id: ad
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 12
|
||||
anchors.topMargin: 12
|
||||
anchors.bottomMargin: 12
|
||||
spacing: 20
|
||||
anchors.margins: 16
|
||||
|
||||
ColumnLayout {
|
||||
Image {
|
||||
source: "qrc:/images/controls/amnezia.svg"
|
||||
sourceSize: Qt.size(36, 36)
|
||||
|
||||
layer {
|
||||
effect: ColorOverlay {
|
||||
color: AmneziaStyle.color.paleGray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
Layout.rightMargin: 10
|
||||
Layout.leftMargin: 10
|
||||
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
text: ServersModel.adHeader
|
||||
color: AmneziaStyle.color.paleGray
|
||||
font.pixelSize: 14
|
||||
font.weight: 700
|
||||
text: qsTr("Amnezia Premium - for access to all websites and online resources")
|
||||
color: AmneziaStyle.color.pearlGray
|
||||
|
||||
textFormat: Text.RichText
|
||||
lineHeight: 18
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
image: "qrc:/images/controls/close.svg"
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
onClicked: function() {
|
||||
SettingsController.disableHomeAdLabel()
|
||||
}
|
||||
|
||||
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 {
|
||||
anchors.centerIn: parent
|
||||
source: "qrc:/images/controls/chevron-right.svg"
|
||||
sourceSize: Qt.size(24, 24)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
|
||||
onEntered: {
|
||||
chevronBackground.color = AmneziaStyle.color.slateGray
|
||||
}
|
||||
|
||||
onExited: {
|
||||
chevronBackground.color = AmneziaStyle.color.transparent
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
chevronBackground.color = pressed ? AmneziaStyle.color.charcoalGray : containsMouse ? AmneziaStyle.color.slateGray : AmneziaStyle.color.transparent
|
||||
}
|
||||
|
||||
onClicked: function() {
|
||||
root.forceActiveFocus()
|
||||
Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
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 {
|
||||
target: PageController
|
||||
|
||||
|
||||
@@ -74,18 +74,6 @@ Item {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Qt.application
|
||||
|
||||
function onStateChanged() {
|
||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
||||
if (!menu.isClosed) {
|
||||
menu.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: rootButtonContent.implicitWidth
|
||||
implicitHeight: rootButtonContent.implicitHeight
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ Item {
|
||||
|
||||
property string buttonText
|
||||
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 alias textField: textField
|
||||
@@ -51,7 +48,7 @@ Item {
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
@@ -70,7 +67,7 @@ Item {
|
||||
border.width: 1
|
||||
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 100 }
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -197,14 +194,6 @@ Item {
|
||||
focusPolicy: Qt.NoFocus
|
||||
text: root.buttonText
|
||||
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.bottom: content.bottom
|
||||
@@ -212,7 +201,7 @@ Item {
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
squareLeftSide: false
|
||||
squareLeftSide: true
|
||||
|
||||
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 pearlGray: '#EAEAEC'
|
||||
readonly property color translucentRichBrown: Qt.rgba(99/255, 51/255, 3/255, 0.26)
|
||||
readonly property color translucentSlateGray: Qt.rgba(85/255, 86/255, 92/255, 0.13)
|
||||
readonly property color translucentOnyxBlack: Qt.rgba(28/255, 29/255, 33/255, 0.13)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ PageType {
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ PageType {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
|
||||
@@ -20,21 +20,6 @@ import "../Components"
|
||||
PageType {
|
||||
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 {
|
||||
objectName: "pageControllerConnections"
|
||||
|
||||
@@ -86,6 +71,16 @@ PageType {
|
||||
anchors.topMargin: 12 + SettingsController.safeAreaTopMargin
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
AdLabel {
|
||||
id: adLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: adLabel.contentHeight
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 22
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: loggingButton
|
||||
objectName: "loggingButton"
|
||||
@@ -194,16 +189,6 @@ PageType {
|
||||
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.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
@@ -31,11 +31,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
SmartScroll {
|
||||
id: smartScroll
|
||||
listView: listView
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
@@ -85,13 +80,6 @@ PageType {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(mtuTextField)
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
@@ -109,12 +97,6 @@ PageType {
|
||||
clientJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketCountTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -131,12 +113,6 @@ PageType {
|
||||
clientJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketMinSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -153,12 +129,6 @@ PageType {
|
||||
clientJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -177,12 +147,6 @@ PageType {
|
||||
clientSpecialJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(specialJunk1TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -201,12 +165,6 @@ PageType {
|
||||
clientSpecialJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(specialJunk2TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -225,12 +183,6 @@ PageType {
|
||||
clientSpecialJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(specialJunk3TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -249,12 +201,6 @@ PageType {
|
||||
clientSpecialJunk4 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(specialJunk4TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -273,12 +219,6 @@ PageType {
|
||||
clientSpecialJunk5 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(specialJunk5TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -297,12 +237,6 @@ PageType {
|
||||
clientControlledJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(controlledJunk1TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -321,12 +255,6 @@ PageType {
|
||||
clientControlledJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(controlledJunk2TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -345,12 +273,6 @@ PageType {
|
||||
clientControlledJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(controlledJunk3TextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -368,12 +290,6 @@ PageType {
|
||||
clientSpecialHandshakeTimeout = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(iTimeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
|
||||
@@ -34,11 +34,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
SmartScroll {
|
||||
id: smartScroll
|
||||
listView: listView
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
@@ -86,12 +81,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(vpnAddressSubnetTextField)
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
@@ -115,12 +104,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(portTextField)
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
@@ -138,12 +121,6 @@ PageType {
|
||||
serverJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketCountTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -160,12 +137,6 @@ PageType {
|
||||
serverJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketMinSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -182,12 +153,6 @@ PageType {
|
||||
serverJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -204,12 +169,6 @@ PageType {
|
||||
serverInitPacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(initPacketJunkSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -226,12 +185,6 @@ PageType {
|
||||
serverResponsePacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(responsePacketJunkSizeTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
@@ -280,12 +233,6 @@ PageType {
|
||||
serverInitPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(initPacketMagicHeaderTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -302,12 +249,6 @@ PageType {
|
||||
serverResponsePacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(responsePacketMagicHeaderTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -324,12 +265,6 @@ PageType {
|
||||
serverUnderloadPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(underloadPacketMagicHeaderTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
@@ -346,12 +281,6 @@ PageType {
|
||||
serverTransportPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus) {
|
||||
smartScroll.scrollToItem(transportPacketMagicHeaderTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
|
||||
@@ -14,19 +14,6 @@ import "../Config"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
Connections {
|
||||
target: ApiNewsController
|
||||
function onFetchNewsFinished() {
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
||||
function onErrorOccurred(errorCode) {
|
||||
PageController.showErrorMessage(errorCode)
|
||||
PageController.closePage()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
@@ -153,8 +140,9 @@ PageType {
|
||||
return;
|
||||
}
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiNewsController.fetchNews()
|
||||
ApiNewsController.fetchNews();
|
||||
PageController.goToPage(PageEnum.PageSettingsNewsNotifications)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ PageType {
|
||||
id: backButton
|
||||
objectName: "backButton"
|
||||
|
||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
HeaderTypeWithButton {
|
||||
|
||||
@@ -1,243 +1,243 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property string configExtension: ".conf"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
model: ApiCountryModel
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Configuration Files")
|
||||
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
|
||||
text: countryName
|
||||
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||
hideDescription: isWorkerExpired ? false : true
|
||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isIssued) {
|
||||
moreOptionsDrawer.countryName = countryName
|
||||
moreOptionsDrawer.countryCode = countryCode
|
||||
moreOptionsDrawer.openTriggered()
|
||||
} else {
|
||||
issueConfig(countryCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: moreOptionsDrawer
|
||||
|
||||
property string countryName
|
||||
property string countryCode
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: moreOptionsDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: moreOptionsDrawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: drawerListView
|
||||
|
||||
anchors.top: moreOptionsDrawerBackButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
header: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Generate a new configuration file")
|
||||
descriptionText: qsTr("The previously created one will stop working")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Revoke the current configuration file")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function issueConfig(countryCode) {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = countryCode + configExtension
|
||||
} else {
|
||||
fileName = SystemController.getFileName(configCaption,
|
||||
qsTr("Config files (*" + configExtension + ")"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
|
||||
true,
|
||||
configExtension)
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("Config file saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function revokeConfig(countryCode) {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.revokeNativeConfig(countryCode)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("The config has been revoked"))
|
||||
}
|
||||
}
|
||||
|
||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||
var headerText
|
||||
if (isConfigIssue) {
|
||||
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||
} else {
|
||||
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||
}
|
||||
|
||||
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (isConfigIssue) {
|
||||
issueConfig(countryCode)
|
||||
} else {
|
||||
revokeConfig(countryCode)
|
||||
}
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property string configExtension: ".conf"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
model: ApiCountryModel
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Configuration Files")
|
||||
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
|
||||
text: countryName
|
||||
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||
hideDescription: isWorkerExpired ? true : false
|
||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isIssued) {
|
||||
moreOptionsDrawer.countryName = countryName
|
||||
moreOptionsDrawer.countryCode = countryCode
|
||||
moreOptionsDrawer.openTriggered()
|
||||
} else {
|
||||
issueConfig(countryCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: moreOptionsDrawer
|
||||
|
||||
property string countryName
|
||||
property string countryCode
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: moreOptionsDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: moreOptionsDrawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: drawerListView
|
||||
|
||||
anchors.top: moreOptionsDrawerBackButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
header: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Generate a new configuration file")
|
||||
descriptionText: qsTr("The previously created one will stop working")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Revoke the current configuration file")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function issueConfig(countryCode) {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = countryCode + configExtension
|
||||
} else {
|
||||
fileName = SystemController.getFileName(configCaption,
|
||||
qsTr("Config files (*" + configExtension + ")"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
|
||||
true,
|
||||
configExtension)
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("Config file saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function revokeConfig(countryCode) {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.revokeNativeConfig(countryCode)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("The config has been revoked"))
|
||||
}
|
||||
}
|
||||
|
||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||
var headerText
|
||||
if (isConfigIssue) {
|
||||
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||
} else {
|
||||
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||
}
|
||||
|
||||
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (isConfigIssue) {
|
||||
issueConfig(countryCode)
|
||||
} else {
|
||||
revokeConfig(countryCode)
|
||||
}
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ PageType {
|
||||
width: root.width
|
||||
|
||||
BackButtonType {
|
||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
Label {
|
||||
|
||||
@@ -165,11 +165,9 @@ PageType {
|
||||
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
|
||||
anchors.bottom: addAppButton.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
clip: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyAppSplitTunnelingModel
|
||||
@@ -217,54 +215,50 @@ PageType {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
height: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin
|
||||
|
||||
anchors.fill: addAppButton
|
||||
anchors.bottomMargin: -24 - SettingsController.safeAreaBottomMargin
|
||||
color: AmneziaStyle.color.midnightBlack
|
||||
opacity: 0.8
|
||||
|
||||
RowLayout {
|
||||
id: addAppButton
|
||||
}
|
||||
|
||||
enabled: root.pageEnabled
|
||||
RowLayout {
|
||||
id: addAppButton
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
|
||||
enabled: root.pageEnabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
|
||||
|
||||
Layout.fillWidth: true
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
|
||||
textField.placeholderText: qsTr("application name")
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
Layout.fillWidth: true
|
||||
|
||||
rightButtonClickedOnEnter: true
|
||||
textField.placeholderText: qsTr("application name")
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
searchField.focus = false
|
||||
PageController.showBusyIndicator(true)
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
if (Qt.platform.os === "windows") {
|
||||
var fileName = SystemController.getFileName(qsTr("Open executable file"),
|
||||
qsTr("Executable files (*.*)"))
|
||||
if (fileName !== "") {
|
||||
AppSplitTunnelingController.addApp(fileName)
|
||||
}
|
||||
} else if (Qt.platform.os === "android"){
|
||||
installedAppDrawer.openTriggered()
|
||||
clickedFunc: function() {
|
||||
searchField.focus = false
|
||||
PageController.showBusyIndicator(true)
|
||||
|
||||
if (Qt.platform.os === "windows") {
|
||||
var fileName = SystemController.getFileName(qsTr("Open executable file"),
|
||||
qsTr("Executable files (*.*)"))
|
||||
if (fileName !== "") {
|
||||
AppSplitTunnelingController.addApp(fileName)
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
} else if (Qt.platform.os === "android"){
|
||||
installedAppDrawer.openTriggered()
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,163 +1,163 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
|
||||
|
||||
enabled: !isServerFromApi
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isServerFromApi) {
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
|
||||
}
|
||||
}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("DNS servers")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("If AmneziaDNS is not used or installed")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: primaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Primary DNS")
|
||||
|
||||
textField.text: SettingsController.primaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: secondaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Secondary DNS")
|
||||
|
||||
textField.text: SettingsController.secondaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: restoreDefaultButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Restore default")
|
||||
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Restore default DNS settings?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
SettingsController.primaryDns = "1.1.1.1"
|
||||
primaryDns.textField.text = SettingsController.primaryDns
|
||||
SettingsController.secondaryDns = "1.0.0.1"
|
||||
secondaryDns.textField.text = SettingsController.secondaryDns
|
||||
PageController.showNotificationMessage(qsTr("Settings have been reset"))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (primaryDns.textField.text !== SettingsController.primaryDns) {
|
||||
SettingsController.primaryDns = primaryDns.textField.text
|
||||
}
|
||||
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
|
||||
SettingsController.secondaryDns = secondaryDns.textField.text
|
||||
}
|
||||
PageController.showNotificationMessage(qsTr("Settings saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
|
||||
|
||||
enabled: !isServerFromApi
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isServerFromApi) {
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
|
||||
}
|
||||
}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("DNS servers")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("If AmneziaDNS is not used or installed")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: primaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Primary DNS")
|
||||
|
||||
textField.text: SettingsController.primaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: secondaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Secondary DNS")
|
||||
|
||||
textField.text: SettingsController.secondaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: restoreDefaultButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Restore default")
|
||||
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Restore default DNS settings?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
SettingsController.primaryDns = "1.1.1.1"
|
||||
primaryDns.textField.text = SettingsController.primaryDns
|
||||
SettingsController.secondaryDns = "1.0.0.1"
|
||||
secondaryDns.textField.text = SettingsController.secondaryDns
|
||||
PageController.showNotificationMessage(qsTr("Settings have been reset"))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (primaryDns.textField.text !== SettingsController.primaryDns) {
|
||||
SettingsController.primaryDns = primaryDns.textField.text
|
||||
}
|
||||
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
|
||||
SettingsController.secondaryDns = secondaryDns.textField.text
|
||||
}
|
||||
PageController.showNotificationMessage(qsTr("Settings saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,18 +81,18 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
ColumnLayout {
|
||||
id: header
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
HeaderTypeWithSwitcher {
|
||||
Layout.fillWidth: true
|
||||
@@ -168,13 +168,11 @@ PageType {
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: addSiteButton.implicitHeight + 48 + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
|
||||
anchors.bottom: addSiteButton.top
|
||||
|
||||
width: parent.width
|
||||
|
||||
enabled: root.pageEnabled
|
||||
clip: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxySitesModel
|
||||
@@ -233,60 +231,56 @@ PageType {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
height: addSiteButton.implicitHeight + 48
|
||||
|
||||
anchors.fill: addSiteButton
|
||||
anchors.bottomMargin: -24
|
||||
color: AmneziaStyle.color.midnightBlack
|
||||
opacity: 0.8
|
||||
|
||||
RowLayout {
|
||||
id: addSiteButton
|
||||
}
|
||||
|
||||
enabled: root.pageEnabled
|
||||
RowLayout {
|
||||
id: addSiteButton
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 24
|
||||
enabled: root.pageEnabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 24
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightButtonClickedOnEnter: true
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
|
||||
textField.placeholderText: qsTr("website or IP")
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
Layout.fillWidth: true
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
SitesController.addSite(textField.text)
|
||||
textField.text = ""
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
textField.placeholderText: qsTr("website or IP")
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
SitesController.addSite(textField.text)
|
||||
textField.text = ""
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: addSiteButtonImage
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
|
||||
image: "qrc:/images/controls/more-vertical.svg"
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
onClicked: function () {
|
||||
moreActionsDrawer.openTriggered()
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: addSiteButtonImage
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
|
||||
image: "qrc:/images/controls/more-vertical.svg"
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
onClicked: function () {
|
||||
moreActionsDrawer.openTriggered()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
||||
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||
}
|
||||
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
||||
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,179 +1,179 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: imagePath
|
||||
leftText: lText
|
||||
rightText: rText
|
||||
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
if (endpoint !== undefined && endpoint !== "") {
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.importServiceFromGateway()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
region,
|
||||
price,
|
||||
timeLimit,
|
||||
speed,
|
||||
features
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: region
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
|
||||
readonly property string lText: qsTr("For the region")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: price
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/tag.svg"
|
||||
readonly property string lText: qsTr("Price")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: timeLimit
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/history.svg"
|
||||
readonly property string lText: qsTr("Work period")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
property bool isVisible: rText !== ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: speed
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
|
||||
readonly property string lText: qsTr("Speed")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: features
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/info.svg"
|
||||
readonly property string lText: qsTr("Features")
|
||||
readonly property string rText: ""
|
||||
property bool isVisible: true
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: imagePath
|
||||
leftText: lText
|
||||
rightText: rText
|
||||
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
if (endpoint !== undefined && endpoint !== "") {
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.importServiceFromGateway()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
region,
|
||||
price,
|
||||
timeLimit,
|
||||
speed,
|
||||
features
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: region
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
|
||||
readonly property string lText: qsTr("For the region")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: price
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/tag.svg"
|
||||
readonly property string lText: qsTr("Price")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: timeLimit
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/history.svg"
|
||||
readonly property string lText: qsTr("Work period")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
property bool isVisible: rText !== ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: speed
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
|
||||
readonly property string lText: qsTr("Speed")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: features
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/info.svg"
|
||||
readonly property string lText: qsTr("Features")
|
||||
readonly property string rText: ""
|
||||
property bool isVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ PageType {
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -147,7 +147,6 @@ PageType {
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
|
||||
@@ -25,7 +25,7 @@ PageType {
|
||||
source: "qrc:/images/amneziaBigLogo.png"
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: 32 + SettingsController.safeAreaTopMargin
|
||||
Layout.topMargin: 32
|
||||
Layout.preferredWidth: 360
|
||||
Layout.preferredHeight: 287
|
||||
}
|
||||
@@ -33,7 +33,7 @@ PageType {
|
||||
BasicButtonType {
|
||||
id: startButton
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 48 + SettingsController.safeAreaBottomMargin
|
||||
Layout.bottomMargin: 48
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
@@ -1,168 +1,168 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
import "../Config"
|
||||
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
property string headerText: ""
|
||||
property string configContentHeaderText: ""
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 24
|
||||
|
||||
headerText: qsTr("Full access to the server and VPN")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") +
|
||||
qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: serverSelector
|
||||
objectName: "serverSelector"
|
||||
|
||||
signal severSelectorIndexChanged
|
||||
property int currentIndex: 0
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
|
||||
drawerHeight: 0.4375
|
||||
drawerParent: root
|
||||
|
||||
descriptionText: qsTr("Server")
|
||||
headerText: qsTr("Server")
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: serverSelectorListView
|
||||
|
||||
rootWidth: root.width
|
||||
imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyServersModel
|
||||
sourceModel: ServersModel
|
||||
filters: [
|
||||
ValueFilter {
|
||||
roleName: "hasWriteAccess"
|
||||
value: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
handler()
|
||||
|
||||
if (serverSelector.currentIndex !== serverSelectorListView.selectedIndex) {
|
||||
serverSelector.currentIndex = serverSelectorListView.selectedIndex
|
||||
serverSelector.severSelectorIndexChanged()
|
||||
}
|
||||
|
||||
listView.headerText = qsTr("Accessing ") + serverSelector.text
|
||||
listView.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
|
||||
serverSelector.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
|
||||
proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0
|
||||
serverSelectorListView.triggerCurrentItem()
|
||||
}
|
||||
|
||||
function handler() {
|
||||
serverSelector.text = selectedText
|
||||
ServersModel.processedIndex = proxyServersModel.mapToSource(selectedIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: shareButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Share")
|
||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
|
||||
if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) {
|
||||
PageController.showBusyIndicator(false)
|
||||
ExportController.exportErrorOccurred(qsTr("Access error!"))
|
||||
return
|
||||
} else {
|
||||
ExportController.generateFullAccessConfig()
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
PageController.goToShareConnectionPage(listView.headerText, listView.configContentHeaderText, "", ".vpn", "amnezia_config")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
import "../Config"
|
||||
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
property string headerText: ""
|
||||
property string configContentHeaderText: ""
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 24
|
||||
|
||||
headerText: qsTr("Full access to the server and VPN")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") +
|
||||
qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: serverSelector
|
||||
objectName: "serverSelector"
|
||||
|
||||
signal severSelectorIndexChanged
|
||||
property int currentIndex: 0
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
|
||||
drawerHeight: 0.4375
|
||||
drawerParent: root
|
||||
|
||||
descriptionText: qsTr("Server")
|
||||
headerText: qsTr("Server")
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: serverSelectorListView
|
||||
|
||||
rootWidth: root.width
|
||||
imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyServersModel
|
||||
sourceModel: ServersModel
|
||||
filters: [
|
||||
ValueFilter {
|
||||
roleName: "hasWriteAccess"
|
||||
value: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
handler()
|
||||
|
||||
if (serverSelector.currentIndex !== serverSelectorListView.selectedIndex) {
|
||||
serverSelector.currentIndex = serverSelectorListView.selectedIndex
|
||||
serverSelector.severSelectorIndexChanged()
|
||||
}
|
||||
|
||||
listView.headerText = qsTr("Accessing ") + serverSelector.text
|
||||
listView.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
|
||||
serverSelector.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
|
||||
proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0
|
||||
serverSelectorListView.triggerCurrentItem()
|
||||
}
|
||||
|
||||
function handler() {
|
||||
serverSelector.text = selectedText
|
||||
ServersModel.processedIndex = proxyServersModel.mapToSource(selectedIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: shareButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Share")
|
||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
|
||||
if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) {
|
||||
PageController.showBusyIndicator(false)
|
||||
ExportController.exportErrorOccurred(qsTr("Access error!"))
|
||||
return
|
||||
} else {
|
||||
ExportController.generateFullAccessConfig()
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
PageController.goToShareConnectionPage(listView.headerText, listView.configContentHeaderText, "", ".vpn", "amnezia_config")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,9 +304,6 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
// Also adjust TabBar position when keyboard appears (Android 14+ workaround)
|
||||
anchors.bottomMargin: SettingsController.imeHeight
|
||||
|
||||
topPadding: 8
|
||||
bottomPadding: 8 + SettingsController.safeAreaBottomMargin
|
||||
|
||||
@@ -183,7 +183,7 @@ Window {
|
||||
id: privateKeyPassphraseDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin + SettingsController.imeHeight
|
||||
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
|
||||
Reference in New Issue
Block a user