Merge pull request #202 from amnezia-vpn/fix/android_various_fixes
Fix/android various fixes
This commit is contained in:
commit
7e663b05d5
16 changed files with 293 additions and 96 deletions
|
@ -59,6 +59,7 @@ include_directories(
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/migrations.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
|
${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
|
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
|
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
|
||||||
|
@ -85,6 +86,7 @@ if(NOT IOS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
|
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
|
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
||||||
|
|
|
@ -35,7 +35,7 @@ androidExtensions {
|
||||||
}
|
}
|
||||||
|
|
||||||
//def lifecycleVersion = '2.0.0'
|
//def lifecycleVersion = '2.0.0'
|
||||||
//def roomVersion = '2.0.0'
|
def roomVersion = "2.4.3"
|
||||||
//def preferencexVersion = '1.0.0'
|
//def preferencexVersion = '1.0.0'
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
|
@ -45,13 +45,12 @@ dependencies {
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
|
||||||
|
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.2.0"
|
implementation "androidx.core:core-ktx:1.2.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-core-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.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.preference:preference:1.1.0"
|
||||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
||||||
implementation "androidx.browser:browser:1.3.0-alpha01"
|
implementation "androidx.browser:browser:1.3.0-alpha01"
|
||||||
|
@ -65,6 +64,7 @@ dependencies {
|
||||||
// api "com.takisoft.preferencex:preferencex:1.0.0"
|
// api "com.takisoft.preferencex:preferencex:1.0.0"
|
||||||
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
|
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
|
||||||
api 'org.connectbot.jsocks:jsocks:1.0.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"
|
kapt "androidx.lifecycle:lifecycle-compiler:2.4.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,8 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
|
field = value
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "")
|
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "")
|
||||||
mConnectionTime = System.currentTimeMillis()
|
mConnectionTime = System.currentTimeMillis()
|
||||||
|
@ -886,45 +888,4 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||||
class CloseableFd(val fd: FileDescriptor) : Closeable {
|
class CloseableFd(val fd: FileDescriptor) : Closeable {
|
||||||
override fun close() = Os.close(fd)
|
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
const val resumeActivate = 7
|
const val resumeActivate = 7
|
||||||
const val setNotificationText = 8
|
const val setNotificationText = 8
|
||||||
const val setFallBackNotification = 9
|
const val setFallBackNotification = 9
|
||||||
const val shareConfig = 10
|
|
||||||
const val importConfig = 11
|
const val importConfig = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,20 +138,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
return true
|
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 -> {
|
ACTIONS.importConfig -> {
|
||||||
val buffer = data.readString()
|
val buffer = data.readString()
|
||||||
|
|
||||||
|
@ -196,7 +181,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||||
try {
|
try {
|
||||||
mListener?.let {
|
mListener?.let {
|
||||||
if (it.isBinderAlive) {
|
if (it.isBinderAlive) {
|
||||||
Log.i(tag, "Dispatching event: binder alive")
|
|
||||||
val data = Parcel.obtain()
|
val data = Parcel.obtain()
|
||||||
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
||||||
it.transact(code, data, Parcel.obtain(), 0)
|
it.transact(code, data, Parcel.obtain(), 0)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package org.amnezia.vpn.qt;
|
package org.amnezia.vpn.qt;
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -37,6 +39,9 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
private val STORAGE_PERMISSION_CODE = 42
|
private val STORAGE_PERMISSION_CODE = 42
|
||||||
|
|
||||||
private val CAMERA_ACTION_CODE = 101
|
private val CAMERA_ACTION_CODE = 101
|
||||||
|
private val CREATE_FILE_ACTION_CODE = 102
|
||||||
|
|
||||||
|
private var tmpFileContentToSave: String = ""
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private lateinit var instance: VPNActivity
|
private lateinit var instance: VPNActivity
|
||||||
|
@ -56,6 +61,14 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
@JvmStatic fun sendToService(actionCode: Int, body: String) {
|
@JvmStatic fun sendToService(actionCode: Int, body: String) {
|
||||||
VPNActivity.getInstance().dispatchParcel(actionCode, body)
|
VPNActivity.getInstance().dispatchParcel(actionCode, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -76,6 +89,18 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
startActivityForResult(intent, CAMERA_ACTION_CODE)
|
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? {
|
override fun getSystemService(name: String): Any? {
|
||||||
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
|
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
|
||||||
// QT will always attempt to read the clipboard if content is there.
|
// QT will always attempt to read the clipboard if content is there.
|
||||||
|
@ -99,8 +124,6 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
if (!isBound) {
|
if (!isBound) {
|
||||||
Log.d(TAG, "dispatchParcel: not bound")
|
Log.d(TAG, "dispatchParcel: not bound")
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
Log.d(TAG, "dispatchParcel: bound")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val out: Parcel = Parcel.obtain()
|
val out: Parcel = Parcel.obtain()
|
||||||
|
@ -330,13 +353,38 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||||
val extra = data?.getStringExtra("result") ?: ""
|
val extra = data?.getStringExtra("result") ?: ""
|
||||||
onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra)
|
onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requestCode == CREATE_FILE_ACTION_CODE && resultCode == RESULT_OK) {
|
||||||
|
data?.data?.also { uri ->
|
||||||
|
alterDocument(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
private fun alterDocument(uri: Uri) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) {
|
try {
|
||||||
onBackPressed()
|
applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
|
||||||
return true
|
FileOutputStream(fd.fileDescriptor).use { fos ->
|
||||||
|
fos.write(tmpFileContentToSave.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
#include "migrations.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
|
@ -16,6 +17,9 @@
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
Migrations migrationsManager;
|
||||||
|
migrationsManager.doMigrations();
|
||||||
|
|
||||||
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
|
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
|
||||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||||
|
|
||||||
|
|
86
client/migrations.cpp
Normal file
86
client/migrations.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#include "migrations.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
24
client/migrations.h
Normal file
24
client/migrations.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef MIGRATIONS_H
|
||||||
|
#define MIGRATIONS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
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
|
|
@ -15,6 +15,7 @@
|
||||||
#include "private/qandroidextras_p.h"
|
#include "private/qandroidextras_p.h"
|
||||||
#include "ui/pages_logic/StartPageLogic.h"
|
#include "ui/pages_logic/StartPageLogic.h"
|
||||||
|
|
||||||
|
#include "androidvpnactivity.h"
|
||||||
#include "androidutils.h"
|
#include "androidutils.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -54,6 +55,10 @@ AndroidController::AndroidController() : QObject()
|
||||||
|
|
||||||
isConnected = doc.object()["connected"].toBool();
|
isConnected = doc.object()["connected"].toBool();
|
||||||
|
|
||||||
|
if (isConnected) {
|
||||||
|
emit scheduleStatusCheckSignal();
|
||||||
|
}
|
||||||
|
|
||||||
emit initialized(
|
emit initialized(
|
||||||
true, isConnected,
|
true, isConnected,
|
||||||
time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
|
time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
|
||||||
|
@ -66,9 +71,11 @@ AndroidController::AndroidController() : QObject()
|
||||||
Q_UNUSED(parcelBody);
|
Q_UNUSED(parcelBody);
|
||||||
qDebug() << "Transact: connected";
|
qDebug() << "Transact: connected";
|
||||||
|
|
||||||
isConnected = true;
|
if (!isConnected) {
|
||||||
|
emit scheduleStatusCheckSignal();
|
||||||
|
}
|
||||||
|
|
||||||
emit scheduleStatusCheckSignal();
|
isConnected = true;
|
||||||
|
|
||||||
emit connectionStateChanged(VpnProtocol::Connected);
|
emit connectionStateChanged(VpnProtocol::Connected);
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
@ -203,12 +210,7 @@ void AndroidController::setNotificationText(const QString& title,
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
|
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
|
||||||
QJsonObject rootObject;
|
AndroidVPNActivity::saveFileAs(configContent, suggestedName);
|
||||||
rootObject["data"] = configContent;
|
|
||||||
rootObject["suggestedName"] = suggestedName;
|
|
||||||
QJsonDocument doc(rootObject);
|
|
||||||
|
|
||||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SHARE_CONFIG, doc.toJson());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -266,6 +268,11 @@ void AndroidController::startQrReaderActivity()
|
||||||
AndroidVPNActivity::instance()->startQrCodeReader();
|
AndroidVPNActivity::instance()->startQrCodeReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidController::copyTextToClipboard(QString text)
|
||||||
|
{
|
||||||
|
AndroidVPNActivity::instance()->copyTextToClipboard(text);
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidController::scheduleStatusCheckSlot()
|
void AndroidController::scheduleStatusCheckSlot()
|
||||||
{
|
{
|
||||||
QTimer::singleShot(1000, [this]() {
|
QTimer::singleShot(1000, [this]() {
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "ui/pages_logic/StartPageLogic.h"
|
#include "ui/pages_logic/StartPageLogic.h"
|
||||||
|
|
||||||
#include "protocols/vpnprotocol.h"
|
#include "protocols/vpnprotocol.h"
|
||||||
#include "androidvpnactivity.h"
|
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
|
@ -44,6 +43,7 @@ public:
|
||||||
void setVpnConfig(const QJsonObject &newVpnConfig);
|
void setVpnConfig(const QJsonObject &newVpnConfig);
|
||||||
|
|
||||||
void startQrReaderActivity();
|
void startQrReaderActivity();
|
||||||
|
void copyTextToClipboard(QString text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
|
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
|
||||||
|
|
|
@ -57,6 +57,22 @@ void AndroidVPNActivity::startQrCodeReader()
|
||||||
QJniObject::callStaticMethod<void>(CLASSNAME, "startQrCodeReader", "()V");
|
QJniObject::callStaticMethod<void>(CLASSNAME, "startQrCodeReader", "()V");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilename) {
|
||||||
|
QJniObject::callStaticMethod<void>(
|
||||||
|
CLASSNAME,
|
||||||
|
"saveFileAs", "(Ljava/lang/String;Ljava/lang/String;)V",
|
||||||
|
QJniObject::fromString(fileContent).object<jstring>(),
|
||||||
|
QJniObject::fromString(suggestedFilename).object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidVPNActivity::copyTextToClipboard(QString text)
|
||||||
|
{
|
||||||
|
QJniObject::callStaticMethod<void>(
|
||||||
|
CLASSNAME,
|
||||||
|
"putTextToClipboard", "(Ljava/lang/String;)V",
|
||||||
|
QJniObject::fromString(text).object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
AndroidVPNActivity* AndroidVPNActivity::instance() {
|
AndroidVPNActivity* AndroidVPNActivity::instance() {
|
||||||
if (s_instance == nullptr) {
|
if (s_instance == nullptr) {
|
||||||
|
@ -70,9 +86,9 @@ AndroidVPNActivity* AndroidVPNActivity::instance() {
|
||||||
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
|
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
|
||||||
int messageType = (int)type;
|
int messageType = (int)type;
|
||||||
|
|
||||||
QJniEnvironment env;
|
|
||||||
QJniObject::callStaticMethod<void>(
|
QJniObject::callStaticMethod<void>(
|
||||||
CLASSNAME, "sendToService", "(ILjava/lang/String;)V",
|
CLASSNAME,
|
||||||
|
"sendToService", "(ILjava/lang/String;)V",
|
||||||
static_cast<int>(messageType),
|
static_cast<int>(messageType),
|
||||||
QJniObject::fromString(data).object<jstring>());
|
QJniObject::fromString(data).object<jstring>());
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
static void sendToService(ServiceAction type, const QString& data);
|
static void sendToService(ServiceAction type, const QString& data);
|
||||||
static void connectService();
|
static void connectService();
|
||||||
static void startQrCodeReader();
|
static void startQrCodeReader();
|
||||||
|
static void saveFileAs(QString fileContent, QString suggestedFilename);
|
||||||
|
static void copyTextToClipboard(QString text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void serviceConnected();
|
void serviceConnected();
|
||||||
|
|
|
@ -61,8 +61,12 @@ Window {
|
||||||
|
|
||||||
function close_page() {
|
function close_page() {
|
||||||
if (pageLoader.depth <= 1) {
|
if (pageLoader.depth <= 1) {
|
||||||
|
if (GC.isMobile()) {
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pageLoader.currentItem.deactivated()
|
pageLoader.currentItem.deactivated()
|
||||||
pageLoader.pop()
|
pageLoader.pop()
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ void UiLogic::initalizeUiLogic()
|
||||||
connect(AndroidController::instance(), &AndroidController::initialized, [this](bool status, bool connected, const QDateTime& connectionDate) {
|
connect(AndroidController::instance(), &AndroidController::initialized, [this](bool status, bool connected, const QDateTime& connectionDate) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
pageLogic<VpnLogic>()->onConnectionStateChanged(VpnProtocol::Connected);
|
pageLogic<VpnLogic>()->onConnectionStateChanged(VpnProtocol::Connected);
|
||||||
|
m_vpnConnection->restoreConnection();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!AndroidController::instance()->initialize(pageLogic<StartPageLogic>())) {
|
if (!AndroidController::instance()->initialize(pageLogic<StartPageLogic>())) {
|
||||||
|
@ -224,9 +225,10 @@ void UiLogic::keyPressEvent(Qt::Key key)
|
||||||
m_configurator->sshConfigurator->openSshTerminal(m_settings->serverCredentials(m_settings->defaultServerIndex()));
|
m_configurator->sshConfigurator->openSshTerminal(m_settings->serverCredentials(m_settings->defaultServerIndex()));
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Escape:
|
case Qt::Key_Escape:
|
||||||
case Qt::Key_Back:
|
|
||||||
if (currentPage() == Page::Vpn) break;
|
if (currentPage() == Page::Vpn) break;
|
||||||
if (currentPage() == Page::ServerConfiguringProgress) break;
|
if (currentPage() == Page::ServerConfiguringProgress) break;
|
||||||
|
case Qt::Key_Back:
|
||||||
|
|
||||||
// if (currentPage() == Page::Start && pagesStack.size() < 2) break;
|
// if (currentPage() == Page::Start && pagesStack.size() < 2) break;
|
||||||
// if (currentPage() == Page::Sites &&
|
// if (currentPage() == Page::Sites &&
|
||||||
// ui->tableView_sites->selectionModel()->selection().indexes().size() > 0) {
|
// ui->tableView_sites->selectionModel()->selection().indexes().size() > 0) {
|
||||||
|
@ -243,10 +245,16 @@ void UiLogic::keyPressEvent(Qt::Key key)
|
||||||
|
|
||||||
void UiLogic::onCloseWindow()
|
void UiLogic::onCloseWindow()
|
||||||
{
|
{
|
||||||
if (m_settings->serversCount() == 0) qApp->quit();
|
#ifdef Q_OS_ANDROID
|
||||||
else {
|
qApp->quit();
|
||||||
hide();
|
#else
|
||||||
|
if (m_settings->serversCount() == 0)
|
||||||
|
{
|
||||||
|
qApp->quit();
|
||||||
|
} else {
|
||||||
|
emit hide();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UiLogic::containerName(int container)
|
QString UiLogic::containerName(int container)
|
||||||
|
@ -466,7 +474,11 @@ void UiLogic::saveBinaryFile(const QString &desc, QString ext, const QString &da
|
||||||
|
|
||||||
void UiLogic::copyToClipboard(const QString &text)
|
void UiLogic::copyToClipboard(const QString &text)
|
||||||
{
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
AndroidController::instance()->copyTextToClipboard(text);
|
||||||
|
#else
|
||||||
qApp->clipboard()->setText(text);
|
qApp->clipboard()->setText(text);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QString& data) {
|
void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QString& data) {
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "../../platforms/android/android_controller.h"
|
#include "../../platforms/android/android_controller.h"
|
||||||
#include "protocols/android_vpnprotocol.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
|
@ -353,10 +352,8 @@ void VpnConnection::connectToVpn(int serverIndex,
|
||||||
}
|
}
|
||||||
m_vpnProtocol->prepare();
|
m_vpnProtocol->prepare();
|
||||||
#elif defined Q_OS_ANDROID
|
#elif defined Q_OS_ANDROID
|
||||||
Proto proto = ContainerProps::defaultProtocol(container);
|
androidVpnProtocol = createDefaultAndroidVpnProtocol(container);
|
||||||
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
|
createAndroidConnections(container);
|
||||||
connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
|
|
||||||
connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated);
|
|
||||||
|
|
||||||
m_vpnProtocol.reset(androidVpnProtocol);
|
m_vpnProtocol.reset(androidVpnProtocol);
|
||||||
#elif defined Q_OS_IOS
|
#elif defined Q_OS_IOS
|
||||||
|
@ -373,9 +370,7 @@ void VpnConnection::connectToVpn(int serverIndex,
|
||||||
m_vpnProtocol.reset(iosVpnProtocol);
|
m_vpnProtocol.reset(iosVpnProtocol);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
createProtocolConnections();
|
||||||
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)));
|
|
||||||
|
|
||||||
m_serverController->disconnectFromHost(credentials);
|
m_serverController->disconnectFromHost(credentials);
|
||||||
|
|
||||||
|
@ -383,6 +378,46 @@ void VpnConnection::connectToVpn(int serverIndex,
|
||||||
if (e) emit VpnProtocol::Error;
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidVpnProtocol* VpnConnection::createDefaultAndroidVpnProtocol(DockerContainer container)
|
||||||
|
{
|
||||||
|
Proto proto = ContainerProps::defaultProtocol(container);
|
||||||
|
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
|
||||||
|
|
||||||
|
return androidVpnProtocol;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QString VpnConnection::bytesPerSecToText(quint64 bytes)
|
QString VpnConnection::bytesPerSecToText(quint64 bytes)
|
||||||
{
|
{
|
||||||
double mbps = bytes * 8 / 1e6;
|
double mbps = bytes * 8 / 1e6;
|
||||||
|
@ -401,8 +436,6 @@ void VpnConnection::disconnectFromVpn()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!m_vpnProtocol.data()) {
|
if (!m_vpnProtocol.data()) {
|
||||||
emit connectionStateChanged(VpnProtocol::Disconnected);
|
emit connectionStateChanged(VpnProtocol::Disconnected);
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
@ -415,11 +448,8 @@ void VpnConnection::disconnectFromVpn()
|
||||||
|
|
||||||
VpnProtocol::VpnConnectionState VpnConnection::connectionState()
|
VpnProtocol::VpnConnectionState VpnConnection::connectionState()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (!m_vpnProtocol) return VpnProtocol::Disconnected;
|
if (!m_vpnProtocol) return VpnProtocol::Disconnected;
|
||||||
return m_vpnProtocol->connectionState();
|
return m_vpnProtocol->connectionState();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VpnConnection::isConnected() const
|
bool VpnConnection::isConnected() const
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include "protocols/android_vpnprotocol.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class VpnConfigurator;
|
class VpnConfigurator;
|
||||||
class ServerController;
|
class ServerController;
|
||||||
|
|
||||||
|
@ -61,6 +65,10 @@ public:
|
||||||
const QString &remoteAddress() const;
|
const QString &remoteAddress() const;
|
||||||
void addSitesRoutes(const QString &gw, Settings::RouteMode mode);
|
void addSitesRoutes(const QString &gw, Settings::RouteMode mode);
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
void restoreConnection();
|
||||||
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void connectToVpn(int serverIndex,
|
void connectToVpn(int serverIndex,
|
||||||
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig);
|
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig);
|
||||||
|
@ -101,6 +109,15 @@ private:
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
IOSVpnProtocol * iosVpnProtocol{nullptr};
|
IOSVpnProtocol * iosVpnProtocol{nullptr};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
AndroidVpnProtocol* androidVpnProtocol = nullptr;
|
||||||
|
|
||||||
|
AndroidVpnProtocol* createDefaultAndroidVpnProtocol(DockerContainer container);
|
||||||
|
void createAndroidConnections();
|
||||||
|
void createAndroidConnections(DockerContainer container);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void createProtocolConnections();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPNCONNECTION_H
|
#endif // VPNCONNECTION_H
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue