CameraActivity refactoring

This commit is contained in:
albexk 2023-11-21 22:48:52 +03:00
parent 679bd4e4c9
commit e625543b94
13 changed files with 193 additions and 224 deletions

View file

@ -63,6 +63,9 @@
<activity <activity
android:name=".CameraActivity" android:name=".CameraActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false" /> android:exported="false" />
<activity <activity

View file

@ -75,6 +75,10 @@ android {
isUniversalApk = false isUniversalApk = false
} }
} }
lint {
disable += "InvalidFragmentVersionForActivityResult"
}
} }
dependencies { dependencies {
@ -82,11 +86,9 @@ dependencies {
implementation(project(":qt")) implementation(project(":qt"))
implementation(project(":utils")) implementation(project(":utils"))
implementation(libs.androidx.core) implementation(libs.androidx.core)
implementation(libs.androidx.appcompat) implementation(libs.androidx.activity)
implementation(libs.androidx.security.crypto) implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines) implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera) implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit) implementation(libs.google.mlkit)
// todo: remove after finish refactoring
implementation(libs.androidx.constraintlayout)
} }

View file

@ -2,15 +2,15 @@
agp = "8.1.3" agp = "8.1.3"
kotlin = "1.9.20" kotlin = "1.9.20"
androidx-core = "1.12.0" androidx-core = "1.12.0"
androidx-appcompat = "1.6.1" androidx-activity = "1.8.1"
androidx-camera = "1.2.3" androidx-camera = "1.3.0"
androidx-security-crypto = "1.1.0-alpha06" androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3" kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0" google-mlkit = "17.2.0"
[libraries] [libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" } androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" } androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" } androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
@ -18,8 +18,6 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" } androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" } google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
# todo: remove after finish refactoring
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
[bundles] [bundles]
androidx-camera = [ androidx-camera = [

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View file

@ -9,18 +9,12 @@ import org.qtproject.qt.android.bindings.QtActivity
private const val TAG = "AmneziaActivity" private const val TAG = "AmneziaActivity"
private const val CAMERA_ACTION_CODE = 101
private const val CREATE_FILE_ACTION_CODE = 102 private const val CREATE_FILE_ACTION_CODE = 102
class AmneziaActivity : QtActivity() { class AmneziaActivity : QtActivity() {
private var tmpFileContentToSave: String = "" private var tmpFileContentToSave: String = ""
private fun startQrCodeActivity() {
val intent = Intent(this, CameraActivity::class.java)
startActivityForResult(intent, CAMERA_ACTION_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == CREATE_FILE_ACTION_CODE && resultCode == RESULT_OK) { if (requestCode == CREATE_FILE_ACTION_CODE && resultCode == RESULT_OK) {
data?.data?.also { uri -> data?.data?.also { uri ->
@ -97,6 +91,8 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused") @Suppress("unused")
fun startQrCodeReader() { fun startQrCodeReader() {
Log.v(TAG, "Start camera") Log.v(TAG, "Start camera")
startQrCodeActivity() Intent(this, CameraActivity::class.java).also {
startActivity(it)
}
} }
} }

View file

@ -1,22 +1,16 @@
package org.amnezia.vpn package org.amnezia.vpn
import android.content.res.Configuration import androidx.camera.camera2.Camera2Config
// import org.amnezia.vpn.shadowsocks.core.Core import androidx.camera.core.CameraSelector
// import org.amnezia.vpn.shadowsocks.core.VpnManager import androidx.camera.core.CameraXConfig
import org.qtproject.qt.android.bindings.QtActivity
import org.qtproject.qt.android.bindings.QtApplication import org.qtproject.qt.android.bindings.QtApplication
import android.app.Application
class AmneziaApplication: org.qtproject.qt.android.bindings.QtApplication() { class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
override fun onCreate() { override fun getCameraXConfig(): CameraXConfig = CameraXConfig.Builder
super.onCreate() .fromConfig(Camera2Config.defaultConfig())
/* Core.init(this, QtActivity::class) .setMinimumLoggingLevel(android.util.Log.ERROR)
VpnManager.getInstance().init(this) */ .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
} .build()
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Core.updateNotificationChannels()
}
} }

View file

@ -4,167 +4,151 @@ import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent import android.view.MotionEvent.ACTION_UP
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.activity.ComponentActivity
import androidx.camera.core.* import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.FocusMeteringAction.FLAG_AE
import androidx.camera.core.FocusMeteringAction.FLAG_AF
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScannerOptions.Builder
import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.ZoomSuggestionOptions
import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.common.InputImage
import org.amnezia.vpn.R import org.amnezia.vpn.databinding.CameraPreviewBinding
import java.util.concurrent.ExecutorService import org.amnezia.vpn.qt.QtAndroidController
import java.util.concurrent.Executors
private const val TAG = "CameraActivity"
class CameraActivity : AppCompatActivity() { class CameraActivity : ComponentActivity() {
private val CAMERA_REQUEST = 100 private lateinit var viewBinding: CameraPreviewBinding
private lateinit var cameraProvider: ProcessCameraProvider
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)
@ExperimentalGetImage
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera) viewBinding = CameraPreviewBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
viewFinder = findViewById(R.id.viewFinder) checkPermissions(onSuccess = ::startCamera, onFail = ::finish)
cameraExecutor = Executors.newSingleThreadExecutor()
analyzerExecutor = Executors.newSingleThreadExecutor()
instance = this
checkPermissions()
configureVideoPreview()
} }
private fun checkPermissions() { private fun checkPermissions(onSuccess: () -> Unit, onFail: () -> Unit) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST) onSuccess()
} else {
val requestPermissionLauncher =
registerForActivityResult(RequestPermission()) { isGranted ->
if (isGranted) {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show()
onSuccess()
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show()
onFail()
}
}
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
} }
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { @ExperimentalGetImage
super.onRequestPermissionsResult(requestCode, permissions, grantResults) private fun startCamera() {
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", "ClickableViewAccessibility")
private fun configureVideoPreview() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this) val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
val imageCapture = ImageCapture.Builder().build()
cameraProviderFuture.addListener({ cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() cameraProvider = cameraProviderFuture.get()
bindPreview()
val preview = Preview.Builder().build() bindImageAnalysis()
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()
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase)
viewFinder.setOnTouchListener(View.OnTouchListener { view: View, motionEvent: MotionEvent ->
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> return@OnTouchListener true
MotionEvent.ACTION_UP -> {
val factory = viewFinder.meteringPointFactory
val point = factory.createPoint(motionEvent.x, motionEvent.y)
val action = FocusMeteringAction.Builder(point).build()
camera.cameraControl.startFocusAndMetering(action)
return@OnTouchListener true
}
else -> return@OnTouchListener false
}
})
} catch(exc: Exception) {
Log.e("WUTT", "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this)) }, ContextCompat.getMainExecutor(this))
} }
override fun onDestroy() { @SuppressLint("ClickableViewAccessibility")
cameraExecutor.shutdown() private fun bindPreview() {
analyzerExecutor.shutdown() val viewFinder = viewBinding.viewFinder
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
super.onDestroy() val camera = cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview)
}
val barcodesSet = mutableSetOf<String>() viewFinder.setOnTouchListener { _, motionEvent ->
when (motionEvent.action) {
ACTION_DOWN -> true
ACTION_UP -> {
val point = viewFinder
.meteringPointFactory.createPoint(motionEvent.x, motionEvent.x)
private inner class BarCodeAnalyzer(): ImageAnalysis.Analyzer { val action = FocusMeteringAction
.Builder(point, FLAG_AF or FLAG_AE).build()
private val options = BarcodeScannerOptions.Builder() camera.cameraControl.startFocusAndMetering(action)
.setBarcodeFormats(Barcode.FORMAT_QR_CODE) true
.build() }
private val scanner = BarcodeScanning.getClient(options) else -> false
@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()
}
} }
} }
} }
}
@ExperimentalGetImage
private fun bindImageAnalysis() {
val imageAnalysis = ImageAnalysis.Builder().build()
val camera = cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, imageAnalysis)
val barcodeScanner = BarcodeScanning.getClient(
Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.setZoomSuggestionOptions(
ZoomSuggestionOptions.Builder { zoomLevel ->
camera.cameraControl.setZoomRatio(zoomLevel)
true
}.apply {
camera.cameraInfo.zoomState.value?.maxZoomRatio?.let { maxZoomRation ->
setMaxSupportedZoomRatio(maxZoomRation)
}
}.build()
).build()
)
// optimization
val checkedBarcodes = hashSetOf<String>()
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this)) { imageProxy ->
imageProxy.image?.let { InputImage.fromMediaImage(it, imageProxy.imageInfo.rotationDegrees) }
?.let { image ->
barcodeScanner.process(image).addOnSuccessListener { barcodes ->
barcodes.firstOrNull()?.let { barcode ->
barcode.displayValue?.let { code ->
if (code.isNotEmpty() && code !in checkedBarcodes) {
if (QtAndroidController.decodeQrCode(code)) {
barcodeScanner.close()
stopCamera()
}
checkedBarcodes.add(code)
}
}
}
}.addOnFailureListener {
Log.e(TAG, "Processing QR-code image failed: ${it.message}")
}.addOnCompleteListener {
imageProxy.close()
}
}
}
}
private fun stopCamera() {
cameraProvider.unbindAll()
finish()
}
}

View file

@ -15,4 +15,6 @@ object QtAndroidController {
external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long) external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long)
external fun onConfigImported() external fun onConfigImported()
external fun decodeQrCode(data: String): Boolean
} }

View file

@ -3,6 +3,7 @@
#include <QJsonDocument> #include <QJsonDocument>
#include "android_controller.h" #include "android_controller.h"
#include "ui/controllers/importController.h"
namespace namespace
{ {
@ -101,6 +102,7 @@ bool AndroidController::initialize()
{"onVpnDisconnected", "()V", reinterpret_cast<void *>(onVpnDisconnected)}, {"onVpnDisconnected", "()V", reinterpret_cast<void *>(onVpnDisconnected)},
{"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)}, {"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)},
{"onConfigImported", "()V", reinterpret_cast<void *>(onConfigImported)}, {"onConfigImported", "()V", reinterpret_cast<void *>(onConfigImported)},
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
}; };
QJniEnvironment env; QJniEnvironment env;
@ -240,6 +242,7 @@ void AndroidController::onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBy
emit AndroidController::instance()->statisticsUpdated((quint64) rxBytes, (quint64) txBytes); emit AndroidController::instance()->statisticsUpdated((quint64) rxBytes, (quint64) txBytes);
} }
// static
void AndroidController::onConfigImported(JNIEnv *env, jobject thiz) void AndroidController::onConfigImported(JNIEnv *env, jobject thiz)
{ {
Q_UNUSED(env); Q_UNUSED(env);
@ -247,3 +250,18 @@ void AndroidController::onConfigImported(JNIEnv *env, jobject thiz)
emit AndroidController::instance()->configImported(); emit AndroidController::instance()->configImported();
} }
// static
bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
{
Q_UNUSED(thiz);
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return false;
}
QString code(buffer);
env->ReleaseStringUTFChars(data, buffer);
return ImportController::decodeQrCode(code);
}

View file

@ -50,6 +50,7 @@ private:
static void onVpnDisconnected(JNIEnv *env, jobject thiz); static void onVpnDisconnected(JNIEnv *env, jobject thiz);
static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes);
static void onConfigImported(JNIEnv *env, jobject thiz); static void onConfigImported(JNIEnv *env, jobject thiz);
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
template <typename Ret, typename ...Args> template <typename Ret, typename ...Args>
static auto callActivityMethod(const char *methodName, const char *signature, static auto callActivityMethod(const char *methodName, const char *signature,

View file

@ -7,9 +7,7 @@
#include "core/errorstrings.h" #include "core/errorstrings.h"
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
#include "../../platforms/android/android_controller.h" #include "platforms/android/android_controller.h"
#include "../../platforms/android/androidutils.h"
#include <QJniObject>
#endif #endif
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
@ -48,10 +46,6 @@ namespace
#if defined Q_OS_ANDROID #if defined Q_OS_ANDROID
ImportController *mInstance = nullptr; ImportController *mInstance = nullptr;
#endif #endif
#ifdef Q_OS_ANDROID
constexpr auto AndroidCameraActivity = "org.amnezia.vpn.CameraActivity";
#endif
} // namespace } // namespace
ImportController::ImportController(const QSharedPointer<ServersModel> &serversModel, ImportController::ImportController(const QSharedPointer<ServersModel> &serversModel,
@ -61,18 +55,6 @@ ImportController::ImportController(const QSharedPointer<ServersModel> &serversMo
{ {
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
mInstance = this; mInstance = this;
AndroidUtils::runOnAndroidThreadAsync([]() {
JNINativeMethod methods[] {
{ "passDataToDecoder", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onNewQrCodeDataChunk) },
};
QJniObject javaClass(AndroidCameraActivity);
QJniEnvironment env;
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
env->DeleteLocalRef(objectClass);
});
#endif #endif
} }
@ -320,26 +302,20 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
} }
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
void ImportController::onNewQrCodeDataChunk(JNIEnv *env, jobject thiz, jstring data) static QMutex qrDecodeMutex;
// static
bool ImportController::decodeQrCode(const QString &code)
{ {
Q_UNUSED(thiz); QMutexLocker lock(&qrDecodeMutex);
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return;
}
QString parcelBody(buffer); if (!mInstance->m_isQrCodeProcessed) {
env->ReleaseStringUTFChars(data, buffer); mInstance->m_qrCodeChunks.clear();
mInstance->m_isQrCodeProcessed = true;
if (mInstance != nullptr) { mInstance->m_totalQrCodeChunksCount = 0;
if (!mInstance->m_isQrCodeProcessed) { mInstance->m_receivedQrCodeChunksCount = 0;
mInstance->m_qrCodeChunks.clear();
mInstance->m_isQrCodeProcessed = true;
mInstance->m_totalQrCodeChunksCount = 0;
mInstance->m_receivedQrCodeChunksCount = 0;
}
mInstance->parseQrCodeChunk(parcelBody);
} }
return mInstance->parseQrCodeChunk(code);
} }
#endif #endif
@ -360,17 +336,14 @@ void ImportController::startDecodingQr()
void ImportController::stopDecodingQr() void ImportController::stopDecodingQr()
{ {
#if defined Q_OS_ANDROID
QJniObject::callStaticMethod<void>(AndroidCameraActivity, "stopQrCodeReader", "()V");
#endif
emit qrDecodingFinished(); emit qrDecodingFinished();
} }
void ImportController::parseQrCodeChunk(const QString &code) bool ImportController::parseQrCodeChunk(const QString &code)
{ {
// qDebug() << code; // qDebug() << code;
if (!m_isQrCodeProcessed) if (!m_isQrCodeProcessed)
return; return false;
// check if chunk received // check if chunk received
QByteArray ba = QByteArray::fromBase64(code.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray ba = QByteArray::fromBase64(code.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
@ -404,6 +377,7 @@ void ImportController::parseQrCodeChunk(const QString &code)
m_isQrCodeProcessed = false; m_isQrCodeProcessed = false;
qDebug() << "stopDecodingQr"; qDebug() << "stopDecodingQr";
stopDecodingQr(); stopDecodingQr();
return true;
} else { } else {
qDebug() << "error while extracting data from qr"; qDebug() << "error while extracting data from qr";
m_qrCodeChunks.clear(); m_qrCodeChunks.clear();
@ -417,8 +391,10 @@ void ImportController::parseQrCodeChunk(const QString &code)
m_isQrCodeProcessed = false; m_isQrCodeProcessed = false;
qDebug() << "stopDecodingQr"; qDebug() << "stopDecodingQr";
stopDecodingQr(); stopDecodingQr();
return true;
} }
} }
return false;
} }
double ImportController::getQrCodeScanProgressBarValue() double ImportController::getQrCodeScanProgressBarValue()

View file

@ -7,9 +7,6 @@
#include "core/defs.h" #include "core/defs.h"
#include "ui/models/containers_model.h" #include "ui/models/containers_model.h"
#include "ui/models/servers_model.h" #include "ui/models/servers_model.h"
#ifdef Q_OS_ANDROID
#include "jni.h"
#endif
class ImportController : public QObject class ImportController : public QObject
{ {
@ -30,12 +27,16 @@ public slots:
#if defined Q_OS_ANDROID || defined Q_OS_IOS #if defined Q_OS_ANDROID || defined Q_OS_IOS
void startDecodingQr(); void startDecodingQr();
void parseQrCodeChunk(const QString &code); bool parseQrCodeChunk(const QString &code);
double getQrCodeScanProgressBarValue(); double getQrCodeScanProgressBarValue();
QString getQrCodeScanProgressString(); QString getQrCodeScanProgressString();
#endif #endif
#if defined Q_OS_ANDROID
static bool decodeQrCode(const QString &code);
#endif
signals: signals:
void importFinished(); void importFinished();
void importErrorOccurred(const QString &errorMessage); void importErrorOccurred(const QString &errorMessage);
@ -50,9 +51,6 @@ private:
#if defined Q_OS_ANDROID || defined Q_OS_IOS #if defined Q_OS_ANDROID || defined Q_OS_IOS
void stopDecodingQr(); void stopDecodingQr();
#endif #endif
#if defined Q_OS_ANDROID
static void onNewQrCodeDataChunk(JNIEnv *env, jobject thiz, jstring data);
#endif
QSharedPointer<ServersModel> m_serversModel; QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel; QSharedPointer<ContainersModel> m_containersModel;