Commit f643cc46 authored by Martin Straka's avatar Martin Straka

Android file provider prototyp

parent 0946d392
......@@ -13,6 +13,13 @@
<data android:host="*" />
<data android:pathPattern=".*\\.zfo" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="*/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
......@@ -77,7 +84,7 @@
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:authorities="cz.nic.mobiledatovka.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
......
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<file-path name="external_files" path="."/>
<files-path name="my_shared_files" path="share_example_x_files/" />
</paths>
\ No newline at end of file
/*
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#include <QAndroidJniObject>
#include <QtAndroidExtras>
#include <QDesktopServices>
#include <QUrl>
#include "android/src/android_io.h"
AndroidIO::AndroidIO(void)
{
}
bool AndroidIO::openFile(const QString &filePath)
{
/* TOTO: ADD test: if (SDK >= 24) { use FileProvide } else { use QDesktopServices } */
#if 0
return openWithQDesktopServices(filePath);
#else
return openWithFileProvider(filePath);
#endif
}
bool AndroidIO::openWithQDesktopServices(const QString &filePath)
{
return QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}
bool AndroidIO::openWithFileProvider(const QString &filePath)
{
QAndroidJniObject jsPath = QAndroidJniObject::fromString(filePath);
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>(
"cz/nic/mobiledatovka/java/QFileProvider",
"viewFile", "(Ljava/lang/String;I)Z",
jsPath.object<jstring>(), 0);
return (ok);
}
/*
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#pragma once
#include <QtAndroid>
#include <QAndroidActivityResultReceiver>
#include <QObject>
/*!
* @brief Wraps Android intent retrieval.
*
*/
class AndroidIO : public QAndroidActivityResultReceiver {
public:
/*!
* @brief Constructor.
*
* @param[in] parent Parent object.
*/
explicit AndroidIO(void);
/*!
* @brief Obtain intent arguments.
*
* @return List of files to be opened.
*/
static
bool openFile(const QString &filePath);
private:
static
bool openWithQDesktopServices(const QString &filePath);
static
bool openWithFileProvider(const QString &filePath);
};
/*
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
package cz.nic.mobiledatovka.java;
import org.qtproject.qt5.android.QtNative;
import java.lang.String;
import android.content.Intent;
import java.io.File;
import android.net.Uri;
import android.util.Log;
import android.content.ContentResolver;
import android.database.Cursor;
import android.provider.MediaStore;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.util.List;
import android.content.pm.ResolveInfo;
import java.util.ArrayList;
import android.content.pm.PackageManager;
import java.util.Comparator;
import java.util.Collections;
import android.content.Context;
import android.os.Parcelable;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.support.v4.app.ShareCompat;
public class QFileProvider
{
// Authority defined in AndroidManifest.xml
private static String AUTHORITY="cz.nic.mobiledatovka.fileprovider";
protected QFileProvider() {}
public static boolean startActivity(Intent theIntent, Uri uri) {
final Context context = QtNative.activity();
final PackageManager packageManager = context.getPackageManager();
final boolean isLowerOrEqualsKitKat = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
ResolveInfo defaultAppInfo = packageManager.resolveActivity(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (defaultAppInfo == null) {
return false;
}
List<ResolveInfo> appInfoList = packageManager.queryIntentActivities(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (appInfoList.isEmpty()) {
return false;
}
Collections.sort(appInfoList, new Comparator<ResolveInfo>() {
@Override
public int compare(ResolveInfo first, ResolveInfo second) {
String firstName = first.loadLabel(packageManager).toString();
String secondName = second.loadLabel(packageManager).toString();
return firstName.compareToIgnoreCase(secondName);
}
});
List<Intent> targetedIntents = new ArrayList<Intent>();
for (ResolveInfo appInfo : appInfoList) {
String targetPackageName = appInfo.activityInfo.packageName;
if (targetPackageName.equals(context.getPackageName())) {
continue;
}
Intent targetedIntent = new Intent(theIntent);
targetedIntent.setPackage(targetPackageName);
targetedIntents.add(targetedIntent);
if (isLowerOrEqualsKitKat) {
context.grantUriPermission(targetPackageName,
uri, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
if (targetedIntents.isEmpty()) {
return false;
}
Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() - 1), "View File");
if (targetedIntents.isEmpty()) {
} else {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[] {}));
}
if (chooserIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
QtNative.activity().startActivity(chooserIntent);
return true;
}
return false;
}
public static boolean viewFile(String filePath, int id) {
if (QtNative.activity() == null) {
return false;
}
// Intent viewIntent = new Intent();
Intent viewIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
viewIntent.setAction(Intent.ACTION_VIEW);
File imageFileToShare = new File(filePath);
Uri uri;
try {
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
} catch (IllegalArgumentException e) {
Log.d("Error viewFile - cannot be shared: ", filePath);
return false;
}
Log.d("File path: ", filePath);
Log.d("File content URI: ", uri.toString());
String mimeType = QtNative.activity().getContentResolver().getType(uri);
viewIntent.setDataAndType(uri, mimeType);
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
viewIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return startActivity(viewIntent, uri);
}
}
......@@ -88,6 +88,7 @@ TRANSLATIONS_FILES += \
res/locale/datovka_en.qm
SOURCES += \
android/src/android_io.cpp \
src/accounts.cpp \
src/auxiliaries/email_helper.cpp \
src/datovka_shared/gov_services/helper.cpp \
......@@ -209,6 +210,7 @@ SOURCES += \
src/zfo.cpp
HEADERS += \
android/src/android_io.h \
src/accounts.h \
src/auxiliaries/email_helper.h \
src/common.h \
......@@ -372,6 +374,8 @@ android {
}
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
include(android/android.pri)
OTHER_FILES += android/src/cz/nic/mobiledatovka/java/QFileProvider.java
}
# winphone|winrt target.
......
......@@ -22,10 +22,14 @@
*/
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
#include <QQmlEngine>
#include <QStringBuilder>
#if defined (Q_OS_ANDROID)
#include "android/src/android_io.h"
#endif
#include "ios/src/url_opener.h"
#include "src/auxiliaries/email_helper.h"
#include "src/common.h"
......@@ -204,7 +208,28 @@ void Files::openAttachment(const QString &fileName,
return;
}
QString filePath(writeFile(appTmpDirPath(), fileName, binaryData));
QString docLocationRoot = appTmpDirPath();
#if defined (Q_OS_ANDROID)
docLocationRoot = QStandardPaths::standardLocations(
QStandardPaths::AppDataLocation).value(0);
QString documentsWorkPath = docLocationRoot.append("/share_example_x_files");
if (!QDir(documentsWorkPath).exists()) {
if (!QDir("").mkpath(documentsWorkPath)) {
logErrorNL("Failed to create share directory: '%s'.",
documentsWorkPath.toUtf8().constData());
Dialogues::errorMessage(Dialogues::CRITICAL,
tr("Open attachment error"),
tr("Cannot save selected file to disk for opening."),
QString());
return;
}
}
#endif
QString filePath(writeFile(docLocationRoot, fileName, binaryData));
if (!filePath.isEmpty()) {
logInfoNL("Creating temporary file '%s'.",
......@@ -240,12 +265,18 @@ void Files::openAttachmentFromPath(const QString &filePath)
UrlOpener urlOpener;
urlOpener.openFile(filePath);
#else
#elif defined (Q_OS_ANDROID)
QUrl contentUri = QUrl::fromLocalFile(filePath);
contentUri.setScheme("content");
if (!AndroidIO::openFile(filePath)) {
Dialogues::errorMessage(Dialogues::CRITICAL,
tr("Open attachment error"),
tr("There is no application to open this file format."),
tr("File: '%1'").arg(filePath));
}
#else
if (!QDesktopServices::openUrl(contentUri)) {
if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
Dialogues::errorMessage(Dialogues::CRITICAL,
tr("Open attachment error"),
tr("There is no application to open this file format."),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment