diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml
index 09a8775b..22c828fb 100644
--- a/client/android/AndroidManifest.xml
+++ b/client/android/AndroidManifest.xml
@@ -34,6 +34,7 @@
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false"
+ android:theme="@style/Theme.AppCompat.NoActionBar"
android:icon="@drawable/icon">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/android/settings.gradle b/client/android/settings.gradle
index ba31e000..9256d846 100644
--- a/client/android/settings.gradle
+++ b/client/android/settings.gradle
@@ -1 +1,19 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ jcenter()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter()
+ }
+}
+
include ':shadowsocks'
diff --git a/client/android/shadowsocks/build.gradle b/client/android/shadowsocks/build.gradle
index d35daa11..d87139fe 100644
--- a/client/android/shadowsocks/build.gradle
+++ b/client/android/shadowsocks/build.gradle
@@ -1,21 +1,9 @@
-
-allprojects {
- repositories {
- google()
- jcenter()
- mavenCentral()
- }
-}
-
-
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
//apply plugin: 'com.novoda.bintray-release'
-
-
android {
compileSdkVersion 30
defaultConfig {
diff --git a/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt
new file mode 100644
index 00000000..8416d97c
--- /dev/null
+++ b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt
@@ -0,0 +1,158 @@
+package org.amnezia.vpn.qt
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.core.*
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
+import androidx.core.content.ContextCompat
+import com.google.mlkit.vision.barcode.BarcodeScannerOptions
+import com.google.mlkit.vision.barcode.BarcodeScanning
+import com.google.mlkit.vision.barcode.common.Barcode
+import com.google.mlkit.vision.common.InputImage
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import org.amnezia.vpn.R
+
+
+class CameraActivity : AppCompatActivity() {
+
+ private val CAMERA_REQUEST = 100
+
+ private lateinit var cameraExecutor: ExecutorService
+ private lateinit var analyzerExecutor: ExecutorService
+
+ private lateinit var viewFinder: PreviewView
+
+ companion object {
+ private lateinit var instance: CameraActivity
+
+ @JvmStatic fun getInstance(): CameraActivity {
+ return instance
+ }
+
+ @JvmStatic fun stopQrCodeReader() {
+ CameraActivity.getInstance().finish()
+ }
+ }
+
+ external fun passDataToDecoder(data: String)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_camera)
+
+ viewFinder = findViewById(R.id.viewFinder)
+
+ cameraExecutor = Executors.newSingleThreadExecutor()
+ analyzerExecutor = Executors.newSingleThreadExecutor()
+
+ instance = this
+
+ checkPermissions()
+
+ configureVideoPreview()
+ }
+
+ private fun checkPermissions() {
+ if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST)
+ }
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (requestCode == CAMERA_REQUEST) {
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Toast.makeText(this, "CameraX permission granted", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "CameraX permission denied", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ @SuppressLint("UnsafeOptInUsageError")
+ private fun configureVideoPreview() {
+ val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
+ val imageCapture = ImageCapture.Builder().build()
+
+ cameraProviderFuture.addListener({
+ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
+
+ val preview = Preview.Builder().build()
+
+ val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+
+ val imageAnalyzer = BarCodeAnalyzer()
+
+ val analysisUseCase = ImageAnalysis.Builder()
+ .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+ .build()
+
+ analysisUseCase.setAnalyzer(analyzerExecutor, imageAnalyzer)
+
+ try {
+ preview.setSurfaceProvider(viewFinder.surfaceProvider)
+ cameraProvider.unbindAll()
+ cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase)
+ } catch(exc: Exception) {
+ Log.e("WUTT", "Use case binding failed", exc)
+ }
+ }, ContextCompat.getMainExecutor(this))
+ }
+
+ override fun onDestroy() {
+ cameraExecutor.shutdown()
+ analyzerExecutor.shutdown()
+
+ super.onDestroy()
+ }
+
+ val barcodesSet = mutableSetOf()
+
+ private inner class BarCodeAnalyzer(): ImageAnalysis.Analyzer {
+
+ private val options = BarcodeScannerOptions.Builder()
+ .setBarcodeFormats(Barcode.FORMAT_QR_CODE)
+ .build()
+
+ private val scanner = BarcodeScanning.getClient(options)
+
+ @SuppressLint("UnsafeOptInUsageError")
+ override fun analyze(imageProxy: ImageProxy) {
+ val mediaImage = imageProxy.image
+
+ if (mediaImage != null) {
+ val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
+
+ scanner.process(image)
+ .addOnSuccessListener { barcodes ->
+ if (barcodes.isNotEmpty()) {
+ val barcode = barcodes[0]
+ if (barcode != null) {
+ val str = barcode?.displayValue ?: ""
+ if (str.isNotEmpty()) {
+ val isAdded = barcodesSet.add(str)
+ if (isAdded) {
+ passDataToDecoder(str)
+ }
+ }
+ }
+ }
+
+ imageProxy.close()
+ }
+ .addOnFailureListener {
+ imageProxy.close()
+ }
+ }
+ }
+ }
+}
\ 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 673c4b59..b5e8d5fb 100644
--- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt
+++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt
@@ -36,6 +36,8 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
private val TAG = "VPNActivity"
private val STORAGE_PERMISSION_CODE = 42
+ private val CAMERA_ACTION_CODE = 101
+
companion object {
private lateinit var instance: VPNActivity
@@ -47,6 +49,10 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
VPNActivity.getInstance().initServiceConnection()
}
+ @JvmStatic fun startQrCodeReader() {
+ VPNActivity.getInstance().startQrCodeActivity()
+ }
+
@JvmStatic fun sendToService(actionCode: Int, body: String) {
VPNActivity.getInstance().dispatchParcel(actionCode, body)
}
@@ -62,7 +68,12 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
super.onCreate(savedInstanceState)
- instance = this;
+ instance = this
+ }
+
+ private fun startQrCodeActivity() {
+ val intent = Intent(this, CameraActivity::class.java)
+ startActivityForResult(intent, CAMERA_ACTION_CODE)
}
override fun getSystemService(name: String): Any? {
@@ -82,6 +93,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
external fun onServiceMessage(actionCode: Int, body: String?)
external fun qtOnServiceConnected()
external fun qtOnServiceDisconnected()
+ external fun onActivityMessage(actionCode: Int, body: String?)
private fun dispatchParcel(actionCode: Int, body: String) {
if (!isBound) {
@@ -286,6 +298,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
private val EVENT_PERMISSION_REQURED = 6
private val EVENT_DISCONNECTED = 2
+ private val UI_EVENT_QR_CODE_RECEIVED = 0
fun onPermissionRequest(code: Int, data: Parcel?) {
if (code != EVENT_PERMISSION_REQURED) {
@@ -310,7 +323,13 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
}
return
}
+
super.onActivityResult(requestCode, resultCode, data)
+
+ if (requestCode == CAMERA_ACTION_CODE && resultCode == RESULT_OK) {
+ val extra = data?.getStringExtra("result") ?: ""
+ onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra)
+ }
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp
index b25fce78..a86ab7ca 100644
--- a/client/platforms/android/android_controller.cpp
+++ b/client/platforms/android/android_controller.cpp
@@ -15,7 +15,6 @@
#include "private/qandroidextras_p.h"
#include "ui/pages_logic/StartPageLogic.h"
-#include "androidvpnactivity.h"
#include "androidutils.h"
namespace {
@@ -262,6 +261,11 @@ void AndroidController::setVpnConfig(const QJsonObject &newVpnConfig)
m_vpnConfig = newVpnConfig;
}
+void AndroidController::startQrReaderActivity()
+{
+ AndroidVPNActivity::instance()->startQrCodeReader();
+}
+
void AndroidController::scheduleStatusCheckSlot()
{
QTimer::singleShot(1000, [this]() {
diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h
index ddaa0d79..59ecd9b3 100644
--- a/client/platforms/android/android_controller.h
+++ b/client/platforms/android/android_controller.h
@@ -11,6 +11,7 @@
#include "ui/pages_logic/StartPageLogic.h"
#include "protocols/vpnprotocol.h"
+#include "androidvpnactivity.h"
using namespace amnezia;
@@ -42,6 +43,8 @@ public:
const QJsonObject &vpnConfig() const;
void setVpnConfig(const QJsonObject &newVpnConfig);
+ void startQrReaderActivity();
+
signals:
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
@@ -50,8 +53,7 @@ signals:
// to true and the "connectionDate" should be set to the activation date if
// known.
// If "status" is set to false, the backend service is considered unavailable.
- void initialized(bool status, bool connected,
- const QDateTime& connectionDate);
+ void initialized(bool status, bool connected, const QDateTime& connectionDate);
void statusUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4);
void scheduleStatusCheckSignal();
@@ -59,9 +61,6 @@ signals:
protected slots:
void scheduleStatusCheckSlot();
-protected:
-
-
private:
bool m_init = false;
diff --git a/client/platforms/android/androidvpnactivity.cpp b/client/platforms/android/androidvpnactivity.cpp
index 0e8b9574..17639023 100644
--- a/client/platforms/android/androidvpnactivity.cpp
+++ b/client/platforms/android/androidvpnactivity.cpp
@@ -22,12 +22,10 @@ AndroidVPNActivity::AndroidVPNActivity() {
AndroidUtils::runOnAndroidThreadAsync([]() {
JNINativeMethod methods[]{
{"handleBackButton", "()Z", reinterpret_cast(handleBackButton)},
- {"onServiceMessage", "(ILjava/lang/String;)V",
- reinterpret_cast(onServiceMessage)},
- {"qtOnServiceConnected", "()V",
- reinterpret_cast(onServiceConnected)},
- {"qtOnServiceDisconnected", "()V",
- reinterpret_cast(onServiceDisconnected)},
+ {"onServiceMessage", "(ILjava/lang/String;)V", reinterpret_cast(onServiceMessage)},
+ {"qtOnServiceConnected", "()V", reinterpret_cast(onServiceConnected)},
+ {"qtOnServiceDisconnected", "()V", reinterpret_cast(onServiceDisconnected)},
+ {"onActivityMessage", "(ILjava/lang/String;)V", reinterpret_cast(onAndroidVpnActivityMessage)}
};
QJniObject javaClass(CLASSNAME);
@@ -54,6 +52,11 @@ void AndroidVPNActivity::connectService() {
QJniObject::callStaticMethod(CLASSNAME, "connectService", "()V");
}
+void AndroidVPNActivity::startQrCodeReader()
+{
+ QJniObject::callStaticMethod(CLASSNAME, "startQrCodeReader", "()V");
+}
+
// static
AndroidVPNActivity* AndroidVPNActivity::instance() {
if (s_instance == nullptr) {
@@ -121,6 +124,19 @@ void AndroidVPNActivity::handleServiceMessage(int code, const QString& data) {
}
}
+void AndroidVPNActivity::handleActivityMessage(int code, const QString &data)
+{
+ auto mode = (UIEvents)code;
+
+ switch (mode) {
+ case UIEvents::QR_CODED_DECODED:
+ emit eventQrCodeReceived(data);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+}
+
void AndroidVPNActivity::onServiceConnected(JNIEnv* env, jobject thiz) {
Q_UNUSED(env);
Q_UNUSED(thiz);
@@ -134,3 +150,19 @@ void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) {
emit AndroidVPNActivity::instance()->serviceDisconnected();
}
+
+void AndroidVPNActivity::onAndroidVpnActivityMessage(JNIEnv *env, jobject thiz, jint messageType, jstring message)
+{
+ Q_UNUSED(thiz);
+ const char* buffer = env->GetStringUTFChars(message, nullptr);
+ if (!buffer) {
+ return;
+ }
+
+ QString parcelBody(buffer);
+ env->ReleaseStringUTFChars(message, buffer);
+
+ AndroidUtils::dispatchToMainThread([messageType, parcelBody] {
+ AndroidVPNActivity::instance()->handleActivityMessage(messageType, parcelBody);
+ });
+}
diff --git a/client/platforms/android/androidvpnactivity.h b/client/platforms/android/androidvpnactivity.h
index 49d1aae5..1bc1a522 100644
--- a/client/platforms/android/androidvpnactivity.h
+++ b/client/platforms/android/androidvpnactivity.h
@@ -59,6 +59,11 @@ enum ServiceEvents {
};
typedef enum ServiceEvents ServiceEvents;
+enum UIEvents {
+ QR_CODED_DECODED = 0,
+};
+typedef enum UIEvents UIEvents;
+
class AndroidVPNActivity : public QObject
{
Q_OBJECT
@@ -69,6 +74,7 @@ public:
static bool handleBackButton(JNIEnv* env, jobject thiz);
static void sendToService(ServiceAction type, const QString& data);
static void connectService();
+ static void startQrCodeReader();
signals:
void serviceConnected();
@@ -80,6 +86,7 @@ signals:
void eventBackendLogs(const QString& data);
void eventActivationError(const QString& data);
void eventConfigImport(const QString& data);
+ void eventQrCodeReceived(const QString& data);
private:
AndroidVPNActivity();
@@ -87,7 +94,9 @@ private:
static void onServiceMessage(JNIEnv* env, jobject thiz, jint messageType, jstring body);
static void onServiceConnected(JNIEnv* env, jobject thiz);
static void onServiceDisconnected(JNIEnv* env, jobject thiz);
+ static void onAndroidVpnActivityMessage(JNIEnv* env, jobject thiz, jint messageType, jstring message);
void handleServiceMessage(int code, const QString& data);
+ void handleActivityMessage(int code, const QString& data);
};
#endif // ANDROIDVPNACTIVITY_H
diff --git a/client/resources.qrc b/client/resources.qrc
index 4651f91e..fd26f5ad 100644
--- a/client/resources.qrc
+++ b/client/resources.qrc
@@ -81,7 +81,6 @@
ui/qml/Pages/PageSites.qml
ui/qml/Pages/PageStart.qml
ui/qml/Pages/PageVPN.qml
- ui/qml/Pages/PageQrDecoder.qml
ui/qml/Pages/PageAbout.qml
ui/qml/Pages/PageQrDecoderIos.qml
ui/qml/Pages/PageViewConfig.qml
diff --git a/client/ui/pages_logic/QrDecoderLogic.cpp b/client/ui/pages_logic/QrDecoderLogic.cpp
index 764a6e4b..e1845c77 100644
--- a/client/ui/pages_logic/QrDecoderLogic.cpp
+++ b/client/ui/pages_logic/QrDecoderLogic.cpp
@@ -3,15 +3,70 @@
#include "ui/uilogic.h"
#include "ui/pages_logic/StartPageLogic.h"
+#ifdef Q_OS_ANDROID
+#include
+#include
+#include "../../platforms/android/androidutils.h"
+#endif
+
using namespace amnezia;
using namespace PageEnumNS;
+namespace {
+ QrDecoderLogic* mInstance = nullptr;
+ constexpr auto CLASSNAME = "org.amnezia.vpn.qt.CameraActivity";
+}
+
QrDecoderLogic::QrDecoderLogic(UiLogic *logic, QObject *parent):
PageLogicBase(logic, parent)
{
+ mInstance = this;
+ #if (defined(Q_OS_ANDROID))
+ AndroidUtils::runOnAndroidThreadAsync([]() {
+ JNINativeMethod methods[]{
+ {"passDataToDecoder", "(Ljava/lang/String;)V", reinterpret_cast(onNewDataChunk)},
+ };
+
+ QJniObject javaClass(CLASSNAME);
+ QJniEnvironment env;
+ jclass objectClass = env->GetObjectClass(javaClass.object());
+ env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
+ env->DeleteLocalRef(objectClass);
+ });
+ #endif
}
+void QrDecoderLogic::stopDecodingQr()
+{
+ #if (defined(Q_OS_ANDROID))
+ QJniObject::callStaticMethod(CLASSNAME, "stopQrCodeReader", "()V");
+ #endif
+
+ emit stopDecode();
+}
+
+#ifdef Q_OS_ANDROID
+void QrDecoderLogic::onNewDataChunk(JNIEnv *env, jobject thiz, jstring data)
+{
+ Q_UNUSED(thiz);
+ const char* buffer = env->GetStringUTFChars(data, nullptr);
+ if (!buffer) {
+ return;
+ }
+
+ QString parcelBody(buffer);
+ env->ReleaseStringUTFChars(data, buffer);
+
+ if (mInstance != nullptr) {
+ if (!mInstance->m_detectingEnabled) {
+ mInstance->onUpdatePage();
+ }
+ mInstance->onDetectedQrCode(parcelBody);
+ }
+}
+#endif
+
void QrDecoderLogic::onUpdatePage()
{
m_chunks.clear();
@@ -24,7 +79,6 @@ void QrDecoderLogic::onUpdatePage()
void QrDecoderLogic::onDetectedQrCode(const QString &code)
{
//qDebug() << code;
-
if (!detectingEnabled()) return;
// check if chunk received
@@ -32,12 +86,12 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
QDataStream s(&ba, QIODevice::ReadOnly);
qint16 magic; s >> magic;
-
if (magic == amnezia::qrMagicCode) {
quint8 chunksCount; s >> chunksCount;
if (totalChunksCount() != chunksCount) {
m_chunks.clear();
}
+
set_totalChunksCount(chunksCount);
quint8 chunkId; s >> chunkId;
@@ -46,6 +100,7 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
if (m_chunks.size() == totalChunksCount()) {
QByteArray data;
+
for (int i = 0; i < totalChunksCount(); ++i) {
data.append(m_chunks.value(i));
}
@@ -53,21 +108,18 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
bool ok = uiLogic()->pageLogic()->importConnectionFromQr(data);
if (ok) {
set_detectingEnabled(false);
- emit stopDecode();
- }
- else {
+ stopDecodingQr();
+ } else {
m_chunks.clear();
set_totalChunksCount(0);
set_receivedChunksCount(0);
}
}
- }
- else {
+ } else {
bool ok = uiLogic()->pageLogic()->importConnectionFromQr(ba);
if (ok) {
set_detectingEnabled(false);
- emit stopDecode();
+ stopDecodingQr();
}
}
}
-
diff --git a/client/ui/pages_logic/QrDecoderLogic.h b/client/ui/pages_logic/QrDecoderLogic.h
index 243da95b..2b24bf27 100644
--- a/client/ui/pages_logic/QrDecoderLogic.h
+++ b/client/ui/pages_logic/QrDecoderLogic.h
@@ -3,6 +3,10 @@
#include "PageLogicBase.h"
+#ifdef Q_OS_ANDROID
+#include "jni.h"
+#endif
+
class UiLogic;
class QrDecoderLogic : public PageLogicBase
@@ -16,10 +20,17 @@ public:
Q_INVOKABLE void onUpdatePage() override;
Q_INVOKABLE void onDetectedQrCode(const QString &code);
+#ifdef Q_OS_ANDROID
+ static void onNewDataChunk(JNIEnv *env, jobject thiz, jstring data);
+#endif
+
public:
explicit QrDecoderLogic(UiLogic *uiLogic, QObject *parent = nullptr);
~QrDecoderLogic() = default;
+private:
+ void stopDecodingQr();
+
signals:
void startDecode();
void stopDecode();
diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp
index 3c15199d..06febc05 100644
--- a/client/ui/pages_logic/StartPageLogic.cpp
+++ b/client/ui/pages_logic/StartPageLogic.cpp
@@ -13,6 +13,7 @@
#ifdef Q_OS_ANDROID
#include
#include "../../platforms/android/androidutils.h"
+#include "../../platforms/android/android_controller.h"
#endif
namespace {
@@ -184,6 +185,13 @@ void StartPageLogic::onPushButtonImportOpenFile()
}
}
+#ifdef Q_OS_ANDROID
+void StartPageLogic::startQrDecoder()
+{
+ AndroidController::instance()->startQrReaderActivity();
+}
+#endif
+
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 183d0bd3..b3dea002 100644
--- a/client/ui/pages_logic/StartPageLogic.h
+++ b/client/ui/pages_logic/StartPageLogic.h
@@ -31,6 +31,10 @@ public:
Q_INVOKABLE void onPushButtonImport();
Q_INVOKABLE void onPushButtonImportOpenFile();
+#ifdef Q_OS_ANDROID
+ Q_INVOKABLE void startQrDecoder();
+#endif
+
bool importConnection(const QJsonObject &profile);
bool importConnectionFromCode(QString code);
bool importConnectionFromQr(const QByteArray &data);
diff --git a/client/ui/qml/Pages/PageQrDecoder.qml b/client/ui/qml/Pages/PageQrDecoder.qml
deleted file mode 100644
index 9a4b26f5..00000000
--- a/client/ui/qml/Pages/PageQrDecoder.qml
+++ /dev/null
@@ -1,164 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtMultimedia
-import PageEnum 1.0
-import QZXing 3.2
-
-import "./"
-import "../Controls"
-import "../Config"
-
-PageBase {
- id: root
- page: PageEnum.QrDecoder
- logic: QrDecoderLogic
-
- onDeactivated: {
- console.debug("Stopping QR decoder")
- loader.sourceComponent = undefined
- }
-
- BackButton {
- }
- Caption {
- id: caption
- text: qsTr("Import configuration")
- }
-
- Connections {
- target: Qt.platform.os != "ios" ? QrDecoderLogic : null
- function onStartDecode() {
- console.debug("Starting QR decoder")
- loader.sourceComponent = component
- }
- function onStopDecode() {
- console.debug("Stopping QR decoder")
- loader.sourceComponent = undefined
- }
- }
-
- Loader {
- id: loader
-
- anchors.top: caption.bottom
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- }
-
- Component {
- id: component
-
- Item {
- anchors.fill: parent
-
- Camera
- {
- id:camera
- focus {
- focusMode: CameraFocus.FocusContinuous
- focusPointMode: CameraFocus.FocusPointAuto
- }
- }
-
- VideoOutput
- {
- id: videoOutput
- source: camera
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- autoOrientation: true
- fillMode: VideoOutput.PreserveAspectFit
-// filters: [ zxingFilter ]
-
-
- Rectangle {
- color: "black"
- opacity: 0.5
- width: videoOutput.contentRect.width * 0.15
- height: videoOutput.contentRect.height
- x: (videoOutput.width - videoOutput.contentRect.width)/2
- anchors.verticalCenter: videoOutput.verticalCenter
- }
-
- Rectangle {
- color: "black"
- opacity: 0.5
- width: videoOutput.contentRect.width * 0.15
- height: videoOutput.contentRect.height
- x: videoOutput.width/2 + videoOutput.contentRect.width/2 - videoOutput.contentRect.width * 0.15
- anchors.verticalCenter: videoOutput.verticalCenter
- }
-
- Rectangle {
- color: "black"
- opacity: 0.5
- width: videoOutput.contentRect.width * 0.7
- height: videoOutput.contentRect.height * 0.15
- x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15
- y: (videoOutput.height - videoOutput.contentRect.height)/2
- }
-
- Rectangle {
- color: "black"
- opacity: 0.5
- width: videoOutput.contentRect.width * 0.7
- height: videoOutput.contentRect.height * 0.15
- x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15
- y: videoOutput.height/2 + videoOutput.contentRect.height/2 - videoOutput.contentRect.height * 0.15
- }
-
- LabelType {
- width: parent.width
- text: qsTr("Decoded QR chunks " + QrDecoderLogic.receivedChunksCount + "/" + QrDecoderLogic.totalChunksCount)
- horizontalAlignment: Text.AlignLeft
- visible: QrDecoderLogic.totalChunksCount > 0
- anchors.horizontalCenter: videoOutput.horizontalCenter
- y: videoOutput.height/2 + videoOutput.contentRect.height/2
- }
- }
-
- QZXingFilter
- {
- id: zxingFilter
- orientation: videoOutput.orientation
- captureRect: {
- // setup bindings
- videoOutput.contentRect;
- videoOutput.sourceRect;
- return videoOutput.mapRectToSource(videoOutput.mapNormalizedRectToItem(Qt.rect(
- 0.15, 0.15, 0.7, 0.7 //0, 0, 1.0, 1.0
- )));
- }
-
- decoder {
- enabledDecoders: QZXing.DecoderFormat_QR_CODE
-
- onTagFound: {
- QrDecoderLogic.onDetectedQrCode(tag)
- }
-
- tryHarder: true
- }
-
- property int framesDecoded: 0
- property real timePerFrameDecode: 0
-
- onDecodingFinished:
- {
- timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1);
- framesDecoded++;
- if(succeeded)
- console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded);
- }
- }
-
-
- }
-
- }
-
-
-}
diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml
index d5e67e48..eacb607d 100644
--- a/client/ui/qml/Pages/PageStart.qml
+++ b/client/ui/qml/Pages/PageStart.qml
@@ -154,7 +154,7 @@ PageBase {
if (Qt.platform.os === "ios") {
UiLogic.goToPage(PageEnum.QrDecoderIos)
} else {
- UiLogic.goToPage(PageEnum.QrDecoder)
+ StartPageLogic.startQrDecoder()
}
}
enabled: StartPageLogic.pushButtonConnectEnabled
diff --git a/deploy/build_android.sh b/deploy/build_android.sh
index 4357034f..d9d8f5c7 100644
--- a/deploy/build_android.sh
+++ b/deploy/build_android.sh
@@ -51,7 +51,7 @@ $QT_HOST_PATH/bin/androiddeployqt \
--gradle \
--release \
--input android-AmneziaVPN-deployment-settings.json \
- --android-platform android-31
+ --android-platform android-33
echo "............Copy apk.................."
cp $OUT_APP_DIR/android-build/build/outputs/apk/release/android-build-release-unsigned.apk \