Skip to content

Commit

Permalink
Added example with proper handling of Android application permissions…
Browse files Browse the repository at this point in the history
… for QZXingLive triggered by #22
  • Loading branch information
ftylitak committed Jan 29, 2017
1 parent 5ef72fa commit 29860c5
Show file tree
Hide file tree
Showing 13 changed files with 263 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ debug/
*.sln
*.suo
*.vcxproj*
.gradle
.idea
.build
57 changes: 39 additions & 18 deletions examples/QZXingLive/QZXingLive.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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_PACKAGE_SOURCE_DIR = $$PWD/android
android {
QT += androidextras

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
}
2 changes: 1 addition & 1 deletion examples/QZXingLive/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="QZXingLive" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="QZXingLive" android:screenOrientation="unspecified" android:launchMode="singleTop">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.ftylitak.qzxing.QZXingLiveActivity" android:label="QZXingLive" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
Expand Down
4 changes: 3 additions & 1 deletion examples/QZXingLive/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'
classpath 'com.android.tools.build:gradle:2.2.2'
}
}

Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.ftylitak.qzxing;

public class NativeFunctions {
public static native void onPermissionsGranted();
public static native void onPermissionsDenied();
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
42 changes: 42 additions & 0 deletions examples/QZXingLive/android/src/org/ftylitak/qzxing/Utilities.java
Original file line number Diff line number Diff line change
@@ -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<String> 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);
}


}
44 changes: 44 additions & 0 deletions examples/QZXingLive/application.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "application.h"
#include <QDebug>
#include "native.h"

#if defined(Q_OS_ANDROID)
#include <QAndroidJniObject>
#include <QtAndroid>

#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<void>("org/ftylitak/qzxing/Utilities",
"requestQZXingPermissions",
"(Landroid/app/Activity;)V",
QtAndroid::androidActivity().object());
qDebug() << "Permissions granted";
#else
emit onPermissionsGranted();
#endif //Q_OS_ANDROID
}
27 changes: 27 additions & 0 deletions examples/QZXingLive/application.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef APPLICATION_H
#define APPLICATION_H

#include <QQmlApplicationEngine>
#include <QObject>

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
6 changes: 4 additions & 2 deletions examples/QZXingLive/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@

#include <Qt>
#include <QZXing.h>
#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();
}
47 changes: 47 additions & 0 deletions examples/QZXingLive/native.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <jni.h>
#include "native.h"
#include <QMetaObject>

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<void**>(&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;
}
20 changes: 20 additions & 0 deletions examples/QZXingLive/native.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef NATIVE_H
#define NATIVE_H

#include <QObject>

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

0 comments on commit 29860c5

Please sign in to comment.