From 20846c8effae90a8f5fdbb745abe8cd2ee636e78 Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Tue, 14 Mar 2023 16:49:06 +0300 Subject: [PATCH 01/12] * 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 239269a5..e4c6d1c0 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 b5e8d5fb..1ad19a13 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 b8758384..d91c013f 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 895a526c..a44f3433 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 02/12] 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 6ca99e87..6a75b49b 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 03/12] 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 a86ab7ca..f1948c03 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 a44f3433..26e2f870 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 e01f7e61..273fd7ad 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 3a0d4064..b0fff393 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 04/12] 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 273fd7ad..6d8aa493 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 b0fff393..ab8f2de0 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 05/12] 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 6a75b49b..412b1e45 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 e4c6d1c0..bc44217e 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 1ad19a13..9d13b930 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 f1948c03..0db1bd46 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 17639023..2076280d 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 1bc1a522..130bef88 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 06/12] 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 9d13b930..17888a0d 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 0db1bd46..3a93ea19 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 59ecd9b3..00b37225 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 2076280d..9431597b 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 130bef88..8eeb5598 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 26e2f870..00cc5dd1 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 07/12] 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 50d6d0f7..605a4d09 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 62685953..f20c5dd1 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 00000000..6fac6be7 --- /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 00000000..ea6bae92 --- /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 08/12] 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 aeec8161..870a10db 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 e91b1e33..79d13c8b 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 09/12] 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 cbdf2692..0e92f8c9 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 10/12] 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 69ebd0bd..f1d738b2 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 11/12] 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 00cc5dd1..cdfd1833 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 12/12] 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 22c828fb..be67c8ba 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 00000000..de175a98 --- /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 17888a0d..d2b5b7ab 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 3a93ea19..2e5641c9 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 8c78667d..7eb67adf 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 b3dea002..6f21c105 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);