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);