diff --git a/src/app/applicationcontroller.cpp b/src/app/applicationcontroller.cpp index 9207547..bfe9783 100644 --- a/src/app/applicationcontroller.cpp +++ b/src/app/applicationcontroller.cpp @@ -1,360 +1,381 @@ /* 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 "logging.h" #include "pkpassmanager.h" #include "reservationmanager.h" +#include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_ANDROID #include #include #else #include #include #endif using namespace KItinerary; #ifdef Q_OS_ANDROID static void importReservation(JNIEnv *env, jobject that, jstring data) { Q_UNUSED(that); ApplicationController::instance()->importData(env->GetStringUTFChars(data, 0)); } static const JNINativeMethod methods[] = { {"importReservation", "(Ljava/lang/String;)V", (void*)importReservation} }; 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) #ifdef Q_OS_ANDROID , m_activityResultReceiver(this) #endif { 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::showOnMap(const QVariant &place) { if (place.isNull()) { return; } const auto geo = JsonLdDocument::readProperty(place, "geo").value(); const auto addr = JsonLdDocument::readProperty(place, "address").value(); #ifdef Q_OS_ANDROID QString intentUri; if (geo.isValid()) { intentUri = QLatin1String("geo:") + QString::number(geo.latitude()) + QLatin1Char(',') + QString::number(geo.longitude()); } else if (!addr.isEmpty()) { intentUri = QLatin1String("geo:0,0?q=") + addr.streetAddress() + QLatin1String(", ") + addr.postalCode() + QLatin1Char(' ') + addr.addressLocality() + QLatin1String(", ") + addr.addressCountry(); } else { return; } const auto activity = QtAndroid::androidActivity(); if (activity.isValid()) { activity.callMethod("launchViewIntentFromUri", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(intentUri).object()); } #else if (geo.isValid()) { // zoom out further from airports, they are larger and you usually want to go further away from them const auto zoom = place.userType() == qMetaTypeId() ? 12 : 17; QUrl url; url.setScheme(QStringLiteral("https")); url.setHost(QStringLiteral("www.openstreetmap.org")); url.setPath(QStringLiteral("/")); const QString fragment = QLatin1String("map=") + QString::number(zoom) + QLatin1Char('/') + QString::number(geo.latitude()) + QLatin1Char('/') + QString::number(geo.longitude()); url.setFragment(fragment); QDesktopServices::openUrl(url); return; } if (!addr.isEmpty()) { QUrl url; url.setScheme(QStringLiteral("https")); url.setHost(QStringLiteral("www.openstreetmap.org")); url.setPath(QStringLiteral("/search")); const QString queryString = addr.streetAddress() + QLatin1String(", ") + addr.postalCode() + QLatin1Char(' ') + addr.addressLocality() + QLatin1String(", ") + addr.addressCountry(); QUrlQuery query; query.addQueryItem(QStringLiteral("query"), queryString); url.setQuery(query); QDesktopServices::openUrl(url); } #endif } bool ApplicationController::canNavigateTo(const QVariant& place) { if (place.isNull()) { return false; } if (JsonLdDocument::readProperty(place, "geo").value().isValid()) { return true; } #ifdef Q_OS_ANDROID return !JsonLdDocument::readProperty(place, "address").value().isEmpty(); #else return false; #endif } void ApplicationController::navigateTo(const QVariant& place) { if (place.isNull()) { return; } #ifdef Q_OS_ANDROID const auto geo = JsonLdDocument::readProperty(place, "geo").value(); const auto addr = JsonLdDocument::readProperty(place, "address").value(); QString intentUri; if (geo.isValid()) { intentUri = QLatin1String("google.navigation:q=") + QString::number(geo.latitude()) + QLatin1Char(',') + QString::number(geo.longitude()); } else if (!addr.isEmpty()) { intentUri = QLatin1String("google.navigation:q=") + addr.streetAddress() + QLatin1String(", ") + addr.postalCode() + QLatin1Char(' ') + addr.addressLocality() + QLatin1String(", ") + addr.addressCountry(); } else { return; } const auto activity = QtAndroid::androidActivity(); if (activity.isValid()) { activity.callMethod("launchViewIntentFromUri", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(intentUri).object()); } #else if (m_pendingNavigation) { return; } if (!m_positionSource) { m_positionSource = QGeoPositionInfoSource::createDefaultSource(this); if (!m_positionSource) { qWarning() << "no geo position info source available"; return; } } if (m_positionSource->lastKnownPosition().isValid()) { navigateTo(m_positionSource->lastKnownPosition(), place); } else { m_pendingNavigation = connect(m_positionSource, &QGeoPositionInfoSource::positionUpdated, this, [this, place](const QGeoPositionInfo &pos) { navigateTo(pos, place); }); m_positionSource->requestUpdate(); } #endif } #ifndef Q_OS_ANDROID void ApplicationController::navigateTo(const QGeoPositionInfo &from, const QVariant &to) { qDebug() << from.coordinate() << from.isValid(); disconnect(m_pendingNavigation); if (!from.isValid()) { return; } const auto geo = JsonLdDocument::readProperty(to, "geo").value(); if (geo.isValid()) { QUrl url; url.setScheme(QStringLiteral("https")); url.setHost(QStringLiteral("www.openstreetmap.org")); url.setPath(QStringLiteral("/directions")); QUrlQuery query; query.addQueryItem(QLatin1String("route"), QString::number(from.coordinate().latitude()) + QLatin1Char(',') + QString::number(from.coordinate().longitude()) + QLatin1Char(';') + QString::number(geo.latitude()) + QLatin1Char(',') + QString::number(geo.longitude())); url.setQuery(query); QDesktopServices::openUrl(url); return; } } #endif static bool isPkPassFile(const QUrl &url) { if (url.isLocalFile()) { QFile f(url.toLocalFile()); if (f.open(QFile::ReadOnly)) { char buffer[4]; if (f.read(buffer, sizeof(buffer)) != sizeof(buffer)) { return false; } return buffer[0] == 'P' && buffer[1] == 'K' && buffer[2] == 0x03 && buffer[3] == 0x04; } } return url.fileName().endsWith(QLatin1String(".pkpass")); } #ifdef Q_OS_ANDROID void ApplicationController::importFromIntent(const QAndroidJniObject &intent) { if (!intent.isValid()) { return; } const auto uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); if (!uri.isValid()) { return; } const auto scheme = uri.callObjectMethod("getScheme", "()Ljava/lang/String;"); qCDebug(Log) << uri.callObjectMethod("toString", "()Ljava/lang/String;").toString(); if (scheme.toString() == QLatin1String("content")) { const auto tmpFile = QtAndroid::androidActivity().callObjectMethod("receiveContent", "(Landroid/net/Uri;)Ljava/lang/String;", uri.object()); const auto tmpUrl = QUrl::fromLocalFile(tmpFile.toString()); if (isPkPassFile(tmpUrl)) { m_pkPassMgr->importPassFromTempFile(tmpUrl); } else { m_resMgr->importReservation(tmpUrl); } } else if (scheme.toString() == QLatin1String("file")) { const auto uriStr = uri.callObjectMethod("toString", "()Ljava/lang/String;"); importLocalFile(QUrl(uriStr.toString())); } else { const auto uriStr = uri.callObjectMethod("toString", "()Ljava/lang/String;"); qCWarning(Log) << "Unknown intent URI:" << uriStr.toString(); } } void ApplicationController::ActivityResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &intent) { qCDebug(Log) << receiverRequestCode << resultCode; m_controller->importFromIntent(intent); } #endif void ApplicationController::showImportFileDialog() { #ifdef Q_OS_ANDROID const auto ACTION_OPEN_DOCUMENT = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_OPEN_DOCUMENT"); QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_OPEN_DOCUMENT.object()); const auto CATEGORY_OPENABLE = QAndroidJniObject::getStaticObjectField("android/content/Intent", "CATEGORY_OPENABLE"); intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", CATEGORY_OPENABLE.object()); intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QAndroidJniObject::fromString(QStringLiteral("*/*")).object()); QtAndroid::startActivity(intent, 0, &m_activityResultReceiver); #endif } +void ApplicationController::importFromClipboard() +{ + if (QGuiApplication::clipboard()->mimeData()->hasUrls()) { + const auto urls = QGuiApplication::clipboard()->mimeData()->urls(); + for (const auto url : urls) + importLocalFile(url); + } + + if (QGuiApplication::clipboard()->mimeData()->hasText()) { + const auto content = QGuiApplication::clipboard()->text(); + const auto data = IataBcbpParser::parse(content, QDate::currentDate()); + if (!data.isEmpty()) { + for (const auto &res : data) + m_resMgr->addReservation(res); + } + + return; + } +} + void ApplicationController::importLocalFile(const QUrl &url) { if (isPkPassFile(url)) { m_pkPassMgr->importPass(url); } else { m_resMgr->importReservation(url); } } void ApplicationController::importData(const QByteArray &data) { m_resMgr->importReservation(data); } void ApplicationController::checkCalendar() { #ifdef Q_OS_ANDROID const auto activity = QtAndroid::androidActivity(); if (activity.isValid()) { activity.callMethod("checkCalendar"); } #endif } bool ApplicationController::hasClipboardContent() const { - return QGuiApplication::clipboard()->mimeData()->hasText(); + return QGuiApplication::clipboard()->mimeData()->hasText() || QGuiApplication::clipboard()->mimeData()->hasUrls(); } diff --git a/src/app/applicationcontroller.h b/src/app/applicationcontroller.h index bb1610d..fafbe34 100644 --- a/src/app/applicationcontroller.h +++ b/src/app/applicationcontroller.h @@ -1,90 +1,91 @@ /* 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 . */ #ifndef APPLICATIONCONTROLLER_H #define APPLICATIONCONTROLLER_H #include #ifdef Q_OS_ANDROID #include #endif class PkPassManager; class ReservationManager; class QGeoPositionInfo; class QGeoPositionInfoSource; class ApplicationController : public QObject { Q_OBJECT Q_PROPERTY(bool hasClipboardContent READ hasClipboardContent NOTIFY clipboardContentChanged) public: explicit ApplicationController(QObject *parent = nullptr); ~ApplicationController(); void setReservationManager(ReservationManager *resMgr); void setPkPassManager(PkPassManager *pkPassMgr); // navigation Q_INVOKABLE void showOnMap(const QVariant &place); Q_INVOKABLE bool canNavigateTo(const QVariant &place); Q_INVOKABLE void navigateTo(const QVariant &place); // data import Q_INVOKABLE void showImportFileDialog(); + Q_INVOKABLE void importFromClipboard(); #ifdef Q_OS_ANDROID void importFromIntent(const QAndroidJniObject &intent); #endif void importLocalFile(const QUrl &url); void importData(const QByteArray &data); Q_INVOKABLE void checkCalendar(); static ApplicationController* instance(); bool hasClipboardContent() const; signals: void clipboardContentChanged(); private: static ApplicationController *s_instance; ReservationManager *m_resMgr = nullptr; PkPassManager *m_pkPassMgr = nullptr; #ifndef Q_OS_ANDROID void navigateTo(const QGeoPositionInfo &from, const QVariant &to); QGeoPositionInfoSource *m_positionSource = nullptr; QMetaObject::Connection m_pendingNavigation; #else class ActivityResultReceiver : public QAndroidActivityResultReceiver { public: explicit inline ActivityResultReceiver(ApplicationController *controller) : m_controller(controller) {} void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &intent) override; private: ApplicationController *m_controller; }; ActivityResultReceiver m_activityResultReceiver; #endif }; #endif // APPLICATIONCONTROLLER_H diff --git a/src/app/main.qml b/src/app/main.qml index 469baf2..fc691f3 100644 --- a/src/app/main.qml +++ b/src/app/main.qml @@ -1,114 +1,114 @@ /* 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 . */ import QtQuick 2.5 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 as QQC2 import org.kde.kirigami 2.0 as Kirigami import "." as App Kirigami.ApplicationWindow { title: i18n("KDE Itinerary") header: Kirigami.ApplicationHeader {} reachableModeEnabled: false width: 480 height: 720 App.ImportDialog { id: importDialog } globalDrawer: Kirigami.GlobalDrawer { title: i18n("KDE Itinerary") titleIcon: "map-symbolic" actions: [ Kirigami.Action { text: i18n("Import Reservation...") iconName: "document-open" onTriggered: importDialog.importReservation() }, Kirigami.Action { text: i18n("Import Pass...") iconName: "document-open" onTriggered: importDialog.importPass() }, Kirigami.Action { text: i18n("Paste") iconName: "edit-paste" - onTriggered: _reservationManager.importFromClipboard() + onTriggered: _appController.importFromClipboard() enabled: _appController.hasClipboardContent }, Kirigami.Action { text: i18n("Check Calendar") iconName: "view-calendar-day" onTriggered: _appController.checkCalendar() visible: Qt.platform.os == "android" }, Kirigami.Action { text: i18n("Check for Updates") iconName: "view-refresh" onTriggered: { _pkpassManager.updatePasses(); } }, Kirigami.Action { id: settingsAction text: i18n("Settings...") iconName: "settings-configure" onTriggered: pageStack.push(settingsComponent) }, Kirigami.Action { id: aboutAction text: i18n("About") iconName: "help-about" onTriggered: pageStack.push(aboutComponent) } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent App.TimelinePage {} } Component { id: settingsComponent App.SettingsPage { id: settingsPage Binding { target: settingsAction property: "enabled" value: !settingsPage.isCurrentPage } } } Component { id: aboutComponent App.AboutPage { id: aboutPage Binding { target: aboutAction property: "enabled" value: !aboutPage.isCurrentPage } } } } diff --git a/src/app/reservationmanager.cpp b/src/app/reservationmanager.cpp index 682fa3d..3489c7a 100644 --- a/src/app/reservationmanager.cpp +++ b/src/app/reservationmanager.cpp @@ -1,265 +1,250 @@ /* 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 "reservationmanager.h" #include "pkpassmanager.h" #include "logging.h" #include #include #include -#include #include #include -#include #include #include #include #include -#include #include #include #include #include #include #include using namespace KItinerary; ReservationManager::ReservationManager(QObject* parent) : QObject(parent) { } ReservationManager::~ReservationManager() = default; void ReservationManager::setPkPassManager(PkPassManager* mgr) { m_passMgr = mgr; connect(mgr, &PkPassManager::passAdded, this, &ReservationManager::passAdded); connect(mgr, &PkPassManager::passUpdated, this, &ReservationManager::passUpdated); connect(mgr, &PkPassManager::passRemoved, this, &ReservationManager::passRemoved); } QVector ReservationManager::reservations() const { const QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations"); QDir::root().mkpath(basePath); QVector resIds; for (QDirIterator it(basePath, QDir::NoDotAndDotDot | QDir::Files); it.hasNext();) { it.next(); resIds.push_back(it.fileInfo().baseName()); } return resIds; } QVariant ReservationManager::reservation(const QString& id) const { if (id.isEmpty()) { return {}; } const auto it = m_reservations.constFind(id); if (it != m_reservations.constEnd()) { return it.value(); } const QString resPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/") + id + QLatin1String(".jsonld"); QFile f(resPath); if (!f.open(QFile::ReadOnly)) { qCWarning(Log) << "Failed to open JSON-LD reservation data file:" << resPath << f.errorString(); return {}; } const auto doc = QJsonDocument::fromJson(f.readAll()); if (!doc.isArray() && doc.array().size() != 1) { qCWarning(Log) << "Invalid JSON-LD reservation data file:" << resPath; return {}; } const auto resData = JsonLdDocument::fromJson(doc.array()); if (resData.size() != 1) { qCWarning(Log) << "Unable to parse JSON-LD reservation data file:" << resPath; return {}; } // re-run post-processing to benefit from newer augmentations ExtractorPostprocessor postproc; postproc.process(resData); if (postproc.result().size() != 1) { qCWarning(Log) << "Post-processing discarded the reservation:" << resPath; return {}; } const auto res = postproc.result().at(0); m_reservations.insert(id, res); return res; } void ReservationManager::importReservation(const QUrl& filename) { if (!filename.isLocalFile()) return; QFile f(filename.toLocalFile()); if (!f.open(QFile::ReadOnly)) { qCWarning(Log) << "Unable to open file:" << f.errorString(); return; } importReservation(f.readAll()); } void ReservationManager::importReservation(const QByteArray& data) { QJsonParseError error; const auto doc = QJsonDocument::fromJson(data, &error); if (!doc.isArray()) { qCWarning(Log) << "Invalid JSON format." << error.errorString() << error.offset; return; } const auto resData = JsonLdDocument::fromJson(doc.array()); importReservations(resData); } void ReservationManager::importReservations(const QVector &resData) { ExtractorPostprocessor postproc; postproc.setContextDate(QDateTime(QDate::currentDate(), QTime(0, 0))); postproc.process(resData); const QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/"); QDir::root().mkpath(basePath); for (auto res : postproc.result()) { QString resId; bool oldResFound = false; // check if we know this one already, and update if that's the case for (const auto &oldResId : reservations()) { const auto oldRes = reservation(oldResId); if (MergeUtil::isSame(oldRes, res)) { res = JsonLdDocument::apply(oldRes, res); resId = oldResId; oldResFound = true; break; } } if (resId.isEmpty()) { resId = QUuid::createUuid().toString(); } const QString path = basePath + resId + QLatin1String(".jsonld"); QFile f(path); if (!f.open(QFile::WriteOnly)) { qCWarning(Log) << "Unable to create file:" << f.errorString(); continue; } f.write(QJsonDocument(JsonLdDocument::toJson({res})).toJson()); m_reservations.insert(resId, res); if (oldResFound) { emit reservationUpdated(resId); } else { emit reservationAdded(resId); } } } -void ReservationManager::importFromClipboard() -{ - const auto content = QGuiApplication::clipboard()->text(); - // TODO support other types of barcodes and possibly json/pkpass files too? - - const auto data = IataBcbpParser::parse(content, QDate::currentDate()); - if (!data.isEmpty()) { - for (const auto &res : data) - addReservation(res); - } -} - void ReservationManager::addReservation(const QVariant &res) { QString resId = QUuid::createUuid().toString(); const QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/"); QDir::root().mkpath(basePath); const QString path = basePath + resId + QLatin1String(".jsonld"); QFile f(path); if (!f.open(QFile::WriteOnly)) { qCWarning(Log) << "Unable to create file:" << f.errorString(); return; } f.write(QJsonDocument(JsonLdDocument::toJson({res})).toJson()); m_reservations.insert(resId, res); emit reservationAdded(resId); } void ReservationManager::updateReservation(const QString &resId, const QVariant &res) { const QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/"); QDir::root().mkpath(basePath); const QString path = basePath + resId + QLatin1String(".jsonld"); QFile f(path); if (!f.open(QFile::WriteOnly)) { qCWarning(Log) << "Unable to open file:" << f.errorString(); return; } f.write(QJsonDocument(JsonLdDocument::toJson({res})).toJson()); m_reservations.insert(resId, res); emit reservationUpdated(resId); } void ReservationManager::removeReservation(const QString& id) { const QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/"); QFile::remove(basePath + QLatin1Char('/') + id + QLatin1String(".jsonld")); emit reservationRemoved(id); m_reservations.remove(id); } void ReservationManager::removeReservations(const QStringList& ids) { for (const auto &id : ids) removeReservation(id); } void ReservationManager::passAdded(const QString& passId) { const auto pass = m_passMgr->pass(passId); ExtractorEngine engine; engine.setPass(pass); const auto data = engine.extract(); const auto res = JsonLdDocument::fromJson(data); importReservations(res); } void ReservationManager::passUpdated(const QString& passId) { passAdded(passId); } void ReservationManager::passRemoved(const QString& passId) { Q_UNUSED(passId); // TODO } diff --git a/src/app/reservationmanager.h b/src/app/reservationmanager.h index 3afab9e..c232fe6 100644 --- a/src/app/reservationmanager.h +++ b/src/app/reservationmanager.h @@ -1,67 +1,66 @@ /* 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 . */ #ifndef RESERVATIONMANAGER_H #define RESERVATIONMANAGER_H #include #include #include class PkPassManager; class QUrl; /** Manages JSON-LD reservation data. */ class ReservationManager : public QObject { Q_OBJECT public: ReservationManager(QObject *parent = nullptr); ~ReservationManager(); void setPkPassManager(PkPassManager *mgr); QVector reservations() const; Q_INVOKABLE QVariant reservation(const QString &id) const; Q_INVOKABLE void importReservation(const QUrl &filename); - Q_INVOKABLE void importFromClipboard(); Q_INVOKABLE void addReservation(const QVariant &res); Q_INVOKABLE void updateReservation(const QString &resId, const QVariant &res); Q_INVOKABLE void removeReservation(const QString &id); Q_INVOKABLE void removeReservations(const QStringList &ids); void importReservation(const QByteArray &data); signals: void reservationAdded(const QString &id); void reservationUpdated(const QString &id); void reservationRemoved(const QString &id); private: void importReservations(const QVector &resData); void passAdded(const QString &passId); void passUpdated(const QString &passId); void passRemoved(const QString &passId); mutable QHash m_reservations; PkPassManager *m_passMgr = nullptr; }; #endif // RESERVATIONMANAGER_H