merge dev & fix conf

This commit is contained in:
dranik
2026-05-18 19:10:00 +03:00
212 changed files with 14919 additions and 3421 deletions
@@ -5,9 +5,12 @@
#include <QDateTime>
#include <QDebug>
#include <QIODevice>
#include <QJsonArray>
#include <QJsonObject>
#include <QMetaObject>
#include <QPointer>
#include <QRegularExpression>
#include <QSet>
#include <QTimer>
#include <QUuid>
#include <string>
@@ -22,7 +25,6 @@
#endif
#include "core/controllers/gatewayController.h"
#include "core/models/serverConfig.h"
#include "core/models/api/apiV2ServerConfig.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/constants/apiKeys.h"
@@ -38,6 +40,48 @@ constexpr auto kGatewayProbePath = "%1v1/news";
constexpr int kPairingRetryMaxAttempts = 3;
constexpr int kGatewayProbeTimeoutMsecs = 3000;
QJsonObject apiGatewayServicesFromServers(const ServersController *serversController)
{
if (!serversController || serversController->getServersCount() == 0) {
return {};
}
QSet<QString> userCountryCodes;
QSet<QString> serviceTypes;
for (int i = 0; i < serversController->getServersCount(); ++i) {
const QString serverId = serversController->getServerId(i);
const auto apiV2 = serversController->apiV2Config(serverId);
if (!apiV2.has_value()) {
continue;
}
if (!apiV2->apiConfig.userCountryCode.isEmpty()) {
userCountryCodes.insert(apiV2->apiConfig.userCountryCode);
}
const QString serviceType = apiV2->serviceType();
if (!serviceType.isEmpty()) {
serviceTypes.insert(serviceType);
}
}
if (userCountryCodes.isEmpty() && serviceTypes.isEmpty()) {
return {};
}
QJsonObject json;
QJsonArray userCountryCodesArray;
for (const QString &code : userCountryCodes) {
userCountryCodesArray.append(code);
}
json.insert(apiDefs::key::userCountryCode, userCountryCodesArray);
QJsonArray serviceTypesArray;
for (const QString &type : serviceTypes) {
serviceTypesArray.append(type);
}
json.insert(apiDefs::key::serviceType, serviceTypesArray);
return json;
}
bool isPairingRetriableError(ErrorCode code)
{
switch (code) {
@@ -507,19 +551,19 @@ bool PairingUiController::canOpenTvQrPairingPage()
return false;
}
if (!m_serversController || m_serversController->gatewayStacks().isEmpty()) {
const QJsonObject gatewayServices = apiGatewayServicesFromServers(m_serversController);
if (gatewayServices.isEmpty()) {
return true;
}
QJsonObject payload;
payload.insert(QStringLiteral("locale"), m_appSettingsRepository->getAppLanguage().name().split(QLatin1Char('_')).first());
const QJsonObject stacksJson = m_serversController->gatewayStacks().toJson();
if (stacksJson.contains(apiDefs::key::userCountryCode)) {
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
if (gatewayServices.contains(apiDefs::key::userCountryCode)) {
payload.insert(apiDefs::key::userCountryCode, gatewayServices.value(apiDefs::key::userCountryCode));
}
if (stacksJson.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType));
if (gatewayServices.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, gatewayServices.value(apiDefs::key::serviceType));
}
const bool isTestPurchase = false;
@@ -632,10 +676,8 @@ void PairingUiController::dispatchTvGenerateQrAttempt(quint64 generation, int re
}
if (logicalErr == ErrorCode::NoError) {
ServerConfig importedConfig;
const ErrorCode impErr = m_subscriptionController->importServerFromQrPairingResponse(
out.config, out.serviceInfo, out.supportedProtocols, importedConfig);
Q_UNUSED(importedConfig);
out.config, out.serviceInfo, out.supportedProtocols);
setTvBusy(false);
if (impErr != ErrorCode::NoError) {
setTvPairingUiPhase(2);
@@ -742,39 +784,35 @@ void PairingUiController::submitPhonePairing(const QString &qrUuid, int serverIn
return;
}
const ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
if (!serverConfig.isApiV2()) {
const QString serverId = m_serversController->getServerId(serverIndex);
const auto apiV2Opt = m_serversController->apiV2Config(serverId);
if (!apiV2Opt.has_value()) {
qWarning() << "[PairingUi] submitPhonePairing server is not API v2";
emit errorOccurred(ErrorCode::InternalError);
return;
}
const ApiV2ServerConfig *apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) {
qWarning() << "[PairingUi] submitPhonePairing cast to ApiV2ServerConfig failed";
emit errorOccurred(ErrorCode::InternalError);
return;
}
const ApiV2ServerConfig &apiV2 = *apiV2Opt;
QString vpnKey;
const ErrorCode keyErr = m_subscriptionController->prepareVpnKeyExport(serverIndex, vpnKey);
const ErrorCode keyErr = m_subscriptionController->prepareVpnKeyExport(serverId, vpnKey);
if (keyErr != ErrorCode::NoError) {
qWarning() << "[PairingUi] prepareVpnKeyExport failed" << static_cast<int>(keyErr);
emit errorOccurred(keyErr);
return;
}
const QJsonObject serviceInfo = apiV2->apiConfig.serviceInfo.toJson();
const QJsonArray supportedProtocols = apiV2->apiConfig.supportedProtocols;
const QString apiKey = apiV2->authData.apiKey;
const QJsonObject serviceInfo = apiV2.apiConfig.serviceInfo.toJson();
const QJsonArray supportedProtocols = apiV2.apiConfig.supportedProtocols;
const QString apiKey = apiV2.authData.apiKey;
if (apiKey.isEmpty()) {
qWarning() << "[PairingUi] submitPhonePairing aborted: empty API key on server card";
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return;
}
const QString serviceType = apiV2->apiConfig.serviceType.trimmed();
const QString userCountryCode = apiV2->apiConfig.userCountryCode.trimmed();
const QString serviceType = apiV2.apiConfig.serviceType.trimmed();
const QString userCountryCode = apiV2.apiConfig.userCountryCode.trimmed();
const ErrorCode fieldErr =
PairingController::validatePairingScanFields(trimmedUuid, vpnKey, apiKey, serviceType, userCountryCode);
@@ -795,7 +833,7 @@ void PairingUiController::submitPhonePairing(const QString &qrUuid, int serverIn
emit phoneStatusMessageChanged();
setPhoneBusy(true);
dispatchPhoneScanQrAttempt(trimmedUuid, apiV2->apiConfig.isTestPurchase, vpnKey, serviceInfo, supportedProtocols, apiKey,
dispatchPhoneScanQrAttempt(trimmedUuid, apiV2.apiConfig.isTestPurchase, vpnKey, serviceInfo, supportedProtocols, apiKey,
serviceType, userCountryCode, phoneGeneration, 0);
}
@@ -2,14 +2,13 @@
#include "amneziaApplication.h"
#include "core/configurators/wireguardConfigurator.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/qrCodeUtils.h"
#include "ui/controllers/systemController.h"
#include "version.h"
#include "core/models/serverConfig.h"
#include <QClipboard>
#include <QDebug>
#include <QSet>
@@ -71,7 +70,17 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
QObject *parent)
: QObject(parent), m_serversController(serversController), m_apiServicesModel(apiServicesModel), m_servicesCatalogController(servicesCatalogController), m_subscriptionController(subscriptionController), m_apiSubscriptionPlansModel(apiSubscriptionPlansModel), m_apiBenefitsModel(apiBenefitsModel), m_apiAccountInfoModel(apiAccountInfoModel), m_apiCountryModel(apiCountryModel), m_apiDevicesModel(apiDevicesModel), m_settingsController(settingsController)
: QObject(parent),
m_serversController(serversController),
m_apiServicesModel(apiServicesModel),
m_servicesCatalogController(servicesCatalogController),
m_subscriptionController(subscriptionController),
m_apiSubscriptionPlansModel(apiSubscriptionPlansModel),
m_apiBenefitsModel(apiBenefitsModel),
m_apiAccountInfoModel(apiAccountInfoModel),
m_apiCountryModel(apiCountryModel),
m_apiDevicesModel(apiDevicesModel),
m_settingsController(settingsController)
{
connect(m_apiServicesModel, &ApiServicesModel::serviceSelectionChanged, this, [this]() {
ApiServicesModel::ApiServicesData selectedServiceData = m_apiServicesModel->selectedServiceData();
@@ -80,24 +89,24 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
});
}
bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &fileName)
bool SubscriptionUiController::exportVpnKey(const QString &serverId, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
prepareVpnKeyExport(serverIndex);
prepareVpnKeyExport(serverId);
if (m_vpnKey.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return false;
}
SystemController::saveFile(fileName, m_vpnKey);
return true;
return SystemController::saveFile(fileName, m_vpnKey);
}
bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName)
bool SubscriptionUiController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
@@ -105,19 +114,21 @@ bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString
}
QString nativeConfig;
ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverIndex, serverCountryCode, nativeConfig);
ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverId, serverCountryCode, nativeConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
SystemController::saveFile(fileName, nativeConfig);
return true;
const bool saved = SystemController::saveFile(fileName, nativeConfig);
getAccountInfo(serverId, true);
return saved;
}
bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode)
bool SubscriptionUiController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverIndex, serverCountryCode);
ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverId, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -125,10 +136,11 @@ bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString
return true;
}
void SubscriptionUiController::prepareVpnKeyExport(int serverIndex)
void SubscriptionUiController::prepareVpnKeyExport(const QString &serverId)
{
QString vpnKey;
ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverIndex, vpnKey);
ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverId, vpnKey);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
@@ -144,6 +156,7 @@ void SubscriptionUiController::prepareVpnKeyExport(int serverIndex)
emit vpnKeyExportReady();
}
void SubscriptionUiController::copyVpnKeyToClipboard()
{
auto clipboard = amnApp->getClipboard();
@@ -174,14 +187,12 @@ bool SubscriptionUiController::importPremiumFromAppStore(const QString &storePro
productId = QStringLiteral("amnezia_premium_6_month");
}
ServerConfig serverConfig;
int duplicateServerIndex = -1;
ErrorCode errorCode = m_subscriptionController->processAppStorePurchase(
m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
productId,
serverConfig,
&duplicateServerIndex);
if (errorCode != ErrorCode::NoError) {
@@ -264,11 +275,8 @@ bool SubscriptionUiController::importFreeFromGateway()
}
SubscriptionController::ProtocolData protocolData = m_subscriptionController->generateProtocolData(serviceProtocol);
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(userCountryCode, serviceType,
serviceProtocol, protocolData,
serverConfig);
serviceProtocol, protocolData);
if (errorCode == ErrorCode::NoError) {
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
@@ -282,12 +290,10 @@ bool SubscriptionUiController::importFreeFromGateway()
bool SubscriptionUiController::importTrialFromGateway(const QString &email)
{
emit trialEmailError(QString());
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importTrialFromGateway(m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
email,
serverConfig);
email);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiTrialAlreadyUsedError) {
emit trialEmailError(
@@ -302,21 +308,17 @@ bool SubscriptionUiController::importTrialFromGateway(const QString &email)
return true;
}
bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool SubscriptionUiController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
bool isConnectEvent = newCountryCode.isEmpty() && newCountryName.isEmpty() && !reloadServiceConfig;
bool wasSubscriptionExpired = false;
ServerConfig oldServerConfig = m_serversController->getServerConfig(serverIndex);
if (oldServerConfig.isApiV2()) {
const ApiV2ServerConfig *oldApiV2 = oldServerConfig.as<ApiV2ServerConfig>();
if (oldApiV2) {
wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer
|| oldApiV2->apiConfig.isSubscriptionExpired();
}
if (const auto oldApiV2 = m_serversController->apiV2Config(serverId)) {
wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer
|| oldApiV2->apiConfig.isSubscriptionExpired();
}
ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverIndex, newCountryCode, isConnectEvent);
ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverId, newCountryCode, isConnectEvent);
if (errorCode == ErrorCode::NoError) {
if (wasSubscriptionExpired) {
@@ -340,27 +342,10 @@ bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, c
}
}
bool SubscriptionUiController::updateServiceFromTelegram(const int serverIndex)
bool SubscriptionUiController::deactivateDevice(const QString &serverId)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
ErrorCode errorCode = m_subscriptionController->updateServiceFromTelegram(serverIndex);
if (errorCode == ErrorCode::NoError) {
emit updateServerFromApiFinished();
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool SubscriptionUiController::deactivateDevice(int serverIndex)
{
ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverIndex);
ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverId);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -369,9 +354,10 @@ bool SubscriptionUiController::deactivateDevice(int serverIndex)
return true;
}
bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode)
bool SubscriptionUiController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverIndex, uuid, serverCountryCode);
ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverId, uuid, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -380,12 +366,19 @@ bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const Q
return true;
}
void SubscriptionUiController::validateConfig()
{
int serverIndex = m_serversController->getDefaultServerIndex();
bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverIndex);
const QString serverId = m_serversController->getDefaultServerId();
if (!serverId.isEmpty() && m_serversController->isLegacyApiV1Server(serverId)) {
emit unsupportedConnectDrawerRequested();
emit configValidated(false);
return;
}
ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverIndex, hasInstalledContainers);
bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverId);
ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverId, hasInstalledContainers);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
@@ -399,22 +392,34 @@ void SubscriptionUiController::validateConfig()
emit configValidated(true);
}
void SubscriptionUiController::setCurrentProtocol(int serverIndex, const QString &protocolName)
void SubscriptionUiController::setCurrentProtocol(const QString &serverId, const QString &protocolName)
{
m_subscriptionController->setCurrentProtocol(serverIndex, protocolName);
m_subscriptionController->setCurrentProtocol(serverId, protocolName);
}
bool SubscriptionUiController::isVlessProtocol(int serverIndex)
bool SubscriptionUiController::isVlessProtocol(const QString &serverId)
{
return m_subscriptionController->isVlessProtocol(serverIndex);
return m_subscriptionController->isVlessProtocol(serverId);
}
void SubscriptionUiController::removeApiConfig(int serverIndex)
void SubscriptionUiController::removeApiConfig(const QString &serverId)
{
m_subscriptionController->removeApiConfig(serverIndex);
m_subscriptionController->removeApiConfig(serverId);
emit apiConfigRemoved(tr("Api config removed"));
}
void SubscriptionUiController::removeServer(const QString &serverId)
{
const QString serverName = m_serversController->notificationDisplayName(serverId);
if (!m_subscriptionController->removeServer(serverId)) {
return;
}
emit apiServerRemoved(tr("Server '%1' was removed").arg(serverName));
}
QList<QString> SubscriptionUiController::getQrCodes()
{
return m_qrCodes;
@@ -430,7 +435,7 @@ QString SubscriptionUiController::getVpnKey()
return m_vpnKey;
}
bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload)
bool SubscriptionUiController::getAccountInfo(const QString &serverId, bool reload)
{
if (reload) {
QEventLoop wait;
@@ -442,15 +447,18 @@ bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload)
#endif
}
QJsonObject accountInfo;
ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverIndex, accountInfo);
ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverId, accountInfo);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QJsonObject serverConfigJson = serverConfig.toJson();
m_apiAccountInfoModel->updateModel(accountInfo, serverConfigJson);
const auto apiV2 = m_serversController->apiV2Config(serverId);
if (!apiV2.has_value()) {
emit errorOccurred(ErrorCode::InternalError);
return false;
}
m_apiAccountInfoModel->updateModel(accountInfo, apiV2->toJson());
if (reload) {
updateApiCountryModel();
@@ -471,9 +479,9 @@ void SubscriptionUiController::updateApiDevicesModel()
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo(), m_settingsController->getInstallationUuid(false));
}
void SubscriptionUiController::getRenewalLink(int serverIndex)
void SubscriptionUiController::getRenewalLink(const QString &serverId)
{
if (serverIndex < 0) {
if (serverId.isEmpty()) {
emit errorOccurred(ErrorCode::InternalError);
return;
}
@@ -491,6 +499,6 @@ void SubscriptionUiController::getRenewalLink(int serverIndex)
}
emit renewalLinkReceived(url);
});
watcher->setFuture(m_subscriptionController->getRenewalLink(serverIndex));
watcher->setFuture(m_subscriptionController->getRenewalLink(serverId));
}
@@ -13,6 +13,7 @@
#include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
class SubscriptionUiController : public QObject
{
Q_OBJECT
@@ -34,10 +35,10 @@ public:
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
public slots:
bool exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
bool exportVpnKey(int serverIndex, const QString &fileName);
void prepareVpnKeyExport(int serverIndex);
bool exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(const QString &serverId, const QString &serverCountryCode);
bool exportVpnKey(const QString &serverId, const QString &fileName);
void prepareVpnKeyExport(const QString &serverId);
void copyVpnKeyToClipboard();
bool fillAvailableServices();
@@ -45,21 +46,23 @@ public slots:
bool importFreeFromGateway();
bool restoreServiceFromAppStore();
bool importTrialFromGateway(const QString &email);
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(int serverIndex);
bool deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
bool deactivateDevice(const QString &serverId);
bool deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode);
void validateConfig();
void setCurrentProtocol(int serverIndex, const QString &protocolName);
bool isVlessProtocol(int serverIndex);
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(const QString &serverId);
void removeApiConfig(int serverIndex);
void removeApiConfig(const QString &serverId);
void removeServer(const QString &serverId);
bool getAccountInfo(const QString &serverId, bool reload);
void getRenewalLink(const QString &serverId);
bool getAccountInfo(int serverIndex, bool reload);
void getRenewalLink(int serverIndex);
void updateApiCountryModel();
void updateApiDevicesModel();
@@ -77,9 +80,12 @@ signals:
void subscriptionRefreshNeeded();
void apiConfigRemoved(const QString &message);
void apiServerRemoved(const QString &message);
void vpnKeyExportReady();
void unsupportedConnectDrawerRequested();
private:
QList<QString> getQrCodes();
int getQrCodesCount();