From 8d9594ba360b4cf68cdb8d168c370cea09a022fb Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Mar 2023 16:56:17 +0100 Subject: [PATCH 01/18] add auto-focus to CameraActivity --- .../src/org/amnezia/vpn/qt/CameraActivity.kt | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt index 8416d97c9..8733efa5f 100644 --- a/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt @@ -2,11 +2,11 @@ package org.amnezia.vpn.qt import android.Manifest import android.annotation.SuppressLint -import android.app.Activity -import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle import android.util.Log +import android.view.MotionEvent +import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.* @@ -17,9 +17,9 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage +import org.amnezia.vpn.R import java.util.concurrent.ExecutorService import java.util.concurrent.Executors -import org.amnezia.vpn.R class CameraActivity : AppCompatActivity() { @@ -78,7 +78,7 @@ class CameraActivity : AppCompatActivity() { } } - @SuppressLint("UnsafeOptInUsageError") + @SuppressLint("UnsafeOptInUsageError", "ClickableViewAccessibility") private fun configureVideoPreview() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) val imageCapture = ImageCapture.Builder().build() @@ -101,7 +101,20 @@ class CameraActivity : AppCompatActivity() { try { preview.setSurfaceProvider(viewFinder.surfaceProvider) cameraProvider.unbindAll() - cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase) + val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase) + viewFinder.setOnTouchListener(View.OnTouchListener { view: View, motionEvent: MotionEvent -> + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> return@OnTouchListener true + MotionEvent.ACTION_UP -> { + val factory = viewFinder.meteringPointFactory + val point = factory.createPoint(motionEvent.x, motionEvent.y) + val action = FocusMeteringAction.Builder(point).build() + camera.cameraControl.startFocusAndMetering(action) + return@OnTouchListener true + } + else -> return@OnTouchListener false + } + }) } catch(exc: Exception) { Log.e("WUTT", "Use case binding failed", exc) } @@ -146,7 +159,6 @@ class CameraActivity : AppCompatActivity() { } } } - imageProxy.close() } .addOnFailureListener { From 85025695dff2503b4b128205e077bc5802dddd74 Mon Sep 17 00:00:00 2001 From: cyberta Date: Sun, 26 Mar 2023 13:47:25 +0200 Subject: [PATCH 02/18] add a section about how to get started for Android developers --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 6797cd9af..c183de362 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,31 @@ Build might fail with "source files not found" error the first time you try it, dependencies in parallel, and some dependencies end up being built after the ones that require them. In this case simply restart the build. +## How to build the Android app +_tested on Mac OS_ + +The Android app has the following requirements: +* JDK 11 +* Android platform SDK 33 +* cmake 3.25.0 + +After you have installed QT, QT Creator and Android Studio installed, you need to configure QT Creator correctly. Click in the top menu bar on `QT Creator` -> `Preferences` -> `Devices` and select the tab `Android`. + * set path to jdk 11 + * set path to Android SDK ($ANDROID_HOME) + +In case you get errors regarding missing SDK or 'sdkmanager not running', you cannot fix them by correcting the paths and you have some spare GBs on your disk, you can let QT Creator install all requirements by choosing an empty folder for `Android SDK location` and click on `Set Up SDK`. Be aware: This will install a second Android SDK and NDK on your machine! + +Double check that the right cmake version is configured: Click on `QT Creator` -> `Preferences` and click on the side menu on `Kits`. Under the center content view's `Kits` tab you'll find an entry `CMake Tool`. If the default selected CMake version is lower than 3.25.0, install on your system CMake >= 3.25.0 and choose `System CMake at ` from the drop down list. If this entry is missing, you either have not installed CMake yet or QT Creator hasn't found the path to it. In that case click in the preferences window on the side menu item `CMake`, then on the tab `Tools`in the center content view and finally on the Button `Add` to set the path to your installed CMake. + +Please make sure that you have selected Android Platform SDK 33 for your project: click in the main view's side menu on on `Projects`, on the left you'll see a section `Build & Run` showing different Android build targets. You can select any of them, Amnezia VPN's project setup is designed in a way that always all Android targets will be build. Click on the targets submenu item `Build` and scroll in the center content view to `Build Steps`. Click on `Details` at the end of the headline `Build Android APK` (The `Details` button might be hidden in case QT Creator Window is not running in full screen!). Here we are: choose `android-33` as `Android Build platfrom SDK`. + +That's it you should be ready to compile the project from QT Creator! + +### Development flow +After you've hit the build button, QT-Creator copies the whole project to a folder in the repositories parent directory. The folder should look something like `build-amnezia-client-Android_Qt__Clang_-`. +If you want to develop Amnezia VPNs Android components written in Kotlin, such as components using system APIs, you need to import the generated project in Android Studio with `build-amnezia-client-Android_Qt__Clang_-/client/android-build` as the projects root directory. While you should be able to compile the generated project from Android Studio, you cannot work directly in the repository's Android project. So whenever you are confident with your work in the generated proejct, you'll need to copy and paste the affected files to the corresponding path in the repositories Android project so that you can add and commit your changes! + +You may face compiling issues in QT Creator after you've worked in Android Studio on the generated project. Just do a `./gradlew clean` in the geneated project's root directory (`/client/android-build/.`) and you should be good to continue. ## License GPL v.3 From c319c3f52074734dd30e8cc13bd32e1941b8ee24 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 27 Mar 2023 08:16:35 +0300 Subject: [PATCH 03/18] changed the way to get QSsh::SshConnection, now all resources are cleaned up after use, but the disconnectFromHost function becomes useless --- client/core/servercontroller.cpp | 22 +++++++++++----------- client/core/servercontroller.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index a0df4dc09..132cb18c7 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -38,7 +38,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr const std::function)> &cbReadStdOut, const std::function)> &cbReadStdErr) { - SshConnection *client = connectToHost(sshParams(credentials)); + QSharedPointer client = connectToHost(sshParams(credentials)); if (client->state() == SshConnection::State::Connecting) { qDebug() << "ServerController::runScript aborted, connectToHost in progress"; return ErrorCode::SshTimeoutError; @@ -229,7 +229,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, qDebug().noquote() << "Copy file from container\n" << script; - SshConnection *client = connectToHost(sshParams(credentials)); + QSharedPointer client = connectToHost(sshParams(credentials)); if (client->state() != SshConnection::State::Connected) { if (errorCode) *errorCode = fromSshConnectionErrorCode(client->errorState()); return {}; @@ -288,7 +288,7 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, QSsh::SftpOverwriteMode overwriteMode) { - SshConnection *client = connectToHost(sshParams(credentials)); + QSharedPointer client = connectToHost(sshParams(credentials)); if (client->state() != SshConnection::State::Connected) { return fromSshConnectionErrorCode(client->errorState()); } @@ -780,23 +780,23 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential return stdOut; } -SshConnection *ServerController::connectToHost(const SshConnectionParameters &sshParams) +QSharedPointer ServerController::connectToHost(const SshConnectionParameters &sshParams) { - SshConnection *client = acquireConnection(sshParams); - if (!client) return nullptr; + QSharedPointer client(new SshConnection(sshParams)); + if (!client.get()) return nullptr; QEventLoop waitssh; - QObject::connect(client, &SshConnection::connected, &waitssh, [&]() { + QObject::connect(client.get(), &SshConnection::connected, &waitssh, [&]() { qDebug() << "Server connected by ssh"; waitssh.quit(); }); - QObject::connect(client, &SshConnection::disconnected, &waitssh, [&]() { + QObject::connect(client.get(), &SshConnection::disconnected, &waitssh, [&]() { qDebug() << "Server disconnected by ssh"; waitssh.quit(); }); - QObject::connect(client, &SshConnection::error, &waitssh, [&](QSsh::SshError error) { + QObject::connect(client.get(), &SshConnection::error, &waitssh, [&](QSsh::SshError error) { qCritical() << "Ssh error:" << error << client->errorString(); waitssh.quit(); }); @@ -841,8 +841,8 @@ void ServerController::setCancelInstallation(const bool cancel) void ServerController::disconnectFromHost(const ServerCredentials &credentials) { - SshConnection *client = acquireConnection(sshParams(credentials)); - if (client) client->disconnectFromHost(); +// SshConnection *client = acquireConnection(sshParams(credentials)); +// if (client) client->disconnectFromHost(); } ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 94ab4efc2..b13f3ebf4 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -72,7 +72,7 @@ public: Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject()); QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); - QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); + QSharedPointer connectToHost(const QSsh::SshConnectionParameters &sshParams); void setCancelInstallation(const bool cancel); ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap &installedContainers); From 20846c8effae90a8f5fdbb745abe8cd2ee636e78 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Tue, 14 Mar 2023 16:49:06 +0300 Subject: [PATCH 04/18] * correct behavior of back button on Android * removing of annoying debug logs on Android --- .../src/org/amnezia/vpn/VPNServiceBinder.kt | 1 - .../android/src/org/amnezia/vpn/qt/VPNActivity.kt | 10 ---------- client/ui/qml/main.qml | 4 ++++ client/ui/uilogic.cpp | 15 +++++++++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt index 239269a5b..e4c6d1c0c 100644 --- a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt +++ b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt @@ -196,7 +196,6 @@ class VPNServiceBinder(service: VPNService) : Binder() { try { mListener?.let { if (it.isBinderAlive) { - Log.i(tag, "Dispatching event: binder alive") val data = Parcel.obtain() data.writeByteArray(payload?.toByteArray(charset("UTF-8"))) it.transact(code, data, Parcel.obtain(), 0) diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt index b5e8d5fbd..1ad19a133 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -99,8 +99,6 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { if (!isBound) { Log.d(TAG, "dispatchParcel: not bound") return - } else { - Log.d(TAG, "dispatchParcel: bound") } val out: Parcel = Parcel.obtain() @@ -331,12 +329,4 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra) } } - - override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) { - onBackPressed() - return true - } - return super.onKeyDown(keyCode, event) - } } diff --git a/client/ui/qml/main.qml b/client/ui/qml/main.qml index b87583845..d91c013f2 100644 --- a/client/ui/qml/main.qml +++ b/client/ui/qml/main.qml @@ -61,8 +61,12 @@ Window { function close_page() { if (pageLoader.depth <= 1) { + if (GC.isMobile()) { + root.close() + } return } + pageLoader.currentItem.deactivated() pageLoader.pop() } diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index 895a526cb..a44f3433a 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -224,9 +224,10 @@ void UiLogic::keyPressEvent(Qt::Key key) m_configurator->sshConfigurator->openSshTerminal(m_settings->serverCredentials(m_settings->defaultServerIndex())); break; case Qt::Key_Escape: - case Qt::Key_Back: if (currentPage() == Page::Vpn) break; if (currentPage() == Page::ServerConfiguringProgress) break; + case Qt::Key_Back: + // if (currentPage() == Page::Start && pagesStack.size() < 2) break; // if (currentPage() == Page::Sites && // ui->tableView_sites->selectionModel()->selection().indexes().size() > 0) { @@ -243,10 +244,16 @@ void UiLogic::keyPressEvent(Qt::Key key) void UiLogic::onCloseWindow() { - if (m_settings->serversCount() == 0) qApp->quit(); - else { - hide(); +#ifdef Q_OS_ANDROID + qApp->quit(); +#else + if (m_settings->serversCount() == 0) + { + qApp->quit(); + } else { + emit hide(); } +#endif } QString UiLogic::containerName(int container) From 7167c2f20da6392074e0b2f7a55e78b9af99d0dd Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Tue, 14 Mar 2023 17:25:28 +0300 Subject: [PATCH 05/18] Fix of "Connect" button state after restoring of an app --- client/android/src/org/amnezia/vpn/VPNService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index 6ca99e87d..6a75b49bd 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -287,6 +287,8 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface { } } set(value) { + field = value + if (value) { mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "") mConnectionTime = System.currentTimeMillis() From 8fa409d6c196aebb07b7078a6c54129dd37893fe Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Wed, 15 Mar 2023 16:46:59 +0300 Subject: [PATCH 06/18] Restoration of bandwidth meter after closing of GUI --- .../platforms/android/android_controller.cpp | 10 +++- client/ui/uilogic.cpp | 1 + client/vpnconnection.cpp | 54 ++++++++++++++----- client/vpnconnection.h | 17 ++++++ 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index a86ab7caf..f1948c03f 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -54,6 +54,10 @@ AndroidController::AndroidController() : QObject() isConnected = doc.object()["connected"].toBool(); + if (isConnected) { + emit scheduleStatusCheckSignal(); + } + emit initialized( true, isConnected, time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime()); @@ -66,9 +70,11 @@ AndroidController::AndroidController() : QObject() Q_UNUSED(parcelBody); qDebug() << "Transact: connected"; - isConnected = true; + if (!isConnected) { + emit scheduleStatusCheckSignal(); + } - emit scheduleStatusCheckSignal(); + isConnected = true; emit connectionStateChanged(VpnProtocol::Connected); }, Qt::QueuedConnection); diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index a44f3433a..26e2f8700 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -133,6 +133,7 @@ void UiLogic::initalizeUiLogic() connect(AndroidController::instance(), &AndroidController::initialized, [this](bool status, bool connected, const QDateTime& connectionDate) { if (connected) { pageLogic()->onConnectionStateChanged(VpnProtocol::Connected); + m_vpnConnection->restoreConnection(); } }); if (!AndroidController::instance()->initialize(pageLogic())) { diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index e01f7e616..273fd7adf 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -20,7 +20,6 @@ #ifdef Q_OS_ANDROID #include "../../platforms/android/android_controller.h" -#include "protocols/android_vpnprotocol.h" #endif #ifdef Q_OS_IOS @@ -353,10 +352,8 @@ void VpnConnection::connectToVpn(int serverIndex, } m_vpnProtocol->prepare(); #elif defined Q_OS_ANDROID - Proto proto = ContainerProps::defaultProtocol(container); - AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration); - connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState); - connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated); + androidVpnProtocol = createDefaultAndroidVpnProtocol(container); + createAndroidConnections(container); m_vpnProtocol.reset(androidVpnProtocol); #elif defined Q_OS_IOS @@ -373,9 +370,7 @@ void VpnConnection::connectToVpn(int serverIndex, m_vpnProtocol.reset(iosVpnProtocol); #endif - connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); - connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState))); - connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); + createProtocolConnections(); m_serverController->disconnectFromHost(credentials); @@ -383,6 +378,44 @@ void VpnConnection::connectToVpn(int serverIndex, if (e) emit VpnProtocol::Error; } +void VpnConnection::restoreConnection() { + createAndroidConnections(); + + m_vpnProtocol.reset(androidVpnProtocol); + + createProtocolConnections(); +} + +void VpnConnection::createAndroidConnections() +{ + int serverIndex = m_settings->defaultServerIndex(); + DockerContainer container = m_settings->defaultContainer(serverIndex); + + createAndroidConnections(container); +} + +void VpnConnection::createAndroidConnections(DockerContainer container) +{ + androidVpnProtocol = createDefaultAndroidVpnProtocol(container); + + connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState); + connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated); +} + +void VpnConnection::createProtocolConnections() { + connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); + connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState))); + connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); +} + +AndroidVpnProtocol* VpnConnection::createDefaultAndroidVpnProtocol(DockerContainer container) +{ + Proto proto = ContainerProps::defaultProtocol(container); + AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration); + + return androidVpnProtocol; +} + QString VpnConnection::bytesPerSecToText(quint64 bytes) { double mbps = bytes * 8 / 1e6; @@ -401,8 +434,6 @@ void VpnConnection::disconnectFromVpn() } #endif - - if (!m_vpnProtocol.data()) { emit connectionStateChanged(VpnProtocol::Disconnected); #ifdef Q_OS_ANDROID @@ -415,11 +446,8 @@ void VpnConnection::disconnectFromVpn() VpnProtocol::VpnConnectionState VpnConnection::connectionState() { - - if (!m_vpnProtocol) return VpnProtocol::Disconnected; return m_vpnProtocol->connectionState(); - } bool VpnConnection::isConnected() const diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 3a0d40646..b0fff3938 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -18,6 +18,10 @@ #include "core/ipcclient.h" #endif +#ifdef Q_OS_ANDROID +#include "protocols/android_vpnprotocol.h" +#endif + class VpnConfigurator; class ServerController; @@ -61,6 +65,10 @@ public: const QString &remoteAddress() const; void addSitesRoutes(const QString &gw, Settings::RouteMode mode); +#ifdef Q_OS_ANDROID + void restoreConnection(); +#endif + public slots: void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); @@ -101,6 +109,15 @@ private: #ifdef Q_OS_IOS IOSVpnProtocol * iosVpnProtocol{nullptr}; #endif +#ifdef Q_OS_ANDROID + AndroidVpnProtocol* androidVpnProtocol = nullptr; + AndroidVpnProtocol* createDefaultAndroidVpnProtocol(DockerContainer container); + + void createAndroidConnections(); + void createAndroidConnections(DockerContainer container); +#endif + + void createProtocolConnections(); }; #endif // VPNCONNECTION_H From 5f85bf62f51ddbb5db96c7f3a04ccdb126355dcc Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Wed, 15 Mar 2023 17:46:22 +0300 Subject: [PATCH 07/18] Missing def check --- client/vpnconnection.cpp | 14 ++++++++------ client/vpnconnection.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 273fd7adf..6d8aa4930 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -378,6 +378,13 @@ void VpnConnection::connectToVpn(int serverIndex, if (e) emit VpnProtocol::Error; } +void VpnConnection::createProtocolConnections() { + connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); + connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState))); + connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); +} + +#ifdef Q_OS_ANDROID void VpnConnection::restoreConnection() { createAndroidConnections(); @@ -402,12 +409,6 @@ void VpnConnection::createAndroidConnections(DockerContainer container) connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated); } -void VpnConnection::createProtocolConnections() { - connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); - connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState))); - connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); -} - AndroidVpnProtocol* VpnConnection::createDefaultAndroidVpnProtocol(DockerContainer container) { Proto proto = ContainerProps::defaultProtocol(container); @@ -415,6 +416,7 @@ AndroidVpnProtocol* VpnConnection::createDefaultAndroidVpnProtocol(DockerContain return androidVpnProtocol; } +#endif QString VpnConnection::bytesPerSecToText(quint64 bytes) { diff --git a/client/vpnconnection.h b/client/vpnconnection.h index b0fff3938..ab8f2de09 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -111,8 +111,8 @@ private: #endif #ifdef Q_OS_ANDROID AndroidVpnProtocol* androidVpnProtocol = nullptr; - AndroidVpnProtocol* createDefaultAndroidVpnProtocol(DockerContainer container); + AndroidVpnProtocol* createDefaultAndroidVpnProtocol(DockerContainer container); void createAndroidConnections(); void createAndroidConnections(DockerContainer container); #endif From 8d45af2034b93106c09cf3f3b56023b9ab86c359 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Thu, 16 Mar 2023 12:13:55 +0300 Subject: [PATCH 08/18] Files now saving with the file picker, not with the sharing mechanism --- .../android/src/org/amnezia/vpn/VPNService.kt | 41 ------------------- .../src/org/amnezia/vpn/VPNServiceBinder.kt | 15 ------- .../src/org/amnezia/vpn/qt/VPNActivity.kt | 41 +++++++++++++++++++ .../platforms/android/android_controller.cpp | 7 +--- .../platforms/android/androidvpnactivity.cpp | 12 +++++- client/platforms/android/androidvpnactivity.h | 1 + 6 files changed, 53 insertions(+), 64 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index 6a75b49bd..412b1e452 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -888,45 +888,4 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface { class CloseableFd(val fd: FileDescriptor) : Closeable { override fun close() = Os.close(fd) } - - fun saveAsFile(configContent: String?, suggestedFileName: String): String { - val rootDirPath = cacheDir.absolutePath - val rootDir = File(rootDirPath) - - if (!rootDir.exists()) { - rootDir.mkdirs() - } - - val fileName = if (!TextUtils.isEmpty(suggestedFileName)) suggestedFileName else "amnezia.cfg" - - val file = File(rootDir, fileName) - - try { - file.bufferedWriter().use { out -> out.write(configContent) } - return file.toString() - } catch (e: Exception) { - e.printStackTrace() - } - - return "" - } - - fun shareFile(attachmentFile: String?) { - try { - val intent = Intent(Intent.ACTION_SEND) - intent.type = "text/*" - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - - val file = File(attachmentFile) - val uri = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.fileprovider", file) - intent.putExtra(Intent.EXTRA_STREAM, uri) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - val createChooser = Intent.createChooser(intent, "Config sharing") - createChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(createChooser) - } catch (e: Exception) { - Log.i(tag, e.message.toString()) - } - } } diff --git a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt index e4c6d1c0c..bc44217e1 100644 --- a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt +++ b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt @@ -32,7 +32,6 @@ class VPNServiceBinder(service: VPNService) : Binder() { const val resumeActivate = 7 const val setNotificationText = 8 const val setFallBackNotification = 9 - const val shareConfig = 10 const val importConfig = 11 } @@ -139,20 +138,6 @@ class VPNServiceBinder(service: VPNService) : Binder() { return true } - ACTIONS.shareConfig -> { - val byteArray = data.createByteArray() - val json = byteArray?.let { String(it) } - val config = JSONObject(json) - val configContent = config.getString("data") - val suggestedName = config.getString("suggestedName") - - val filePath = mService.saveAsFile(configContent, suggestedName) - Log.i(tag, "save file: $filePath") - - mService.shareFile(filePath) - return true - } - ACTIONS.importConfig -> { val buffer = data.readString() diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt index 1ad19a133..9d13b9303 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -37,6 +37,9 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { private val STORAGE_PERMISSION_CODE = 42 private val CAMERA_ACTION_CODE = 101 + private val CREATE_FILE_ACTION_CODE = 102 + + private var tmpFileContentToSave: String = "" companion object { private lateinit var instance: VPNActivity @@ -56,6 +59,10 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { @JvmStatic fun sendToService(actionCode: Int, body: String) { VPNActivity.getInstance().dispatchParcel(actionCode, body) } + + @JvmStatic fun saveFileAs(fileContent: String, suggestedName: String) { + VPNActivity.getInstance().saveFile(fileContent, suggestedName) + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -76,6 +83,18 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { startActivityForResult(intent, CAMERA_ACTION_CODE) } + private fun saveFile(fileContent: String, suggestedName: String) { + tmpFileContentToSave = fileContent + + val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "text/*" + putExtra(Intent.EXTRA_TITLE, suggestedName) + } + + startActivityForResult(intent, CREATE_FILE_ACTION_CODE) + } + override fun getSystemService(name: String): Any? { return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") { // QT will always attempt to read the clipboard if content is there. @@ -328,5 +347,27 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { val extra = data?.getStringExtra("result") ?: "" onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra) } + + if (requestCode == CREATE_FILE_ACTION_CODE && resultCode == RESULT_OK) { + data?.data?.also { uri -> + alterDocument(uri) + } + } + } + + private fun alterDocument(uri: Uri) { + try { + applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd -> + FileOutputStream(fd.fileDescriptor).use { fos -> + fos.write(tmpFileContentToSave.toByteArray()) + } + } + } catch (e: FileNotFoundException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } + + tmpFileContentToSave = "" } } diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index f1948c03f..0db1bd466 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -209,12 +209,7 @@ void AndroidController::setNotificationText(const QString& title, } void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) { - QJsonObject rootObject; - rootObject["data"] = configContent; - rootObject["suggestedName"] = suggestedName; - QJsonDocument doc(rootObject); - - AndroidVPNActivity::sendToService(ServiceAction::ACTION_SHARE_CONFIG, doc.toJson()); + AndroidVPNActivity::saveFileAs(configContent, suggestedName); } /* diff --git a/client/platforms/android/androidvpnactivity.cpp b/client/platforms/android/androidvpnactivity.cpp index 176390235..2076280d7 100644 --- a/client/platforms/android/androidvpnactivity.cpp +++ b/client/platforms/android/androidvpnactivity.cpp @@ -57,6 +57,14 @@ void AndroidVPNActivity::startQrCodeReader() QJniObject::callStaticMethod(CLASSNAME, "startQrCodeReader", "()V"); } +void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilename) { + QJniObject::callStaticMethod( + CLASSNAME, + "saveFileAs", "(Ljava/lang/String;Ljava/lang/String;)V", + QJniObject::fromString(fileContent).object(), + QJniObject::fromString(suggestedFilename).object()); +} + // static AndroidVPNActivity* AndroidVPNActivity::instance() { if (s_instance == nullptr) { @@ -70,9 +78,9 @@ AndroidVPNActivity* AndroidVPNActivity::instance() { void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) { int messageType = (int)type; - QJniEnvironment env; QJniObject::callStaticMethod( - CLASSNAME, "sendToService", "(ILjava/lang/String;)V", + CLASSNAME, + "sendToService", "(ILjava/lang/String;)V", static_cast(messageType), QJniObject::fromString(data).object()); } diff --git a/client/platforms/android/androidvpnactivity.h b/client/platforms/android/androidvpnactivity.h index 1bc1a5226..130bef884 100644 --- a/client/platforms/android/androidvpnactivity.h +++ b/client/platforms/android/androidvpnactivity.h @@ -75,6 +75,7 @@ public: static void sendToService(ServiceAction type, const QString& data); static void connectService(); static void startQrCodeReader(); + static void saveFileAs(QString fileContent, QString suggestedFilename); signals: void serviceConnected(); From c0bb06bf49b4691319456fe50140a45e13e65720 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Fri, 17 Mar 2023 16:53:02 +0300 Subject: [PATCH 09/18] Fix of copying to clipboard --- .../src/org/amnezia/vpn/qt/VPNActivity.kt | 17 +++++++++++++++++ client/platforms/android/android_controller.cpp | 6 ++++++ client/platforms/android/android_controller.h | 2 +- client/platforms/android/androidvpnactivity.cpp | 8 ++++++++ client/platforms/android/androidvpnactivity.h | 1 + client/ui/uilogic.cpp | 4 ++++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt index 9d13b9303..17888a0da 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -5,6 +5,8 @@ package org.amnezia.vpn.qt; import android.Manifest +import android.content.ClipData +import android.content.ClipboardManager import android.content.ComponentName import android.content.ContentResolver import android.content.Context @@ -63,6 +65,10 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { @JvmStatic fun saveFileAs(fileContent: String, suggestedName: String) { VPNActivity.getInstance().saveFile(fileContent, suggestedName) } + + @JvmStatic fun putTextToClipboard(text: String) { + VPNActivity.getInstance().putToClipboard(text) + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -370,4 +376,15 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { tmpFileContentToSave = "" } + + private fun putToClipboard(text: String) { + this.runOnUiThread { + val clipboard = applicationContext.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager? + + if (clipboard != null) { + val clip: ClipData = ClipData.newPlainText("", text) + clipboard.setPrimaryClip(clip) + } + } + } } diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 0db1bd466..3a93ea19d 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -15,6 +15,7 @@ #include "private/qandroidextras_p.h" #include "ui/pages_logic/StartPageLogic.h" +#include "androidvpnactivity.h" #include "androidutils.h" namespace { @@ -267,6 +268,11 @@ void AndroidController::startQrReaderActivity() AndroidVPNActivity::instance()->startQrCodeReader(); } +void AndroidController::copyTextToClipboard(QString text) +{ + AndroidVPNActivity::instance()->copyTextToClipboard(text); +} + void AndroidController::scheduleStatusCheckSlot() { QTimer::singleShot(1000, [this]() { diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 59ecd9b3d..00b372251 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -11,7 +11,6 @@ #include "ui/pages_logic/StartPageLogic.h" #include "protocols/vpnprotocol.h" -#include "androidvpnactivity.h" using namespace amnezia; @@ -44,6 +43,7 @@ public: void setVpnConfig(const QJsonObject &newVpnConfig); void startQrReaderActivity(); + void copyTextToClipboard(QString text); signals: void connectionStateChanged(VpnProtocol::VpnConnectionState state); diff --git a/client/platforms/android/androidvpnactivity.cpp b/client/platforms/android/androidvpnactivity.cpp index 2076280d7..9431597bc 100644 --- a/client/platforms/android/androidvpnactivity.cpp +++ b/client/platforms/android/androidvpnactivity.cpp @@ -65,6 +65,14 @@ void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilena QJniObject::fromString(suggestedFilename).object()); } +void AndroidVPNActivity::copyTextToClipboard(QString text) +{ + QJniObject::callStaticMethod( + CLASSNAME, + "putTextToClipboard", "(Ljava/lang/String;)V", + QJniObject::fromString(text).object()); +} + // static AndroidVPNActivity* AndroidVPNActivity::instance() { if (s_instance == nullptr) { diff --git a/client/platforms/android/androidvpnactivity.h b/client/platforms/android/androidvpnactivity.h index 130bef884..8eeb5598b 100644 --- a/client/platforms/android/androidvpnactivity.h +++ b/client/platforms/android/androidvpnactivity.h @@ -76,6 +76,7 @@ public: static void connectService(); static void startQrCodeReader(); static void saveFileAs(QString fileContent, QString suggestedFilename); + static void copyTextToClipboard(QString text); signals: void serviceConnected(); diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index 26e2f8700..00cc5dd18 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -474,7 +474,11 @@ void UiLogic::saveBinaryFile(const QString &desc, QString ext, const QString &da void UiLogic::copyToClipboard(const QString &text) { +#ifdef Q_OS_ANDROID + AndroidController::instance()->copyTextToClipboard(text); +#elif qApp->clipboard()->setText(text); +#endif } void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QString& data) { From 3a5317f16a35a3b11d978589e876ab5702c29710 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Thu, 23 Mar 2023 15:13:15 +0300 Subject: [PATCH 10/18] Implementation of "migration manager" to fix placement of config files issue appeared after moving from Qt 5 to Qt 6 --- client/CMakeLists.txt | 2 + client/main.cpp | 4 ++ client/migrations.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++ client/migrations.h | 24 ++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 client/migrations.cpp create mode 100644 client/migrations.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 50d6d0f78..605a4d09d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -59,6 +59,7 @@ include_directories( ) set(HEADERS ${HEADERS} + ${CMAKE_CURRENT_LIST_DIR}/migrations.h ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h @@ -85,6 +86,7 @@ if(NOT IOS) endif() set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_LIST_DIR}/migrations.cpp ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp diff --git a/client/main.cpp b/client/main.cpp index 62685953b..f20c5dd15 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -4,6 +4,7 @@ #include "amnezia_application.h" #include "defines.h" +#include "migrations.h" #ifdef Q_OS_WIN #include "Windows.h" @@ -16,6 +17,9 @@ int main(int argc, char *argv[]) { + Migrations migrationsManager; + migrationsManager.doMigrations(); + QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false")); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); diff --git a/client/migrations.cpp b/client/migrations.cpp new file mode 100644 index 000000000..6fac6be71 --- /dev/null +++ b/client/migrations.cpp @@ -0,0 +1,86 @@ +#include "migrations.h" + +#include +#include +#include +#include + +#include "defines.h" + +Migrations::Migrations(QObject *parent) + : QObject{parent} +{ + QString version(APP_MAJOR_VERSION); + + QStringList versionDigits = version.split("."); + + if (versionDigits.size() >= 3) { + currentMajor = versionDigits[0].toInt(); + currentMinor = versionDigits[1].toInt(); + currentMicro = versionDigits[2].toInt(); + } + + if (versionDigits.size() == 4) { + currentPatch = versionDigits[3].toInt(); + } +} + +void Migrations::doMigrations() +{ + if (currentMajor == 3) { + migrateV3(); + } +} + +void Migrations::migrateV3() +{ +#ifdef Q_OS_ANDROID + qDebug() << "Migration to V3 on Android..."; + + QString packageName = "org.amnezia.vpn"; + + QDir dir("."); + QString currentDir = dir.absolutePath(); + + int packageNameIndex = currentDir.indexOf(packageName); + + if (packageNameIndex == -1) { + return; + } + + QString rootLocation = currentDir.left(packageNameIndex + packageName.size()); + + if (rootLocation.isEmpty()) { + return; + } + + QString location = rootLocation + "/files/.config/AmneziaVPN.ORG/AmneziaVPN.conf"; + + QFile oldConfig(location); + + if (oldConfig.exists()) { + QString newConfigPath = rootLocation + "/files/settings"; + + QDir newConfigDir(newConfigPath); + + newConfigPath += "/AmneziaVPN.ORG"; + + bool mkPathRes = newConfigDir.mkpath(newConfigPath); + + if (!mkPathRes) { + return; + } + + QFile newConfigFile(newConfigPath + "/AmneziaVPN.conf"); + + if (!newConfigFile.exists()) { + bool cpResult = QFile::copy(oldConfig.fileName(), newConfigFile.fileName()); + if (cpResult) { + oldConfig.remove(); + QDir oldConfigDir(rootLocation + "/files/.config"); + oldConfigDir.rmdir("AmneziaVPN.ORG"); + } + } + } +#endif +} diff --git a/client/migrations.h b/client/migrations.h new file mode 100644 index 000000000..ea6bae92c --- /dev/null +++ b/client/migrations.h @@ -0,0 +1,24 @@ +#ifndef MIGRATIONS_H +#define MIGRATIONS_H + +#include + +class Migrations : public QObject +{ + Q_OBJECT +public: + explicit Migrations(QObject *parent = nullptr); + + void doMigrations(); + +private: + void migrateV3(); + +private: + int currentMajor = 0; + int currentMinor = 0; + int currentMicro = 0; + int currentPatch = 0; +}; + +#endif // MIGRATIONS_H From 8bbc0b9e1a50ac1cd0823181750a4a2c40644b37 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 29 Mar 2023 09:05:35 +0300 Subject: [PATCH 11/18] set m_installCredentials when visiting the ServerContainers page --- client/ui/pages_logic/ServerContainersLogic.cpp | 1 + client/ui/pages_logic/ServerListLogic.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index aeec81617..870a10dbc 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -31,6 +31,7 @@ void ServerContainersLogic::onUpdatePage() p_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); set_isManagedServer(m_settings->haveAuthData(uiLogic()->m_selectedServerIndex)); + uiLogic()->m_installCredentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex); emit updatePage(); } diff --git a/client/ui/pages_logic/ServerListLogic.cpp b/client/ui/pages_logic/ServerListLogic.cpp index e91b1e330..79d13c8b4 100644 --- a/client/ui/pages_logic/ServerListLogic.cpp +++ b/client/ui/pages_logic/ServerListLogic.cpp @@ -21,7 +21,6 @@ void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) { uiLogic()->m_selectedServerIndex = index; - uiLogic()->m_installCredentials = m_settings->serverCredentials(index); uiLogic()->goToPage(Page::ServerSettings); } From 5fa6e755b206353b072d384781344d06217c340b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 29 Mar 2023 09:03:26 +0300 Subject: [PATCH 12/18] changed the existsAnyServer value to properly display locked settings --- client/ui/pages_logic/GeneralSettingsLogic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/pages_logic/GeneralSettingsLogic.cpp b/client/ui/pages_logic/GeneralSettingsLogic.cpp index cbdf26927..0e92f8c94 100644 --- a/client/ui/pages_logic/GeneralSettingsLogic.cpp +++ b/client/ui/pages_logic/GeneralSettingsLogic.cpp @@ -13,7 +13,7 @@ GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): void GeneralSettingsLogic::onUpdatePage() { uiLogic()->m_selectedServerIndex = m_settings->defaultServerIndex(); - set_existsAnyServer(uiLogic()->m_selectedServerIndex >= 0); + set_existsAnyServer(m_settings->serversCount() > 0); uiLogic()->m_selectedDockerContainer = m_settings->defaultContainer(m_settings->defaultServerIndex()); set_pushButtonGeneralSettingsShareConnectionEnable(m_settings->haveAuthData(m_settings->defaultServerIndex())); From c48d65525a43aed00f2e1668940aa316f3975565 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 29 Mar 2023 09:05:35 +0300 Subject: [PATCH 13/18] set m_installCredentials when visiting the ServerContainers page --- client/ui/pages_logic/ServerContainersLogic.cpp | 1 + client/ui/pages_logic/ServerListLogic.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index aeec81617..870a10dbc 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -31,6 +31,7 @@ void ServerContainersLogic::onUpdatePage() p_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); set_isManagedServer(m_settings->haveAuthData(uiLogic()->m_selectedServerIndex)); + uiLogic()->m_installCredentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex); emit updatePage(); } diff --git a/client/ui/pages_logic/ServerListLogic.cpp b/client/ui/pages_logic/ServerListLogic.cpp index e91b1e330..79d13c8b4 100644 --- a/client/ui/pages_logic/ServerListLogic.cpp +++ b/client/ui/pages_logic/ServerListLogic.cpp @@ -21,7 +21,6 @@ void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) { uiLogic()->m_selectedServerIndex = index; - uiLogic()->m_installCredentials = m_settings->serverCredentials(index); uiLogic()->goToPage(Page::ServerSettings); } From 3261c36f97054429088decaf57b2dc241f9f38f6 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 29 Mar 2023 09:03:26 +0300 Subject: [PATCH 14/18] changed the existsAnyServer value to properly display locked settings --- client/ui/pages_logic/GeneralSettingsLogic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/pages_logic/GeneralSettingsLogic.cpp b/client/ui/pages_logic/GeneralSettingsLogic.cpp index cbdf26927..0e92f8c94 100644 --- a/client/ui/pages_logic/GeneralSettingsLogic.cpp +++ b/client/ui/pages_logic/GeneralSettingsLogic.cpp @@ -13,7 +13,7 @@ GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): void GeneralSettingsLogic::onUpdatePage() { uiLogic()->m_selectedServerIndex = m_settings->defaultServerIndex(); - set_existsAnyServer(uiLogic()->m_selectedServerIndex >= 0); + set_existsAnyServer(m_settings->serversCount() > 0); uiLogic()->m_selectedDockerContainer = m_settings->defaultContainer(m_settings->defaultServerIndex()); set_pushButtonGeneralSettingsShareConnectionEnable(m_settings->haveAuthData(m_settings->defaultServerIndex())); From 90c8fbb49598a0949d7ac7142da03847b4cd9c3e Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Wed, 29 Mar 2023 09:53:18 +0300 Subject: [PATCH 15/18] Update Room to fix build on Apple M1 --- client/android/shadowsocks/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/android/shadowsocks/build.gradle b/client/android/shadowsocks/build.gradle index 69ebd0bd3..f1d738b2c 100644 --- a/client/android/shadowsocks/build.gradle +++ b/client/android/shadowsocks/build.gradle @@ -35,7 +35,7 @@ androidExtensions { } //def lifecycleVersion = '2.0.0' -//def roomVersion = '2.0.0' +def roomVersion = "2.4.3" //def preferencexVersion = '1.0.0' dependencies { @@ -45,13 +45,12 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0" - implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0" implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" - implementation "androidx.room:room-runtime:2.2.5" // runtime + implementation "androidx.room:room-runtime:$roomVersion" // runtime implementation "androidx.preference:preference:1.1.0" implementation "androidx.work:work-runtime-ktx:2.7.1" implementation "androidx.browser:browser:1.3.0-alpha01" @@ -65,6 +64,7 @@ dependencies { // api "com.takisoft.preferencex:preferencex:1.0.0" implementation 'com.takisoft.preferencex:preferencex:1.1.0' api 'org.connectbot.jsocks:jsocks:1.0.0' - kapt "androidx.room:room-compiler:2.2.5" + + kapt "androidx.room:room-compiler:$roomVersion" kapt "androidx.lifecycle:lifecycle-compiler:2.4.0" } From 2225a735ca872151c580dd4f194f715cbc835a8b Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Wed, 29 Mar 2023 12:01:18 +0300 Subject: [PATCH 16/18] Fixed typo --- client/ui/uilogic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index 00cc5dd18..cdfd18330 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -476,7 +476,7 @@ void UiLogic::copyToClipboard(const QString &text) { #ifdef Q_OS_ANDROID AndroidController::instance()->copyTextToClipboard(text); -#elif +#else qApp->clipboard()->setText(text); #endif } From 9e461ef6e131d5075b0faeda55366463363999d3 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Wed, 29 Mar 2023 16:09:46 +0300 Subject: [PATCH 17/18] Fixed import from external applications --- client/android/AndroidManifest.xml | 98 ++++++------ client/android/build.gradle | 6 +- .../res/layout/activity_import_config.xml | 5 + .../amnezia/vpn/qt/ImportConfigActivity.kt | 140 ++++++++++++++++++ .../src/org/amnezia/vpn/qt/VPNActivity.kt | 135 +++++------------ .../platforms/android/android_controller.cpp | 3 +- client/ui/pages_logic/StartPageLogic.cpp | 21 ++- client/ui/pages_logic/StartPageLogic.h | 2 + 8 files changed, 256 insertions(+), 154 deletions(-) create mode 100644 client/android/res/layout/activity_import_config.xml create mode 100644 client/android/src/org/amnezia/vpn/qt/ImportConfigActivity.kt diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 22c828fb0..be67c8ba4 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -51,50 +51,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/qt/ImportConfigActivity.kt b/client/android/src/org/amnezia/vpn/qt/ImportConfigActivity.kt new file mode 100644 index 000000000..de175a980 --- /dev/null +++ b/client/android/src/org/amnezia/vpn/qt/ImportConfigActivity.kt @@ -0,0 +1,140 @@ +package org.amnezia.vpn.qt + +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import android.content.ContentResolver +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.MediaStore +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat + +import java.io.* + +import org.amnezia.vpn.R + + +const val INTENT_ACTION_IMPORT_CONFIG = "org.amnezia.vpn.qt.IMPORT_CONFIG" + +class ImportConfigActivity : Activity() { + + private val STORAGE_PERMISSION_CODE = 42 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_import_config) + startReadConfig(intent) + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + startReadConfig(intent) + } + + private fun startMainActivity(config: String?) { + + if (config == null || config.length == 0) { + return + } + + val activityIntent = Intent(applicationContext, VPNActivity::class.java) + activityIntent.action = INTENT_ACTION_IMPORT_CONFIG + activityIntent.addCategory("android.intent.category.DEFAULT") + activityIntent.putExtra("CONFIG", config) + + startActivity(activityIntent) + finish() + } + + private fun startReadConfig(intent: Intent?) { + val newIntent = intent + val newIntentAction: String = newIntent?.action ?: "" + + if (newIntent != null && newIntentAction == Intent.ACTION_VIEW) { + readConfig(newIntent, newIntentAction) + } + } + + private fun readConfig(newIntent: Intent, newIntentAction: String) { + if (isReadStorageAllowed()) { + val configString = processIntent(newIntent, newIntentAction) + startMainActivity(configString) + } else { + requestStoragePermission() + } + } + + private fun requestStoragePermission() { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + if (requestCode == STORAGE_PERMISSION_CODE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + val configString = processIntent(intent, intent.action!!) + + if (configString != null) { + startMainActivity(configString) + } + } else { + Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show() + } + } + } + + private fun processIntent(intent: Intent, action: String): String? { + val scheme = intent.scheme + + if (scheme == null) { + return null + } + + if (action.compareTo(Intent.ACTION_VIEW) == 0) { + val resolver = contentResolver + + if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) { + val uri = intent.data + val name: String? = getContentName(resolver, uri) + + println("Content intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) + + val input = resolver.openInputStream(uri!!) + + return input?.bufferedReader()?.use(BufferedReader::readText) + } else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) { + val uri = intent.data + val name = uri!!.lastPathSegment + + println("File intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) + + val input = resolver.openInputStream(uri) + + return input?.bufferedReader()?.use(BufferedReader::readText) + } + } + + return null + } + + private fun isReadStorageAllowed(): Boolean { + val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + return permissionStatus == PackageManager.PERMISSION_GRANTED + } + + private fun getContentName(resolver: ContentResolver?, uri: Uri?): String? { + val cursor = resolver!!.query(uri!!, null, null, null, null) + + cursor.use { + cursor!!.moveToFirst() + val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) + return if (nameIndex >= 0) { + return cursor.getString(nameIndex) + } else { + null + } + } + } +} \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt index 17888a0da..d2b5b7ab5 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -34,15 +34,23 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { private var configString: String? = null private var vpnServiceBinder: IBinder? = null private var isBound = false + set(value) { + field = value + + if (value && configString != null) { + sendImportConfigCommand() + } + } private val TAG = "VPNActivity" - private val STORAGE_PERMISSION_CODE = 42 private val CAMERA_ACTION_CODE = 101 private val CREATE_FILE_ACTION_CODE = 102 private var tmpFileContentToSave: String = "" + private val delayedCommands: ArrayList> = ArrayList() + companion object { private lateinit var instance: VPNActivity @@ -72,16 +80,16 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - val newIntent = intent - val newIntentAction = newIntent.action - - if (newIntent != null && newIntentAction != null) { - configString = processIntent(newIntent, newIntentAction) - } - super.onCreate(savedInstanceState) instance = this + + val newIntent = intent + val newIntentAction: String? = newIntent.action + + if (newIntent != null && newIntentAction != null && newIntentAction == "org.amnezia.vpn.qt.IMPORT_CONFIG") { + configString = newIntent.getStringExtra("CONFIG") + } } private fun startQrCodeActivity() { @@ -123,9 +131,22 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { private fun dispatchParcel(actionCode: Int, body: String) { if (!isBound) { Log.d(TAG, "dispatchParcel: not bound") + delayedCommands.add(Pair(actionCode, body)) return } + if (delayedCommands.size > 0) { + for (command in delayedCommands) { + processCommand(command.first, command.second) + } + + delayedCommands.clear() + } + + processCommand(actionCode, body) + } + + private fun processCommand(actionCode: Int, body: String) { val out: Parcel = Parcel.obtain() out.writeByteArray(body.toByteArray()) @@ -141,19 +162,15 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } override fun onNewIntent(newIntent: Intent) { - intent = newIntent + super.onNewIntent(intent) + + setIntent(newIntent) val newIntentAction = newIntent.action - if (newIntent != null && newIntentAction != null && newIntentAction != Intent.ACTION_MAIN) { - if (isReadStorageAllowed()) { - configString = processIntent(newIntent, newIntentAction) - } else { - requestStoragePermission() - } - } - - super.onNewIntent(intent) + if (newIntent != null && newIntentAction != null && newIntentAction == INTENT_ACTION_IMPORT_CONFIG) { + configString = newIntent.getStringExtra("CONFIG") + } } override fun onResume() { @@ -164,84 +181,6 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } } - private fun isReadStorageAllowed(): Boolean { - val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) - return permissionStatus == PackageManager.PERMISSION_GRANTED - } - - private fun requestStoragePermission() { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE) - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (requestCode == STORAGE_PERMISSION_CODE) { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "Storage read permission granted") - - if (configString == null) { - configString = processIntent(intent, intent.action!!) - } - - if (configString != null) { - Log.d(TAG, "not empty") - sendImportConfigCommand() - } else { - Log.d(TAG, "empty") - } - } else { - Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show() - } - } - } - - private fun processIntent(intent: Intent, action: String): String? { - val scheme = intent.scheme - - if (scheme == null) { - return null - } - - if (action.compareTo(Intent.ACTION_VIEW) == 0) { - val resolver = contentResolver - - if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) { - val uri = intent.data - val name: String? = getContentName(resolver, uri) - - Log.d(TAG, "Content intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) - - val input = resolver.openInputStream(uri!!) - - return input?.bufferedReader()?.use(BufferedReader::readText) - } else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) { - val uri = intent.data - val name = uri!!.lastPathSegment - - Log.d(TAG, "File intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) - - val input = resolver.openInputStream(uri) - - return input?.bufferedReader()?.use(BufferedReader::readText) - } - } - - return null - } - - private fun getContentName(resolver: ContentResolver?, uri: Uri?): String? { - val cursor = resolver!!.query(uri!!, null, null, null, null) - - cursor.use { - cursor!!.moveToFirst() - val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) - return if (nameIndex >= 0) { - return cursor.getString(nameIndex) - } else { - null - } - } - } - private fun sendImportConfigCommand() { if (configString != null) { val msg: Parcel = Parcel.obtain() @@ -257,7 +196,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } } - private var connection: ServiceConnection = object : ServiceConnection { + private fun createConnection() = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, binder: IBinder) { vpnServiceBinder = binder @@ -283,6 +222,8 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } } + private var connection: ServiceConnection = createConnection() + private fun registerBinder(): Boolean { val binder = VPNClientBinder() val out: Parcel = Parcel.obtain() diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 3a93ea19d..2e5641c94 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -92,7 +92,6 @@ AndroidController::AndroidController() : QObject() connect(activity, &AndroidVPNActivity::eventStatisticUpdate, this, [this](const QString& parcelBody) { qDebug() << "Transact: update"; - auto doc = QJsonDocument::fromJson(parcelBody.toUtf8()); QString rx = doc.object()["rx_bytes"].toString(); @@ -250,7 +249,7 @@ void AndroidController::cleanupBackendLogs() { } void AndroidController::importConfig(const QString& data){ - m_startPageLogic->importConnectionFromCode(data); + m_startPageLogic->selectConfigFormat(data); } const QJsonObject &AndroidController::vpnConfig() const diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 8c78667d7..7eb67adfa 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -175,14 +175,7 @@ void StartPageLogic::onPushButtonImportOpenFile() file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); - auto configFormat = checkConfigFormat(QString(data)); - if (configFormat == ConfigTypes::OpenVpn) { - importConnectionFromOpenVpnConfig(QString(data)); - } else if (configFormat == ConfigTypes::WireGuard) { - importConnectionFromWireguardConfig(QString(data)); - } else { - importConnectionFromCode(QString(data)); - } + selectConfigFormat(QString(data)); } #ifdef Q_OS_ANDROID @@ -192,6 +185,18 @@ void StartPageLogic::startQrDecoder() } #endif +void StartPageLogic::selectConfigFormat(QString configData) +{ + auto configFormat = checkConfigFormat(configData); + if (configFormat == ConfigTypes::OpenVpn) { + importConnectionFromOpenVpnConfig(configData); + } else if (configFormat == ConfigTypes::WireGuard) { + importConnectionFromWireguardConfig(configData); + } else { + importConnectionFromCode(configData); + } +} + bool StartPageLogic::importConnection(const QJsonObject &profile) { ServerCredentials credentials; diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index b3dea0020..6f21c1050 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -35,6 +35,8 @@ public: Q_INVOKABLE void startQrDecoder(); #endif + void selectConfigFormat(QString configData); + bool importConnection(const QJsonObject &profile); bool importConnectionFromCode(QString code); bool importConnectionFromQr(const QByteArray &data); From af5b9172ef0d328ea7bf80b11f293d5d874d0434 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sun, 2 Apr 2023 15:19:59 +0300 Subject: [PATCH 18/18] removed unused disconnectFromHost() from ServerController --- client/core/servercontroller.cpp | 6 ------ client/core/servercontroller.h | 1 - client/ui/pages_logic/AdvancedServerSettingsLogic.cpp | 1 - client/ui/uilogic.cpp | 3 --- client/vpnconnection.cpp | 2 -- 5 files changed, 13 deletions(-) diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 132cb18c7..67a21bba5 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -839,12 +839,6 @@ void ServerController::setCancelInstallation(const bool cancel) m_cancelInstallation = cancel; } -void ServerController::disconnectFromHost(const ServerCredentials &credentials) -{ -// SshConnection *client = acquireConnection(sshParams(credentials)); -// if (client) client->disconnectFromHost(); -} - ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) { return runScript(credentials, diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index b13f3ebf4..68d474177 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -31,7 +31,6 @@ public: ErrorCode fromSshProcessExitStatus(int exitStatus); QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); - void disconnectFromHost(const ServerCredentials &credentials); ErrorCode removeAllContainers(const ServerCredentials &credentials); ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container); diff --git a/client/ui/pages_logic/AdvancedServerSettingsLogic.cpp b/client/ui/pages_logic/AdvancedServerSettingsLogic.cpp index 0b02241c1..187af9ee5 100644 --- a/client/ui/pages_logic/AdvancedServerSettingsLogic.cpp +++ b/client/ui/pages_logic/AdvancedServerSettingsLogic.cpp @@ -45,7 +45,6 @@ void AdvancedServerSettingsLogic::onPushButtonClearServerClicked() } ErrorCode e = m_serverController->removeAllContainers(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex)); - m_serverController->disconnectFromHost(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex)); if (e) { emit uiLogic()->showWarningMessage(tr("Error occurred while cleaning the server.") + "\n" + tr("Error message: ") + errorString(e) + "\n" + diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index cdfd18330..3dbd7b862 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -342,8 +342,6 @@ void UiLogic::installServer(QPair &container) errorCode = pageLogic()->doInstallAction(installAction, pageFunc, progressBarFunc, noButton, waitInfoFunc, busyInfoFunc, cancelButtonFunc); - m_serverController->disconnectFromHost(m_installCredentials); - if (errorCode == ErrorCode::NoError) { if (!isServerCreated) { QJsonObject server; @@ -534,7 +532,6 @@ ErrorCode UiLogic::addAlreadyInstalledContainersGui(bool createNewServer, bool & QMap installedContainers; ErrorCode errorCode = m_serverController->getAlreadyInstalledContainers(credentials, installedContainers); - m_serverController->disconnectFromHost(credentials); if (errorCode != ErrorCode::NoError) { return errorCode; } diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 6d8aa4930..21643ae55 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -372,8 +372,6 @@ void VpnConnection::connectToVpn(int serverIndex, createProtocolConnections(); - m_serverController->disconnectFromHost(credentials); - e = m_vpnProtocol.data()->start(); if (e) emit VpnProtocol::Error; }