diff --git a/src/app/AndroidManifest.xml b/src/app/AndroidManifest.xml index b2d0bbd..4f9261f 100644 --- a/src/app/AndroidManifest.xml +++ b/src/app/AndroidManifest.xml @@ -1,107 +1,117 @@ + + + + diff --git a/src/app/applicationcontroller.cpp b/src/app/applicationcontroller.cpp index 0d9b12a..ed46dca 100644 --- a/src/app/applicationcontroller.cpp +++ b/src/app/applicationcontroller.cpp @@ -1,496 +1,510 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library 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 . */ #include "applicationcontroller.h" #include "documentmanager.h" #include "logging.h" #include "pkpassmanager.h" #include "reservationmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_ANDROID +#include #include #include #include +#include +#include #include #include #else #include #endif #include using namespace KItinerary; #ifdef Q_OS_ANDROID #define PERMISSION_CALENDAR QStringLiteral("android.permission.READ_CALENDAR") static void importReservation(JNIEnv *env, jobject that, jstring data) { Q_UNUSED(that); const char *str = env->GetStringUTFChars(data, nullptr); ApplicationController::instance()->importData(str); env->ReleaseStringUTFChars(data, str); } static void importDavDroidJson(JNIEnv *env, jobject that, jstring data) { Q_UNUSED(that); const char *str = env->GetStringUTFChars(data, nullptr); const auto doc = QJsonDocument::fromJson(str); env->ReleaseStringUTFChars(data, str); const auto array = doc.array(); if (array.size() < 2 || array.at(0).toString() != QLatin1String("X-KDE-KITINERARY-RESERVATION")) { return; } ApplicationController::instance()->importData(array.at(1).toString().toUtf8()); } static void importFromIntent(JNIEnv *env, jobject that, jobject data) { Q_UNUSED(that) Q_UNUSED(env) KAndroidExtras::Intent intent(data); ApplicationController::instance()->importFromUrl(intent.getData()); } static const JNINativeMethod methods[] = { {"importReservation", "(Ljava/lang/String;)V", (void*)importReservation}, {"importFromIntent", "(Landroid/content/Intent;)V", (void*)importFromIntent}, {"importDavDroidJson", "(Ljava/lang/String;)V", (void*)importDavDroidJson} }; Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void*) { static bool initialized = false; if (initialized) return JNI_VERSION_1_6; initialized = true; JNIEnv *env = nullptr; if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { qCWarning(Log) << "Failed to get JNI environment."; return -1; } jclass cls = env->FindClass("org/kde/itinerary/Activity"); if (env->RegisterNatives(cls, methods, sizeof(methods) / sizeof(JNINativeMethod)) < 0) { qCWarning(Log) << "Failed to register native functions."; return -1; } return JNI_VERSION_1_4; } #endif ApplicationController* ApplicationController::s_instance = nullptr; ApplicationController::ApplicationController(QObject* parent) : QObject(parent) { s_instance = this; connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &ApplicationController::clipboardContentChanged); } ApplicationController::~ApplicationController() { s_instance = nullptr; } ApplicationController* ApplicationController::instance() { return s_instance; } void ApplicationController::setReservationManager(ReservationManager* resMgr) { m_resMgr = resMgr; } void ApplicationController::setPkPassManager(PkPassManager* pkPassMgr) { m_pkPassMgr = pkPassMgr; } void ApplicationController::setDocumentManager(DocumentManager* docMgr) { m_docMgr = docMgr; } void ApplicationController::showImportFileDialog() { #ifdef Q_OS_ANDROID using namespace KAndroidExtras; Intent intent; intent.setAction(Intent::ACTION_OPEN_DOCUMENT()); intent.addCategory(Intent::CATEGORY_OPENABLE()); intent.setType(QStringLiteral("*/*")); QtAndroid::startActivity(intent, 0, [this](int, int, const QAndroidJniObject &intent) { importFromUrl(KAndroidExtras::Intent(intent).getData()); }); #else const auto url = QFileDialog::getOpenFileUrl(nullptr, i18n("Import Reservation"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), i18n("All Files (*.*);;PkPass files (*.pkpass);;PDF files (*.pdf);;KDE Itinerary files (*.itinerary)")); if (url.isValid()) { importFromUrl(url); } #endif } void ApplicationController::importFromClipboard() { if (QGuiApplication::clipboard()->mimeData()->hasUrls()) { const auto urls = QGuiApplication::clipboard()->mimeData()->urls(); for (const auto &url : urls) importFromUrl(url); return; } if (QGuiApplication::clipboard()->mimeData()->hasText()) { const auto content = QGuiApplication::clipboard()->mimeData()->data(QLatin1String("text/plain")); importData(content); } } void ApplicationController::importFromUrl(const QUrl &url) { qCDebug(Log) << url; if (url.isLocalFile() || url.scheme() == QLatin1String("content")) { importLocalFile(url); return; } if (url.scheme().startsWith(QLatin1String("http"))) { if (!m_nam ) { m_nam = new QNetworkAccessManager(this); } auto reqUrl(url); reqUrl.setScheme(QLatin1String("https")); QNetworkRequest req(reqUrl); req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); auto reply = m_nam->get(req); connect(reply, &QNetworkReply::finished, this, [this, reply]() { if (reply->error() != QNetworkReply::NoError) { qCDebug(Log) << reply->url() << reply->errorString(); return; } importData(reply->readAll()); }); return; } qCDebug(Log) << "Unhandled URL type:" << url; } void ApplicationController::importLocalFile(const QUrl &url) { qCDebug(Log) << url; if (url.isEmpty()) { return; } QFile f(url.isLocalFile() ? url.toLocalFile() : url.toString()); if (!f.open(QFile::ReadOnly)) { qCWarning(Log) << "Failed to open" << f.fileName() << f.errorString(); return; } if (f.size() > 4000000) { qCWarning(Log) << "File too large, ignoring" << f.fileName() << f.size(); return; } const auto head = f.peek(4); if (url.fileName().endsWith(QLatin1String(".pkpass"), Qt::CaseInsensitive)) { m_pkPassMgr->importPass(url); } else if (url.fileName().endsWith(QLatin1String(".itinerary"), Qt::CaseInsensitive)) { importBundle(url); } else if (strncmp(head.constData(), "PK\x03\x04", 4) == 0) { if (m_pkPassMgr->importPass(url).isEmpty()) { importBundle(url); } } else { const auto data = f.readAll(); const auto resIds = m_resMgr->importReservation(data, f.fileName()); if (resIds.empty()) { return; } // check if there is a document we want to attach here QMimeDatabase db; const auto mt = db.mimeTypeForFileNameAndData(f.fileName(), data); if (mt.name() != QLatin1String("application/pdf")) { // TODO support more file types (however we certainly want to exclude pkpass and json here) return; } DigitalDocument docInfo; docInfo.setName(f.fileName()); docInfo.setEncodingFormat(mt.name()); const auto docId = DocumentUtil::idForContent(data); m_docMgr->addDocument(docId, docInfo, data); for (const auto &resId : resIds) { auto res = m_resMgr->reservation(resId); if (DocumentUtil::addDocumentId(res, docId)) { m_resMgr->updateReservation(resId, res); } } } } void ApplicationController::importData(const QByteArray &data) { qCDebug(Log); if (data.size() < 4) { return; } if (strncmp(data.constData(), "PK\x03\x04", 4) == 0) { if (m_pkPassMgr->importPassFromData(data).isEmpty()) { importBundle(data); } } else { m_resMgr->importReservation(data); } } void ApplicationController::checkCalendar() { #ifdef Q_OS_ANDROID if (QtAndroid::checkPermission(PERMISSION_CALENDAR) == QtAndroid::PermissionResult::Granted) { const auto activity = QtAndroid::androidActivity(); if (activity.isValid()) { activity.callMethod("checkCalendar"); } } else { QtAndroid::requestPermissions({PERMISSION_CALENDAR}, [this] (const QtAndroid::PermissionResultMap &result){ if (result[PERMISSION_CALENDAR] == QtAndroid::PermissionResult::Granted) { checkCalendar(); } }); } #endif } bool ApplicationController::hasClipboardContent() const { return QGuiApplication::clipboard()->mimeData()->hasText() || QGuiApplication::clipboard()->mimeData()->hasUrls(); } void ApplicationController::exportData() { qCDebug(Log); #ifdef Q_OS_ANDROID using namespace KAndroidExtras; Intent intent; intent.setAction(Intent::ACTION_CREATE_DOCUMENT()); intent.addCategory(Intent::CATEGORY_OPENABLE()); intent.setType(QStringLiteral("*/*")); QtAndroid::startActivity(intent, 0, [this](int, int, const QAndroidJniObject &jniIntent) { Intent intent(jniIntent); exportToFile(intent.getData().toString()); }); #else const auto filePath = QFileDialog::getSaveFileName(nullptr, i18n("Export Itinerary Data"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), i18n("KDE Itinerary files (*.itinerary)")); exportToFile(filePath); #endif } void ApplicationController::exportToFile(const QString &filePath) { qCDebug(Log) << filePath; if (filePath.isEmpty()) { return; } File f(filePath); if (!f.open(File::Write)) { qCWarning(Log) << f.errorString(); // TODO show error in UI return; } // export reservation data for (const auto &batchId : m_resMgr->batches()) { const auto resIds = m_resMgr->reservationsForBatch(batchId); for (const auto &resId : resIds) { f.addReservation(resId, m_resMgr->reservation(resId)); } } // export passes const auto passIds = m_pkPassMgr->passes(); for (const auto &passId : passIds) { f.addPass(passId, m_pkPassMgr->rawData(passId)); } // export documents const auto docIds = m_docMgr->documents(); for (const auto &docId : docIds) { const auto fileName = m_docMgr->documentFilePath(docId); QFile file(fileName); if (!file.open(QFile::ReadOnly)) { qCWarning(Log) << "failed to open" << fileName << "for exporting" << file.errorString(); continue; } f.addDocument(docId, m_docMgr->documentInfo(docId), file.readAll()); } // TODO export settings } void ApplicationController::importBundle(const QUrl &url) { KItinerary::File f(url.isLocalFile() ? url.toLocalFile() : url.toString()); if (!f.open(File::Read)) { // TODO show error in the ui qCWarning(Log) << "Failed to open bundle file:" << url << f.errorString(); return; } importBundle(&f); } void ApplicationController::importBundle(const QByteArray &data) { QBuffer buffer; buffer.setData(data); buffer.open(QBuffer::ReadOnly); KItinerary::File f(&buffer); if (!f.open(File::Read)) { // TODO show error in the ui qCWarning(Log) << "Failed to open bundle data:" << f.errorString(); return; } importBundle(&f); } void ApplicationController::importBundle(KItinerary::File *file) { const auto resIds = file->reservations(); for (const auto &resId : resIds) { m_resMgr->addReservation(file->reservation(resId)); } const auto passIds = file->passes(); for (const auto &passId : passIds) { m_pkPassMgr->importPassFromData(file->passData(passId)); } const auto docIds = file->documents(); for (const auto &docId : docIds) { m_docMgr->addDocument(docId, file->documentInfo(docId), file->documentData(docId)); } } void ApplicationController::addDocument(const QString &batchId) { // TODO Android support #ifndef Q_OS_ANDROID const auto url = QFileDialog::getOpenFileUrl(nullptr, i18n("Add Document"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), i18n("All Files (*.*)")); addDocument(batchId, url); #else using namespace KAndroidExtras; Intent intent; intent.setAction(Intent::ACTION_OPEN_DOCUMENT()); intent.addCategory(Intent::CATEGORY_OPENABLE()); intent.setType(QStringLiteral("*/*")); QtAndroid::startActivity(intent, 0, [this, batchId](int, int, const QAndroidJniObject &jniIntent) { Intent intent(jniIntent); addDocument(batchId, intent.getData()); }); #endif } void ApplicationController::addDocument(const QString &batchId, const QUrl &url) { if (!url.isValid()) { return; } const auto docId = QUuid::createUuid().toString(); auto res = m_resMgr->reservation(batchId); DocumentUtil::addDocumentId(res, docId); DigitalDocument docInfo; #ifdef Q_OS_ANDROID docInfo.setEncodingFormat(KAndroidExtras::ContentResolver::mimeType(url)); docInfo.setName(KAndroidExtras::ContentResolver::fileName(url)); #else QMimeDatabase db; docInfo.setEncodingFormat(db.mimeTypeForFile(url.isLocalFile() ? url.toLocalFile() : url.toString()).name()); docInfo.setName(url.fileName()); #endif m_docMgr->addDocument(docId, docInfo, url.isLocalFile() ? url.toLocalFile() : url.toString()); const auto resIds = m_resMgr->reservationsForBatch(batchId); for (const auto &resId : resIds) { m_resMgr->updateReservation(resId, res); } } void ApplicationController::removeDocument(const QString &batchId, const QString &docId) { const auto resIds = m_resMgr->reservationsForBatch(batchId); for (const auto &resId : resIds) { auto res = m_resMgr->reservation(batchId); if (DocumentUtil::removeDocumentId(res, docId)) { m_resMgr->updateReservation(resId, res); } } m_docMgr->removeDocument(docId); } void ApplicationController::openDocument(const QUrl &url) { +#ifdef Q_OS_ANDROID + using namespace KAndroidExtras; + auto activity = QtAndroid::androidActivity(); + auto uri = activity.callObjectMethod("openDocument", Jni::signature(), QAndroidJniObject::fromString(url.toLocalFile()).object()); + + Intent intent; + intent.setData(uri); + intent.setAction(Intent::ACTION_VIEW()); + Activity::startActivity(intent, 0); +#else QDesktopServices::openUrl(url); +#endif } diff --git a/src/app/build.gradle b/src/app/build.gradle index 5b9794a..ccfa545 100644 --- a/src/app/build.gradle +++ b/src/app/build.gradle @@ -1,64 +1,65 @@ buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.2.0' } } repositories { google() jcenter() } apply plugin: 'com.android.application' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation 'org.mnode.ical4j:ical4j:2.2.0' + implementation 'androidx.core:core:1.0.2' } android { /******************************************************* * The following variables: * - androidBuildToolsVersion, * - androidCompileSdkVersion * - qt5AndroidDir - holds the path to qt android files * needed to build any Qt application * on Android. * * are defined in gradle.properties file. This file is * updated by QtCreator and androiddeployqt tools. * Changing them manually might break the compilation! *******************************************************/ compileSdkVersion androidCompileSdkVersion.toInteger() buildToolsVersion androidBuildToolsVersion sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] res.srcDirs = [qt5AndroidDir + '/res', 'res'] resources.srcDirs = ['src'] renderscript.srcDirs = ['src'] assets.srcDirs = ['assets'] jniLibs.srcDirs = ['libs'] } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError false } } diff --git a/src/app/res/xml/file_paths.xml b/src/app/res/xml/file_paths.xml new file mode 100644 index 0000000..8c6158d --- /dev/null +++ b/src/app/res/xml/file_paths.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/app/src/org/kde/itinerary/Activity.java b/src/app/src/org/kde/itinerary/Activity.java index 9786ce4..6576e04 100644 --- a/src/app/src/org/kde/itinerary/Activity.java +++ b/src/app/src/org/kde/itinerary/Activity.java @@ -1,123 +1,132 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library 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 . */ package org.kde.itinerary; import org.qtproject.qt5.android.bindings.QtActivity; import net.fortuna.ical4j.model.property.XProperty; +import androidx.core.content.FileProvider; + import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.CalendarContract; import android.util.Log; import android.view.WindowManager; import java.io.*; import java.util.*; public class Activity extends QtActivity { private static final String TAG = "org.kde.itinerary"; public native void importReservation(String data); public native void importDavDroidJson(String data); public native void importFromIntent(Intent data); /** Check the calendar for with JSON-LD data. * This assumes the custom property serialization format used by DavDroid. */ public void checkCalendar() { Calendar startTime = Calendar.getInstance(); startTime.add(Calendar.DAY_OF_YEAR, -5); Calendar endTime = Calendar.getInstance(); endTime.add(Calendar.MONTH, 6); String[] eventColumns = new String[] { "uid2445", "title", "_id" }; String[] propColumns = new String[] { "name", "value" }; String eventSelection = "(( " + CalendarContract.Events.DTSTART + " >= " + startTime.getTimeInMillis() + " ) AND ( " + CalendarContract.Events.DTSTART + " <= " + endTime.getTimeInMillis() + " ))"; Cursor cursor = getContentResolver().query(CalendarContract.Events.CONTENT_URI, eventColumns, eventSelection, null, null); while (cursor.moveToNext()) { if (cursor.getString(0) == null || !cursor.getString(0).startsWith("KIT-")) { continue; } Log.i(TAG, cursor.getString(1)); String propSelection = "(event_id == " + cursor.getInt(2) + ")"; Cursor propCursor = getContentResolver().query(CalendarContract.ExtendedProperties.CONTENT_URI, propColumns, propSelection, null, null); while (propCursor.moveToNext()) { String propName = propCursor.getString(0); String propValue = propCursor.getString(1); if (propName == null || propValue == null) { continue; } if (propName.equals("unknown-property.v2")) { importDavDroidJson(propValue); // legacy, replaced by the above in Feb 2019, removing this eventually will allow us to remove the ical4j dependency } else if (propName.equals("unknown-property")) { ByteArrayInputStream bis = new ByteArrayInputStream(android.util.Base64.decode(propValue, android.util.Base64.NO_WRAP)); try { ObjectInputStream ois = new ObjectInputStream(bis); Object prop = ois.readObject(); if (prop instanceof XProperty) { importReservation(((XProperty)prop).getValue()); } } catch (Exception e) { Log.i(TAG, e.toString()); continue; } } } } } public void setBrightness(final float brightness) { runOnUiThread(() -> { WindowManager.LayoutParams layout = getWindow().getAttributes(); layout.screenBrightness = brightness; getWindow().setAttributes(layout); }); } public float getBrightness() { return getWindow().getAttributes().screenBrightness; } public void setLockInhibitionOn() { runOnUiThread(() -> getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); } public void setLockInhibitionOff() { runOnUiThread(() -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); importFromIntent(intent); } + + public Uri openDocument(String filePath) + { + Log.i(TAG, filePath); + File file = new File(filePath); + return FileProvider.getUriForFile(this, "org.kde.itinerary.documentProvider", file); + } } diff --git a/src/kandroidextras/intent.cpp b/src/kandroidextras/intent.cpp index dcb89c4..4f880f9 100644 --- a/src/kandroidextras/intent.cpp +++ b/src/kandroidextras/intent.cpp @@ -1,93 +1,98 @@ /* Copyright (C) 2019 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library 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 . */ #include "intent.h" #include "uri.h" #include "jnitypes.h" #include "jnisignature.h" #include #include using namespace KAndroidExtras; Intent::Intent() { m_intent = QAndroidJniObject(Jni::typeName()); } Intent::Intent(const QAndroidJniObject &intent) : m_intent(intent) { } Intent::~Intent() = default; void Intent::addCategory(const QAndroidJniObject &category) { m_intent.callObjectMethod("addCategory", Jni::signature(), category.object()); } QUrl Intent::getData() const { if (!m_intent.isValid()) { return {}; } const auto uri = m_intent.callObjectMethod("getData", Jni::signature()); return Uri::toUrl(uri); } void Intent::setAction(const QAndroidJniObject &action) { m_intent.callObjectMethod("setAction", Jni::signature(), action.object()); } void Intent::setData(const QUrl& url) { const auto uri = Uri::fromUrl(url); + setData(uri); +} + +void Intent::setData(const QAndroidJniObject& uri) +{ m_intent.callObjectMethod("setData", Jni::signature(), uri.object()); } void Intent::setType(const QString &type) { m_intent.callObjectMethod("setType", Jni::signature(), QAndroidJniObject::fromString(type).object()); } Intent::operator QAndroidJniObject() const { return m_intent; } QAndroidJniObject Intent::ACTION_CREATE_DOCUMENT() { return QAndroidJniObject::getStaticObjectField(Jni::typeName(), "ACTION_CREATE_DOCUMENT"); } QAndroidJniObject Intent::ACTION_OPEN_DOCUMENT() { return QAndroidJniObject::getStaticObjectField(Jni::typeName(), "ACTION_OPEN_DOCUMENT"); } QAndroidJniObject Intent::ACTION_VIEW() { return QAndroidJniObject::getStaticObjectField(Jni::typeName(), "ACTION_VIEW"); } QAndroidJniObject Intent::CATEGORY_OPENABLE() { return QAndroidJniObject::getStaticObjectField(Jni::typeName(), "CATEGORY_OPENABLE"); } diff --git a/src/kandroidextras/intent.h b/src/kandroidextras/intent.h index 603a979..5d7d4e1 100644 --- a/src/kandroidextras/intent.h +++ b/src/kandroidextras/intent.h @@ -1,67 +1,68 @@ /* Copyright (C) 2019 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library 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 . */ #ifndef KANDROIDEXTRAS_INTENT_H #define KANDROIDEXTRAS_INTENT_H #include class QUrl; namespace KAndroidExtras { /** Methods to interact with android.content.Intent objects, beyond what QAndroidIntent offers. */ class Intent { public: /** Creates a new empty intent. */ Intent(); /** Adopts an existing intent available as a JNI object. */ explicit Intent(const QAndroidJniObject &intent); ~Intent(); /** Add a category to the intent. */ void addCategory(const QAndroidJniObject &category); /** Returns the data of this intent. */ QUrl getData() const; /** Sets the action of the intent. */ void setAction(const QAndroidJniObject &action); /** Set the data URL of this intent. */ void setData(const QUrl &url); + void setData(const QAndroidJniObject &uri); /** Set the mime type for this intent. */ void setType(const QString &type); /** Implicit conversion to an QAndroidJniObject. */ operator QAndroidJniObject() const; /** Action contstant for create document intents. */ static QAndroidJniObject ACTION_CREATE_DOCUMENT(); /** Action contstant for open document intents. */ static QAndroidJniObject ACTION_OPEN_DOCUMENT(); /** Action contstant for viewing intents. */ static QAndroidJniObject ACTION_VIEW(); /** Category constant for openable content. */ static QAndroidJniObject CATEGORY_OPENABLE(); private: QAndroidJniObject m_intent; }; } #endif // KANDROIDEXTRAS_INTENT_H