diff --git a/.gitignore b/.gitignore index bc8311d..80f7c85 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ debug/ *.sln *.suo *.vcxproj* +.gradle +.idea +.build diff --git a/examples/QZXingLive/QZXingLive.pro b/examples/QZXingLive/QZXingLive.pro index 5ca26b5..3827c9a 100644 --- a/examples/QZXingLive/QZXingLive.pro +++ b/examples/QZXingLive/QZXingLive.pro @@ -4,7 +4,18 @@ QT += qml quick CONFIG += c++11 qzxing_multimedia -SOURCES += main.cpp +CONFIG(debug, debug|release) { + CONFIG+=qml_debug +} else { + DEFINES += QT_NO_DEBUG + DEFINES += QT_NO_DEBUG_OUTPUT +} + +HEADERS += \ + application.h + +SOURCES += main.cpp \ + application.cpp RESOURCES += qml.qrc @@ -16,20 +27,30 @@ include(../../src/QZXing.pri) # Default rules for deployment. include(deployment.pri) -DISTFILES += \ - android/AndroidManifest.xml \ - android/gradle/wrapper/gradle-wrapper.jar \ - android/gradlew \ - android/res/values/libs.xml \ - android/build.gradle \ - android/gradle/wrapper/gradle-wrapper.properties \ - android/gradlew.bat \ - android/AndroidManifest.xml \ - android/gradle/wrapper/gradle-wrapper.jar \ - android/gradlew \ - android/res/values/libs.xml \ - android/build.gradle \ - android/gradle/wrapper/gradle-wrapper.properties \ - android/gradlew.bat +android { + QT += androidextras -ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + HEADERS += \ + native.h + + SOURCES += \ + native.cpp + + DISTFILES += \ + android/AndroidManifest.xml \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradlew \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew.bat \ + android/AndroidManifest.xml \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradlew \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew.bat + + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +} diff --git a/examples/QZXingLive/android/AndroidManifest.xml b/examples/QZXingLive/android/AndroidManifest.xml index f84045d..215e583 100644 --- a/examples/QZXingLive/android/AndroidManifest.xml +++ b/examples/QZXingLive/android/AndroidManifest.xml @@ -1,7 +1,7 @@ - + diff --git a/examples/QZXingLive/android/build.gradle b/examples/QZXingLive/android/build.gradle index ef416b0..1596d33 100644 --- a/examples/QZXingLive/android/build.gradle +++ b/examples/QZXingLive/android/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:1.1.0' + classpath 'com.android.tools.build:gradle:2.2.2' } } @@ -18,6 +18,8 @@ apply plugin: 'com.android.application' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:25.0.1' + compile 'com.android.support:design:25.0.1' } android { diff --git a/examples/QZXingLive/android/gradle/wrapper/gradle-wrapper.properties b/examples/QZXingLive/android/gradle/wrapper/gradle-wrapper.properties index 0c71e76..4aeb7f7 100644 --- a/examples/QZXingLive/android/gradle/wrapper/gradle-wrapper.properties +++ b/examples/QZXingLive/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Sun Jan 29 12:19:44 EET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/examples/QZXingLive/android/src/org/ftylitak/qzxing/NativeFunctions.java b/examples/QZXingLive/android/src/org/ftylitak/qzxing/NativeFunctions.java new file mode 100644 index 0000000..487fc65 --- /dev/null +++ b/examples/QZXingLive/android/src/org/ftylitak/qzxing/NativeFunctions.java @@ -0,0 +1,6 @@ +package org.ftylitak.qzxing; + +public class NativeFunctions { + public static native void onPermissionsGranted(); + public static native void onPermissionsDenied(); +} diff --git a/examples/QZXingLive/android/src/org/ftylitak/qzxing/QZXingLiveActivity.java b/examples/QZXingLive/android/src/org/ftylitak/qzxing/QZXingLiveActivity.java new file mode 100644 index 0000000..37ccb6c --- /dev/null +++ b/examples/QZXingLive/android/src/org/ftylitak/qzxing/QZXingLiveActivity.java @@ -0,0 +1,25 @@ +package org.ftylitak.qzxing; + +import android.Manifest; +import android.content.pm.PackageManager; +import org.qtproject.qt5.android.bindings.QtActivity; +import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA; + +public class QZXingLiveActivity extends QtActivity { + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case REQUEST_CAMERA: { + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + NativeFunctions.onPermissionsGranted(); + } else { + NativeFunctions.onPermissionsDenied(); + } + return; + } + } + } +} diff --git a/examples/QZXingLive/android/src/org/ftylitak/qzxing/Utilities.java b/examples/QZXingLive/android/src/org/ftylitak/qzxing/Utilities.java new file mode 100644 index 0000000..70f655a --- /dev/null +++ b/examples/QZXingLive/android/src/org/ftylitak/qzxing/Utilities.java @@ -0,0 +1,42 @@ +package org.ftylitak.qzxing; + +import android.Manifest; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +import java.util.ArrayList; + +public class Utilities { + + public static final int REQUEST_CAMERA = 0; + + public static final String[] requiredPermissionsModifyPhoneState = { + Manifest.permission.CAMERA, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + public static void checkAndRequestPermissionList(Activity activity, String[] permissions) { + ArrayList permissionsToRequest = new ArrayList<>(); + for (int i = 0; i < permissions.length; i++) { + if (ContextCompat.checkSelfPermission(activity, permissions[i]) + != PackageManager.PERMISSION_GRANTED) + permissionsToRequest.add(permissions[i]); + } + + if (permissionsToRequest.size() != 0) + ActivityCompat.requestPermissions(activity, + permissionsToRequest.toArray(new String[0]), + REQUEST_CAMERA); + else + NativeFunctions.onPermissionsGranted(); + } + + public static void requestQZXingPermissions(Activity activity) { + checkAndRequestPermissionList(activity, requiredPermissionsModifyPhoneState); + } + + +} \ No newline at end of file diff --git a/examples/QZXingLive/application.cpp b/examples/QZXingLive/application.cpp new file mode 100644 index 0000000..dc1d085 --- /dev/null +++ b/examples/QZXingLive/application.cpp @@ -0,0 +1,44 @@ +#include "application.h" +#include +#include "native.h" + +#if defined(Q_OS_ANDROID) + #include + #include + +#endif // Q_OS_ANDROID + +Application::Application() +{ + //both signals will be connected to the same function for + //simplicity + + connect(this, &Application::onPermissionsGranted, + this, &Application::initializeQML); + + connect(this, &Application::onPermissionsDenied, + this, &Application::initializeQML); + + NativeHelpers::registerApplicationInstance(this); +} + +void Application::initializeQML() +{ + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); +} + +void Application::checkPermissions() +{ +#if defined(Q_OS_ANDROID) + //intentionally called in the C++ thread since it is blocking and will continue after the check + qDebug() << "About to request permissions"; + + QAndroidJniObject::callStaticMethod("org/ftylitak/qzxing/Utilities", + "requestQZXingPermissions", + "(Landroid/app/Activity;)V", + QtAndroid::androidActivity().object()); + qDebug() << "Permissions granted"; +#else + emit onPermissionsGranted(); +#endif //Q_OS_ANDROID +} diff --git a/examples/QZXingLive/application.h b/examples/QZXingLive/application.h new file mode 100644 index 0000000..adcb1f4 --- /dev/null +++ b/examples/QZXingLive/application.h @@ -0,0 +1,27 @@ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include +#include + +class Application : public QObject +{ + Q_OBJECT + +public: + Application(); + + void checkPermissions(); + +public slots: + void initializeQML(); + +signals: + void onPermissionsGranted(); + void onPermissionsDenied(); + +private: + QQmlApplicationEngine engine; +}; + +#endif // APPLICATION_H diff --git a/examples/QZXingLive/main.cpp b/examples/QZXingLive/main.cpp index 30b4167..4d501f8 100644 --- a/examples/QZXingLive/main.cpp +++ b/examples/QZXingLive/main.cpp @@ -6,15 +6,17 @@ #include #include +#include "application.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication app(argc, argv); - QQmlApplicationEngine engine; QZXing::registerQMLTypes(); - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + Application customApp; + customApp.checkPermissions(); + return app.exec(); } diff --git a/examples/QZXingLive/native.cpp b/examples/QZXingLive/native.cpp new file mode 100644 index 0000000..7e456ea --- /dev/null +++ b/examples/QZXingLive/native.cpp @@ -0,0 +1,47 @@ +#include +#include "native.h" +#include + +QObject *NativeHelpers::application_p_ = 0; + +// define our native static functions +// these are the functions that Java part will call directly from Android UI thread +static void onPermissionsGranted(JNIEnv * /*env*/, jobject /*obj*/) +{ + QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsGranted" + , Qt::QueuedConnection); +} + +static void onPermissionsDenied(JNIEnv * /*env*/, jobject /*obj*/) +{ + QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsDenied" + , Qt::QueuedConnection); +} + +//create a vector with all our JNINativeMethod(s) +static JNINativeMethod methods[] = { + {"onPermissionsGranted", "()V", (void *)onPermissionsGranted}, + {"onPermissionsDenied", "()V", (void *)onPermissionsDenied}, +}; + +// this method is called automatically by Java after the .so file is loaded +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) +{ + JNIEnv* env; + // get the JNIEnv pointer. + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) + return JNI_ERR; + + // search for Java class which declares the native methods + jclass javaClass = env->FindClass("org/ftylitak/qzxing/NativeFunctions"); + if (!javaClass) + return JNI_ERR; + + // register our native methods + if (env->RegisterNatives(javaClass, methods, + sizeof(methods) / sizeof(methods[0])) < 0) { + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} diff --git a/examples/QZXingLive/native.h b/examples/QZXingLive/native.h new file mode 100644 index 0000000..db187a7 --- /dev/null +++ b/examples/QZXingLive/native.h @@ -0,0 +1,20 @@ +#ifndef NATIVE_H +#define NATIVE_H + +#include + +class NativeHelpers { +public: + static void registerApplicationInstance(QObject *app_p) { + application_p_ = app_p; + } + + static QObject* getApplicationInstance() { + return application_p_; + } + +private: + static QObject *application_p_; +}; + +#endif // NATIVE_H