Added example with proper handling of Android application permissions for QZXingLive triggered by #22

This commit is contained in:
favoritas37 2017-01-29 17:54:16 +02:00
parent 5ef72fa114
commit 29860c538c
13 changed files with 262 additions and 23 deletions

3
.gitignore vendored
View File

@ -22,3 +22,6 @@ debug/
*.sln
*.suo
*.vcxproj*
.gradle
.idea
.build

View File

@ -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
}

View File

@ -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"/>

View File

@ -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 {

View File

@ -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

View File

@ -0,0 +1,6 @@
package org.ftylitak.qzxing;
public class NativeFunctions {
public static native void onPermissionsGranted();
public static native void onPermissionsDenied();
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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